jsonld_expansion/
literal.rs1use crate::{ActiveProperty, WarningHandler, expand_iri, node_id_of_term};
2use jsonld_context_processing::algorithm::{Action, RejectVocab};
3use jsonld_core::{Context, Environment, IndexedObject, LangString, Node, Object, Type, Value, object::value::Literal};
4use jsonld_syntax::{ErrorCode, LenientLangTag, Nullable};
5use jstrict::Number;
6use rdfx::vocabulary::VocabularyMut;
7
8pub(crate) enum GivenLiteralValue<'a> {
10 Boolean(bool),
11 Number(&'a Number),
12 String(&'a str),
13}
14
15#[derive(Debug, thiserror::Error)]
18#[error("not a literal value")]
19pub struct NotALiteral;
20
21impl<'a> GivenLiteralValue<'a> {
22 pub fn new(value: &'a jstrict::Value) -> Result<Self, NotALiteral> {
23 match value {
24 jstrict::Value::Boolean(b) => Ok(Self::Boolean(*b)),
25 jstrict::Value::Number(n) => Ok(Self::Number(n)),
26 jstrict::Value::String(s) => Ok(Self::String(s)),
27 _ => Err(NotALiteral),
28 }
29 }
30
31 pub fn as_str(&self) -> Option<&'a str> {
32 match self {
33 Self::String(s) => Some(s),
34 _ => None,
35 }
36 }
37}
38
39pub(crate) enum LiteralValue<'a> {
41 Given(GivenLiteralValue<'a>),
43 Inferred(jsonld_syntax::String),
46}
47
48impl LiteralValue<'_> {
49 pub fn as_str(&self) -> Option<&str> {
50 match self {
51 Self::Given(v) => v.as_str(),
52 Self::Inferred(s) => Some(s.as_str()),
53 }
54 }
55}
56
57pub(crate) type ExpandedLiteral<T, B> = IndexedObject<T, B>;
58
59#[derive(Debug, thiserror::Error)]
61pub enum LiteralExpansionError {
62 #[error("Invalid `@type` value")]
66 InvalidTypeValue,
67
68 #[error("Forbidden use of `@vocab`")]
71 ForbiddenVocab,
72
73 #[error("IRI expansion produced no result")]
76 IdExpansionEmpty,
77
78 #[error(transparent)]
80 NotALiteral(#[from] NotALiteral),
81}
82
83impl LiteralExpansionError {
84 #[must_use]
86 pub fn code(&self) -> ErrorCode {
87 match self {
88 Self::InvalidTypeValue => ErrorCode::InvalidTypeValue,
89 Self::ForbiddenVocab => ErrorCode::InvalidVocabMapping,
90 Self::IdExpansionEmpty => ErrorCode::InvalidIdValue,
91 Self::NotALiteral(_) => ErrorCode::InvalidValueObject,
92 }
93 }
94}
95
96impl From<RejectVocab> for LiteralExpansionError {
97 fn from(_value: RejectVocab) -> Self {
98 Self::ForbiddenVocab
99 }
100}
101
102pub(crate) type LiteralExpansionResult<T, B> = Result<ExpandedLiteral<T, B>, LiteralExpansionError>;
103
104pub(crate) fn expand_literal<N, L, W>(
112 mut env: Environment<N, L, W>,
113 vocab_policy: Action,
114 active_context: &Context<N::Iri, N::BlankId>,
115 active_property: ActiveProperty<'_>,
116 value: LiteralValue,
117) -> LiteralExpansionResult<N::Iri, N::BlankId>
118where
119 N: VocabularyMut,
120 N::Iri: Clone,
121 N::BlankId: Clone,
122 W: WarningHandler<N>,
123{
124 let active_property_definition = active_property.get_from(active_context);
125 let active_property_type = if let Some(active_property_definition) = active_property_definition {
126 active_property_definition.typ().cloned()
127 } else {
128 None
129 };
130
131 match active_property_type {
132 Some(Type::Id) if let Some(s) = value.as_str() => {
137 let mut node = Node::new();
138 let Some(id_term) = expand_iri(&mut env, active_context, Nullable::Some(s.into()), true, None)? else {
139 return Err(LiteralExpansionError::IdExpansionEmpty);
140 };
141 node.id = node_id_of_term(id_term);
142 Ok(Object::node(node).into())
143 }
144
145 Some(Type::Vocab) if let Some(s) = value.as_str() => {
150 let mut node = Node::new();
151
152 let ty = expand_iri(&mut env, active_context, Nullable::Some(s.into()), true, Some(vocab_policy))?;
153
154 if let Some(ty) = ty {
155 let id = node_id_of_term(ty);
156 node.id = id;
157 }
158
159 Ok(Object::node(node).into())
160 }
161
162 _ => {
163 let result: Literal = match value {
166 LiteralValue::Given(v) => match v {
167 GivenLiteralValue::Boolean(b) => Literal::Boolean(b),
168 GivenLiteralValue::Number(n) => Literal::Number(jstrict::NumberBuf::from_number(n)),
169 GivenLiteralValue::String(s) => Literal::String(s.into()),
170 },
171 LiteralValue::Inferred(s) => Literal::String(s),
172 };
173
174 let mut ty = None;
178 match active_property_type {
179 None | Some(Type::Id | Type::Vocab | Type::None) => {
180 if let Literal::String(s) = result {
182 let language = if let Some(active_property_definition) = active_property_definition {
186 if let Some(language) = active_property_definition.language() {
187 language.cloned().option()
188 } else {
189 active_context.default_language().map(LenientLangTag::to_owned)
190 }
191 } else {
192 active_context.default_language().map(LenientLangTag::to_owned)
193 };
194
195 let direction = if let Some(active_property_definition) = active_property_definition {
199 if let Some(direction) = active_property_definition.direction() {
200 direction.option()
201 } else {
202 active_context.default_base_direction()
203 }
204 } else {
205 active_context.default_base_direction()
206 };
207
208 return match LangString::new(s, language, direction) {
213 Ok(lang_str) => Ok(Object::Value(Value::LangString(lang_str)).into()),
214 Err(jsonld_core::InvalidLangString(s)) => Ok(Object::Value(Value::Literal(Literal::String(s), None)).into()),
215 };
216 }
217 }
218
219 Some(t) => {
220 if let Ok(t) = t.into_iri() {
221 ty = Some(t);
222 } else {
223 return Err(LiteralExpansionError::InvalidTypeValue);
224 }
225 }
226 }
227
228 Ok(Object::Value(Value::Literal(result, ty)).into())
229 }
230 }
231}