flams_ontology/uris/narrative/
documents.rs1use 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 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 }