flams_ontology/uris/narrative/
documents.rs

1use crate::languages::Language;
2use crate::uris::name::InvalidURICharacter;
3use crate::uris::{
4    debugdisplay, ArchiveURI, ArchiveURIRef, ArchiveURITrait, BaseURI, ContentURIRef, ContentURITrait, ModuleURI, Name, NarrativeURIRef, NarrativeURITrait, PathURI, PathURIRef, PathURITrait, URIOrRefTrait, URIParseError, URIRef, URIRefTrait, URIWithLanguage
5};
6use const_format::concatcp;
7use eyre::Context;
8use std::fmt::Display;
9use std::str::{FromStr, Split};
10
11lazy_static::lazy_static! {
12    static ref NO_DOCUMENT:DocumentURI = "http://unknown.source?a=no/archive&d=unknown_document&l=en".parse().unwrap_or_else(|_| unreachable!());
13}
14
15#[cfg(feature = "wasm")]
16#[cfg_attr(feature = "wasm", wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section))]
17const TS_URI: &str = "export type DocumentURI = string;";
18
19
20#[derive(Clone, PartialEq, Eq, Hash)]
21pub struct DocumentURI {
22    pub(in crate::uris) path: PathURI,
23    pub(in crate::uris) name: Name,
24    pub(in crate::uris) language: Language,
25}
26impl DocumentURI {
27    pub const SEPARATOR: char = 'd';
28    #[must_use]
29    pub fn no_doc() -> Self { NO_DOCUMENT.clone()}
30
31    #[must_use]
32    pub fn from_archive_relpath(a:ArchiveURI,rel_path:&str) -> eyre::Result<Self> {
33        #[cfg(windows)]
34        let replaced = rel_path.replace('\\',"/");
35        #[cfg(windows)]
36        let rel_path = &replaced;
37        let (path,mut name) = rel_path.rsplit_once('/')
38            .unwrap_or(("",rel_path));
39        name = name.rsplit_once('.').map_or_else(|| name,|(name,_)| name);
40        let lang = Language::from_rel_path(name);
41        name = name.strip_suffix(&format!(".{lang}")).unwrap_or(name);
42        ((a % path).wrap_err_with(|| format!("Error in path component `{path}`"))? & (name,lang)).wrap_err_with(|| format!("Error in name component `{name}`"))
43    }
44
45    /// #### Errors
46    pub fn module_uri_from(&self,name:&str) -> Result<ModuleURI,InvalidURICharacter> {
47        if self.name.last_name().as_ref() == name {
48            self.as_path().owned() | name
49        } else {
50            (self.as_path().owned() / self.name().last_name().as_ref())? | name
51        }
52    }
53}
54impl Display for DocumentURI {
55    #[inline]
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        write!(
58            f,
59            "{}&{}={}&{}={}",
60            self.path,
61            Self::SEPARATOR,
62            self.name,
63            Language::SEPARATOR,
64            self.language
65        )
66    }
67}
68debugdisplay!(DocumentURI);
69impl URIOrRefTrait for DocumentURI {
70    #[inline]
71    fn base(&self) -> &BaseURI {
72        self.path.base()
73    }
74    #[inline]
75    fn as_uri(&self) -> URIRef {
76        URIRef::Narrative(self.as_narrative())
77    }
78}
79impl URIWithLanguage for DocumentURI {
80    #[inline]
81    fn language(&self) -> Language {
82        self.language
83    }
84}
85impl NarrativeURITrait for DocumentURI {
86    #[inline]
87    fn as_narrative(&self) -> NarrativeURIRef {
88        NarrativeURIRef::Document(self)
89    }
90    #[inline]
91    fn document(&self) -> &DocumentURI {
92        self
93    }
94}
95
96impl DocumentURI {
97    #[inline]
98    #[must_use]
99    pub const fn name(&self) -> &Name {
100        &self.name
101    }
102    pub(super) fn pre_parse<R>(
103        s: &str,
104        uri_kind: &'static str,
105        f: impl FnOnce(Self, Split<char>) -> Result<R, URIParseError>,
106    ) -> Result<R, URIParseError> {
107        PathURI::pre_parse(s, uri_kind, |path, next, mut split| {
108            let Some(m) = next.or_else(|| split.next()) else {
109                return Err(URIParseError::MissingPartFor {
110                    uri_kind,
111                    part: "document name",
112                    original: s.to_string(),
113                });
114            };
115            m.strip_prefix(concatcp!(DocumentURI::SEPARATOR, "="))
116                .map_or_else(
117                    || {
118                        Err(URIParseError::MissingPartFor {
119                            uri_kind,
120                            part: "document name",
121                            original: s.to_string(),
122                        })
123                    },
124                    |name| {
125                        let Some(l) = split.next() else {
126                            return Err(URIParseError::MissingPartFor {
127                                uri_kind,
128                                part: "language",
129                                original: s.to_string(),
130                            });
131                        };
132                        l.strip_prefix(concatcp!(Language::SEPARATOR, "="))
133                            .map_or_else(
134                                || {
135                                    Err(URIParseError::MissingPartFor {
136                                        uri_kind,
137                                        part: "language",
138                                        original: s.to_string(),
139                                    })
140                                },
141                                |lang| {
142                                    let language = lang.parse().map_or_else(
143                                        |()| {
144                                            Err(URIParseError::InvalidLanguage {
145                                                uri_kind,
146                                                original: s.to_string(),
147                                            })
148                                        },
149                                        Ok,
150                                    )?;
151                                    f(
152                                        Self {
153                                            path,
154                                            name: name.parse()?,
155                                            language,
156                                        },
157                                        split,
158                                    )
159                                },
160                            )
161                    },
162                )
163        })
164    }
165}
166
167impl FromStr for DocumentURI {
168    type Err = URIParseError;
169    fn from_str(s: &str) -> Result<Self, Self::Err> {
170        Self::pre_parse(s, "document uri", |u, mut split| {
171            if split.next().is_some() {
172                return Err(URIParseError::TooManyPartsFor {
173                    uri_kind: "document uri",
174                    original: s.to_string(),
175                });
176            }
177            Ok(u)
178        })
179    }
180}
181impl ArchiveURITrait for DocumentURI {
182    #[inline]
183    fn archive_uri(&self) -> ArchiveURIRef {
184        self.path.archive_uri()
185    }
186}
187impl PathURITrait for DocumentURI {
188    #[inline]
189    fn as_path(&self) -> PathURIRef {
190        self.path.as_path()
191    }
192    #[inline]
193    fn path(&self) -> Option<&Name> {
194        self.path.path()
195    }
196}
197
198#[cfg(feature = "serde")]
199mod serde_impl {
200    use crate::uris::{serialize, DocumentURI};
201    serialize!(DE DocumentURI);
202}
203
204#[cfg(feature="tantivy")]
205impl tantivy::schema::document::ValueDeserialize for DocumentURI {
206    fn deserialize<'de, D>(deserializer: D) -> Result<Self, tantivy::schema::document::DeserializeError>
207        where D: tantivy::schema::document::ValueDeserializer<'de> {
208        deserializer.deserialize_string()?.parse()
209          .map_err(|_| tantivy::schema::document::DeserializeError::custom(""))
210    }
211  }