xsd_parser/pipeline/parser/resolver/
mod.rs

1//! Defines the [`Resolver`] trait and different resolver implementations.
2
3mod file_resolver;
4mod many_resolver;
5mod noop_resolver;
6
7#[cfg(feature = "web-resolver")]
8mod web_resolver;
9
10use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
11
12use url::Url;
13
14use crate::models::schema::Namespace;
15
16pub use self::file_resolver::FileResolver;
17pub use self::many_resolver::ManyResolver;
18pub use self::noop_resolver::NoOpResolver;
19
20#[cfg(feature = "web-resolver")]
21pub use self::web_resolver::WebResolver;
22
23/// Trait that defines a so called resolver that can be used to load schema
24/// information from different sources.
25pub trait Resolver: Debug {
26    /// Buffer that contains the data of the resolved schema.
27    type Buffer;
28
29    /// Error that is returned by the resolver.
30    type Error: Display;
31
32    /// Try to resolve the schema information from the passed request.
33    ///
34    /// This methods tries to resolve the schema information by using the
35    /// information provided by the passed request. If the operation was
36    /// successful the name, url and a buffer that contains the schema information
37    /// is returned in `Ok(Some((name, url, buffer)))`. If the request could
38    /// not be resolved `Ok(None)` is returned.
39    ///
40    /// # Errors
41    ///
42    /// May return any error if resolving the schema information was not
43    /// successful.
44    #[allow(clippy::type_complexity)]
45    fn resolve(&mut self, req: &ResolveRequest) -> ResolveResult<Self>;
46}
47
48/// Helper type to simplify the result of a [`Resolver`].
49pub type ResolveResult<R> =
50    Result<Option<(Option<String>, Url, <R as Resolver>::Buffer)>, <R as Resolver>::Error>;
51
52/// Contains information about the requested resolve action.
53#[must_use]
54#[derive(Debug)]
55pub struct ResolveRequest {
56    /// Location of the requested schema.
57    pub requested_location: String,
58
59    /// Namespace of the requested schema.
60    pub requested_ns: Option<Namespace>,
61
62    /// Location of the current processed schema.
63    pub current_location: Option<Url>,
64
65    /// Namespace of the current processed schema.
66    pub current_ns: Option<Namespace>,
67
68    /// Indicates wether the request was initiated
69    /// by the user, an import or an include.
70    pub request_type: ResolveRequestType,
71}
72
73/// Distinction of included vs imported schemas
74#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
75pub enum ResolveRequestType {
76    /// Schema was added directly by the user
77    #[default]
78    UserDefined,
79    /// Schema has been included
80    IncludeRequest,
81    /// Schema has been imported
82    ImportRequest,
83}
84
85impl ResolveRequest {
86    /// Create a new [`ResolveRequest`] instance from the passed `requested_location` and `request_type`.
87    pub fn new<X: Into<String>>(requested_location: X, request_type: ResolveRequestType) -> Self {
88        Self {
89            requested_location: requested_location.into(),
90            requested_ns: None,
91
92            current_location: None,
93            current_ns: None,
94            request_type,
95        }
96    }
97
98    /// Set the `requested_ns` field of this [`ResolveRequest`] instance.
99    pub fn requested_ns(mut self, ns: Namespace) -> Self {
100        self.requested_ns = Some(ns);
101
102        self
103    }
104
105    /// Set the `current_ns` field of this [`ResolveRequest`] instance.
106    pub fn current_ns(mut self, ns: Namespace) -> Self {
107        self.current_ns = Some(ns);
108
109        self
110    }
111
112    /// Set the `current_location` field of this [`ResolveRequest`] instance.
113    pub fn current_location<X: Into<Url>>(mut self, location: X) -> Self {
114        self.current_location = Some(location.into());
115
116        self
117    }
118}
119
120impl Display for ResolveRequest {
121    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
122        write!(f, "Location={}", self.requested_location)?;
123
124        if let Some(ns) = &self.requested_ns {
125            write!(f, "({})", ns)?;
126        }
127
128        if let Some(current) = &self.current_location {
129            write!(f, "Current={}", current)?;
130        }
131
132        if let Some(current_ns) = &self.current_ns {
133            write!(f, "({})", current_ns)?;
134        }
135
136        Ok(())
137    }
138}
139
140fn strip_name_ext(s: &str) -> &str {
141    if let Some(s) = s.strip_suffix(".xml") {
142        s
143    } else if let Some(s) = s.strip_suffix(".xsd") {
144        s
145    } else {
146        s
147    }
148}