1use core::fmt;
2use std::sync::Arc;
3
4use fluent_uri::Uri;
5use serde_json::Value;
6
7use crate::{list::List, resource::JsonSchemaResource, Draft, Error, Registry, ResourceRef};
8
9#[derive(Clone)]
13pub struct Resolver<'r> {
14 pub(crate) registry: &'r Registry,
15 base_uri: Arc<Uri<String>>,
16 scopes: List<Uri<String>>,
17}
18
19impl PartialEq for Resolver<'_> {
20 fn eq(&self, other: &Self) -> bool {
21 self.base_uri == other.base_uri
22 }
23}
24impl Eq for Resolver<'_> {}
25
26impl fmt::Debug for Resolver<'_> {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 f.debug_struct("Resolver")
29 .field("base_uri", &self.base_uri.as_str())
30 .field("scopes", &{
31 let mut buf = String::from("[");
32 let mut values = self.scopes.iter();
33 if let Some(value) = values.next() {
34 buf.push_str(value.as_str());
35 }
36 for value in values {
37 buf.push_str(", ");
38 buf.push_str(value.as_str());
39 }
40 buf.push(']');
41 buf
42 })
43 .finish()
44 }
45}
46
47impl<'r> Resolver<'r> {
48 pub(crate) fn new(registry: &'r Registry, base_uri: Arc<Uri<String>>) -> Self {
50 Self {
51 registry,
52 base_uri,
53 scopes: List::new(),
54 }
55 }
56 #[must_use]
57 pub fn base_uri(&self) -> Arc<Uri<String>> {
58 self.base_uri.clone()
59 }
60 pub fn lookup(&self, reference: &str) -> Result<Resolved<'r>, Error> {
66 let (uri, fragment) = if let Some(reference) = reference.strip_prefix('#') {
67 (self.base_uri.clone(), reference)
68 } else {
69 let (uri, fragment) = if let Some((uri, fragment)) = reference.rsplit_once('#') {
70 (uri, fragment)
71 } else {
72 (reference, "")
73 };
74 let uri = self
75 .registry
76 .resolve_against(&self.base_uri.borrow(), uri)?;
77 (uri, fragment)
78 };
79
80 let Some(retrieved) = self.registry.resources.get(&*uri) else {
81 return Err(Error::unretrievable(
82 uri.as_str(),
83 "Retrieving external resources is not supported once the registry is populated"
84 .into(),
85 ));
86 };
87
88 if fragment.starts_with('/') {
89 let resolver = self.evolve(uri);
90 return retrieved.pointer(fragment, resolver);
91 }
92
93 if !fragment.is_empty() {
94 let retrieved = self.registry.anchor(&uri, fragment)?;
95 let resolver = self.evolve(uri);
96 return retrieved.resolve(resolver);
97 }
98
99 let resolver = self.evolve(uri);
100 Ok(Resolved::new(
101 retrieved.contents(),
102 resolver,
103 retrieved.draft(),
104 ))
105 }
106 pub fn lookup_recursive_ref(&self) -> Result<Resolved<'r>, Error> {
118 let mut resolved = self.lookup("#")?;
119
120 if let Value::Object(obj) = resolved.contents {
121 if obj
122 .get("$recursiveAnchor")
123 .and_then(Value::as_bool)
124 .unwrap_or(false)
125 {
126 for uri in &self.dynamic_scope() {
127 let next_resolved = self.lookup(uri.as_str())?;
128
129 match next_resolved.contents {
130 Value::Object(next_obj) => {
131 if !next_obj
132 .get("$recursiveAnchor")
133 .and_then(Value::as_bool)
134 .unwrap_or(false)
135 {
136 break;
137 }
138 }
139 _ => break,
140 }
141
142 resolved = next_resolved;
143 }
144 }
145 }
146
147 Ok(resolved)
148 }
149 pub fn in_subresource(&self, subresource: ResourceRef<'_>) -> Result<Self, Error> {
155 self.in_subresource_inner(&subresource)
156 }
157
158 pub(crate) fn in_subresource_inner(
159 &self,
160 subresource: &impl JsonSchemaResource,
161 ) -> Result<Self, Error> {
162 if let Some(id) = subresource.id() {
163 let base_uri = self.registry.resolve_against(&self.base_uri.borrow(), id)?;
164 Ok(Resolver {
165 registry: self.registry,
166 base_uri,
167 scopes: self.scopes.clone(),
168 })
169 } else {
170 Ok(self.clone())
171 }
172 }
173 #[must_use]
174 pub fn dynamic_scope(&self) -> List<Uri<String>> {
175 self.scopes.clone()
176 }
177 fn evolve(&self, base_uri: Arc<Uri<String>>) -> Resolver<'r> {
178 if !self.base_uri.as_str().is_empty()
179 && (self.scopes.is_empty() || base_uri != self.base_uri)
180 {
181 Resolver {
182 registry: self.registry,
183 base_uri,
184 scopes: self.scopes.push_front(self.base_uri.clone()),
185 }
186 } else {
187 Resolver {
188 registry: self.registry,
189 base_uri,
190 scopes: self.scopes.clone(),
191 }
192 }
193 }
194 pub fn resolve_against(&self, base: &Uri<&str>, uri: &str) -> Result<Arc<Uri<String>>, Error> {
200 self.registry.resolve_against(base, uri)
201 }
202}
203
204#[derive(Debug)]
206pub struct Resolved<'r> {
207 contents: &'r Value,
209 resolver: Resolver<'r>,
211 draft: Draft,
212}
213
214impl<'r> Resolved<'r> {
215 pub(crate) fn new(contents: &'r Value, resolver: Resolver<'r>, draft: Draft) -> Self {
216 Self {
217 contents,
218 resolver,
219 draft,
220 }
221 }
222 #[must_use]
224 pub fn contents(&self) -> &'r Value {
225 self.contents
226 }
227 #[must_use]
229 pub fn resolver(&self) -> &Resolver<'r> {
230 &self.resolver
231 }
232 #[must_use]
233 pub fn into_inner(self) -> (&'r Value, Resolver<'r>, Draft) {
234 (self.contents, self.resolver, self.draft)
235 }
236}