1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use std::convert::TryFrom;
use iref::{Iri, IriRef};
use crate::{
BlankId,
Id,
Lenient,
Context,
syntax::{
Keyword,
is_keyword_like,
Term
}
};
// Default value for `document_relative` is `false` and for `vocab` is `true`.
pub fn expand_iri<T: Id, C: Context<T>>(active_context: &C, value: &str, document_relative: bool, vocab: bool) -> Lenient<Term<T>> {
if let Ok(keyword) = Keyword::try_from(value) {
Term::Keyword(keyword).into()
} else {
// If value has the form of a keyword, a processor SHOULD generate a warning and return
// null.
if is_keyword_like(value) {
return Term::Null.into()
}
if let Some(term_definition) = active_context.get(value) {
// If active context has a term definition for value, and the associated IRI mapping
// is a keyword, return that keyword.
if let Some(value) = &term_definition.value {
if value.is_keyword() {
return Term::from(value.clone()).into()
}
}
// If vocab is true and the active context has a term definition for value, return the
// associated IRI mapping.
if vocab {
if let Some(mapped_value) = &term_definition.value {
return mapped_value.clone().into()
} else {
return Lenient::Unknown(value.to_string()).into()
}
}
}
// If value contains a colon (:) anywhere after the first character, it is either an IRI,
// a compact IRI, or a blank node identifier:
if let Some(index) = value.find(':') {
if index > 0 {
// Split value into a prefix and suffix at the first occurrence of a colon (:).
let (prefix, suffix) = value.split_at(index);
let suffix = &suffix[1..suffix.len()];
// If prefix is underscore (_) or suffix begins with double-forward-slash (//),
// return value as it is already an IRI or a blank node identifier.
if prefix == "_" {
return Term::from(BlankId::new(suffix)).into()
}
if suffix.starts_with("//") {
if let Ok(iri) = Iri::new(value) {
return Term::from(T::from_iri(iri)).into()
} else {
return Lenient::Unknown(value.to_string())
}
}
// If active context contains a term definition for prefix having a non-null IRI
// mapping and the prefix flag of the term definition is true, return the result
// of concatenating the IRI mapping associated with prefix and suffix.
if let Some(term_definition) = active_context.get(prefix) {
if term_definition.prefix {
if let Some(mapping) = &term_definition.value {
let mut result = mapping.as_str().to_string();
result.push_str(suffix);
if let Ok(result) = Iri::new(&result) {
return Term::from(T::from_iri(result)).into()
} else {
if let Ok(blank) = BlankId::try_from(result.as_ref()) {
return Term::from(blank).into()
} else {
return Lenient::Unknown(result)
}
}
}
}
}
// If value has the form of an IRI, return value.
if let Ok(result) = Iri::new(value) {
return Term::from(T::from_iri(result)).into()
}
}
}
// If vocab is true, and active context has a vocabulary mapping, return the result of
// concatenating the vocabulary mapping with value.
if vocab {
if let Some(vocabulary) = active_context.vocabulary() {
if let Term::Ref(mapping) = vocabulary {
let mut result = mapping.as_str().to_string();
result.push_str(value);
if let Ok(result) = Iri::new(&result) {
return Term::from(T::from_iri(result)).into()
} else {
if let Ok(blank) = BlankId::try_from(result.as_ref()) {
return Term::from(blank).into()
} else {
return Lenient::Unknown(result)
}
}
} else {
return Lenient::Unknown(value.to_string())
}
}
}
// Otherwise, if document relative is true set value to the result of resolving value
// against the base IRI from active context. Only the basic algorithm in section 5.2 of
// [RFC3986] is used; neither Syntax-Based Normalization nor Scheme-Based Normalization
// are performed. Characters additionally allowed in IRI references are treated in the
// same way that unreserved characters are treated in URI references, per section 6.5 of
// [RFC3987].
if document_relative {
if let Ok(iri_ref) = IriRef::new(value) {
if let Some(base_iri) = active_context.base_iri() {
let value = iri_ref.resolved(base_iri);
return Term::from(T::from_iri(value.as_iri())).into()
} else {
return Lenient::Unknown(value.to_string())
}
} else {
return Lenient::Unknown(value.to_string())
}
}
// Return value as is.
Lenient::Unknown(value.to_string())
}
}