Skip to main content

json_ld/core/document/
mod.rs

1use hashbrown::HashSet;
2use json_syntax::{JsonCodeMap, JsonPrint, JsonValue, print::JsonPrintOptions};
3use mime::Mime;
4use rdf_syntax::{Iri, IriBuf};
5
6pub mod expanded;
7pub mod flattened;
8pub mod profile;
9
10pub use expanded::JsonLdExpandedDocument;
11pub use flattened::JsonLdFlattenedDocument;
12pub use profile::JsonLdProfile;
13
14use crate::syntax::JsonLdContextDocumentValue;
15
16/// Remote document.
17///
18/// Stores the content of a loaded remote document along with its original URL.
19#[derive(Debug, Clone)]
20pub struct JsonLdDocument<T = JsonValue> {
21	/// The final URL of the loaded document, after eventual redirection.
22	pub url: Option<IriBuf>,
23
24	/// The HTTP `Content-Type` header value of the loaded document, exclusive
25	/// of any optional parameters.
26	pub content_type: Option<Mime>,
27
28	/// If available, the value of the HTTP `Link Header` [RFC 8288] using the
29	/// `http://www.w3.org/ns/json-ld#context` link relation in the response.
30	///
31	/// If the response's `Content-Type` is `application/ld+json`, the HTTP
32	/// `Link Header` is ignored. If multiple HTTP `Link Headers` using the
33	/// `http://www.w3.org/ns/json-ld#context` link relation are found, the
34	/// loader fails with a `multiple context link headers` error.
35	///
36	/// [RFC 8288]: https://www.rfc-editor.org/rfc/rfc8288
37	pub context_url: Option<IriBuf>,
38
39	pub profile: HashSet<JsonLdProfile>,
40
41	pub source: Option<JsonLdSourceCode>,
42
43	/// The retrieved document.
44	pub document: T,
45}
46
47pub type JsonLdContextDocument = JsonLdDocument<JsonLdContextDocumentValue>;
48
49impl<T> JsonLdDocument<T> {
50	/// Creates a new remote document.
51	///
52	/// `url` is the final URL of the loaded document, after eventual
53	/// redirection.
54	/// `content_type` is the HTTP `Content-Type` header value of the loaded
55	/// document, exclusive of any optional parameters.
56	pub fn new(url: Option<IriBuf>, content_type: Option<Mime>, document: T) -> Self {
57		Self::new_full(url, content_type, None, HashSet::new(), None, document)
58	}
59
60	/// Creates a new remote document.
61	///
62	/// `url` is the final URL of the loaded document, after eventual
63	/// redirection.
64	/// `content_type` is the HTTP `Content-Type` header value of the loaded
65	/// document, exclusive of any optional parameters.
66	/// `context_url` is the value of the HTTP `Link Header` [RFC 8288] using the
67	/// `http://www.w3.org/ns/json-ld#context` link relation in the response,
68	/// if any.
69	/// `profile` is the value of any profile parameter retrieved as part of the
70	/// original contentType.
71	///
72	/// [RFC 8288]: https://www.rfc-editor.org/rfc/rfc8288
73	pub fn new_full(
74		url: Option<IriBuf>,
75		content_type: Option<Mime>,
76		context_url: Option<IriBuf>,
77		profile: HashSet<JsonLdProfile>,
78		source: Option<JsonLdSourceCode>,
79		document: T,
80	) -> Self {
81		Self {
82			url,
83			content_type,
84			context_url,
85			profile,
86			source,
87			document,
88		}
89	}
90
91	/// Maps the content of the remote document.
92	pub fn map<U>(self, f: impl Fn(T) -> U) -> JsonLdDocument<U> {
93		JsonLdDocument {
94			url: self.url,
95			content_type: self.content_type,
96			context_url: self.context_url,
97			profile: self.profile,
98			source: self.source,
99			document: f(self.document),
100		}
101	}
102
103	/// Tries to map the content of the remote document.
104	pub fn try_map<U, E>(self, f: impl Fn(T) -> Result<U, E>) -> Result<JsonLdDocument<U>, E> {
105		Ok(JsonLdDocument {
106			url: self.url,
107			content_type: self.content_type,
108			context_url: self.context_url,
109			profile: self.profile,
110			source: self.source,
111			document: f(self.document)?,
112		})
113	}
114
115	/// Returns a reference to the final URL of the loaded document, after eventual redirection.
116	pub fn url(&self) -> Option<&Iri> {
117		self.url.as_deref()
118	}
119
120	/// Returns the HTTP `Content-Type` header value of the loaded document,
121	/// exclusive of any optional parameters.
122	pub fn content_type(&self) -> Option<&Mime> {
123		self.content_type.as_ref()
124	}
125
126	/// Returns the value of the HTTP `Link Header` [RFC 8288] using the
127	/// `http://www.w3.org/ns/json-ld#context` link relation in the response,
128	/// if any.
129	///
130	/// If the response's `Content-Type` is `application/ld+json`, the HTTP
131	/// `Link Header` is ignored. If multiple HTTP `Link Headers` using the
132	/// `http://www.w3.org/ns/json-ld#context` link relation are found, the
133	/// loader fails with a `multiple context link headers` error.
134	///
135	/// [RFC 8288]: https://www.rfc-editor.org/rfc/rfc8288
136	pub fn context_url(&self) -> Option<&Iri> {
137		self.context_url.as_deref()
138	}
139
140	/// Returns a reference to the content of the document.
141	pub fn document(&self) -> &T {
142		&self.document
143	}
144
145	/// Returns a mutable reference to the content of the document.
146	pub fn document_mut(&mut self) -> &mut T {
147		&mut self.document
148	}
149
150	/// Drops the original URL and returns the content of the document.
151	pub fn into_document(self) -> T {
152		self.document
153	}
154
155	/// Drops the content and returns the original URL of the document.
156	pub fn into_url(self) -> Option<IriBuf> {
157		self.url
158	}
159
160	/// Sets the URL of the document.
161	pub fn set_url(&mut self, url: Option<IriBuf>) {
162		self.url = url
163	}
164}
165
166impl JsonLdDocument {
167	#[cfg(feature = "serde")]
168	pub fn try_into_context_document(
169		self,
170	) -> Result<JsonLdContextDocument, crate::syntax::serde::DeserializeError> {
171		Ok(JsonLdContextDocument {
172			url: self.url,
173			content_type: self.content_type,
174			context_url: self.context_url,
175			profile: self.profile,
176			source: self.source,
177			document: crate::syntax::from_value(self.document)?,
178		})
179	}
180}
181
182#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
183pub struct JsonLdSourceCode {
184	pub text: String,
185	pub code_map: JsonCodeMap,
186}
187
188impl JsonLdSourceCode {
189	pub fn new(text: String, code_map: JsonCodeMap) -> Self {
190		Self { text, code_map }
191	}
192
193	pub fn from_value(value: &JsonValue) -> Self {
194		JsonLdSourceCode {
195			text: value.pretty_print().to_string(),
196			code_map: JsonCodeMap::from_value(value, &JsonPrintOptions::PRETTY),
197		}
198	}
199}