flams_math_archives/backend/
temp.rs

1use crate::{
2    Archive,
3    backend::{AnyBackend, GlobalBackend, LocalBackend},
4    manager::ArchiveOrGroup,
5    utils::{AsyncEngine, errors::BackendError},
6};
7use ftml_ontology::{
8    domain::modules::{Module, ModuleLike},
9    narrative::{DocDataRef, DocumentRange, documents::Document, elements::Notation},
10    utils::Css,
11};
12use ftml_uris::{ArchiveId, DocumentElementUri, DocumentUri, ModuleUri, SymbolUri, UriPath};
13
14#[derive(Debug)]
15pub struct HTMLData {
16    pub html: Box<str>,
17    pub css: Box<[Css]>,
18    pub body: DocumentRange,
19    pub inner_offset: usize,
20    pub refs: Box<[u8]>,
21}
22
23#[derive(Clone, Debug)]
24pub struct TemporaryBackend {
25    inner: triomphe::Arc<TemporaryBackendI>,
26}
27impl Default for TemporaryBackend {
28    #[inline]
29    fn default() -> Self {
30        Self::new(AnyBackend::Global)
31    }
32}
33
34#[derive(Debug)]
35struct TemporaryBackendI {
36    modules: dashmap::DashMap<ModuleUri, Module, rustc_hash::FxBuildHasher>,
37    documents: dashmap::DashMap<DocumentUri, Document, rustc_hash::FxBuildHasher>,
38    html: dashmap::DashMap<DocumentUri, HTMLData, rustc_hash::FxBuildHasher>,
39    parent: AnyBackend,
40}
41
42impl TemporaryBackend {
43    pub fn reset<A: AsyncEngine>(&self) {
44        self.inner.modules.clear();
45        self.inner.documents.clear();
46        GlobalBackend.reset::<A>();
47    }
48
49    #[must_use]
50    pub fn new(parent: AnyBackend) -> Self {
51        Self {
52            inner: triomphe::Arc::new(TemporaryBackendI {
53                modules: dashmap::DashMap::default(),
54                documents: dashmap::DashMap::default(),
55                html: dashmap::DashMap::default(),
56                parent,
57            }),
58        }
59    }
60    pub fn add_module(&self, m: Module) {
61        self.inner.modules.insert(m.uri.clone(), m);
62    }
63    pub fn add_document(&self, d: Document) {
64        self.inner.documents.insert(d.uri.clone(), d);
65    }
66    pub fn add_html(&self, uri: DocumentUri, d: HTMLData) {
67        self.inner.html.insert(uri, d);
68    }
69}
70
71impl LocalBackend for TemporaryBackend {
72    type ArchiveIter<'a> = <AnyBackend as LocalBackend>::ArchiveIter<'a>;
73
74    #[inline]
75    fn save(
76        &self,
77        in_doc: &ftml_uris::DocumentUri,
78        rel_path: Option<&UriPath>,
79        log: crate::artifacts::FileOrString,
80        from: crate::formats::BuildTargetId,
81        result: Option<Box<dyn crate::artifacts::Artifact>>,
82    ) -> std::result::Result<(), crate::utils::errors::ArtifactSaveError> {
83        self.inner.parent.save(in_doc, rel_path, log, from, result)
84    }
85
86    fn get_document(&self, uri: &DocumentUri) -> Result<Document, BackendError> {
87        self.inner.documents.get(uri).map_or_else(
88            || self.inner.parent.get_document(uri),
89            |e| Ok(e.value().clone()),
90        )
91    }
92
93    fn get_document_async<A: AsyncEngine>(
94        &self,
95        uri: &DocumentUri,
96    ) -> std::pin::Pin<Box<dyn Future<Output = Result<Document, BackendError>> + Send>>
97    where
98        Self: Sized,
99    {
100        if let Some(d) = self.inner.documents.get(uri) {
101            return Box::pin(std::future::ready(Ok(d.value().clone()))) as _;
102        }
103        Box::pin(self.inner.parent.get_document_async::<A>(uri)) as _
104    }
105
106    fn get_module(&self, uri: &ModuleUri) -> Result<ModuleLike, BackendError> {
107        if uri.is_top() {
108            self.inner.modules.get(uri).map_or_else(
109                || self.inner.parent.get_module(uri),
110                |e| Ok(ModuleLike::Module(e.value().clone())),
111            )
112        } else {
113            // SAFETY: !is_top()
114            let SymbolUri { name, module } =
115                unsafe { uri.clone().into_symbol().unwrap_unchecked() };
116            let Some(m) = self.inner.modules.get(&module) else {
117                return self.inner.parent.get_module(uri);
118            };
119            m.as_module_like(&name)
120                .ok_or(BackendError::NotFound(ftml_uris::UriKind::Symbol))
121        }
122    }
123
124    fn get_module_async<A: AsyncEngine>(
125        &self,
126        uri: &ModuleUri,
127    ) -> std::pin::Pin<Box<dyn Future<Output = Result<ModuleLike, BackendError>> + Send>>
128    where
129        Self: Sized,
130    {
131        if uri.is_top() {
132            if let Some(m) = self.inner.modules.get(uri) {
133                Box::pin(std::future::ready(Ok(ModuleLike::Module(
134                    m.value().clone(),
135                ))))
136            } else {
137                Box::pin(self.inner.parent.get_module_async::<A>(uri)) as _
138            }
139        } else {
140            // SAFETY: !is_top()
141            let SymbolUri { name, module } =
142                unsafe { uri.clone().into_symbol().unwrap_unchecked() };
143            if let Some(m) = self.inner.modules.get(&module) {
144                let r = m
145                    .as_module_like(&name)
146                    .ok_or(BackendError::NotFound(ftml_uris::UriKind::Symbol));
147                Box::pin(std::future::ready(r)) as _
148            } else {
149                Box::pin(self.inner.parent.get_module_async::<A>(&uri)) as _
150            }
151        }
152    }
153
154    #[inline]
155    fn with_archive_or_group<R>(
156        &self,
157        id: &ArchiveId,
158        f: impl FnOnce(Option<&ArchiveOrGroup>) -> R,
159    ) -> R
160    where
161        Self: Sized,
162    {
163        self.inner.parent.with_archive_or_group(id, f)
164    }
165
166    #[inline]
167    fn with_archive<R>(&self, id: &ArchiveId, f: impl FnOnce(Option<&Archive>) -> R) -> R
168    where
169        Self: Sized,
170    {
171        self.inner.parent.with_archive(id, f)
172    }
173
174    #[inline]
175    fn with_archives<R>(&self, f: impl FnOnce(Self::ArchiveIter<'_>) -> R) -> R
176    where
177        Self: Sized,
178    {
179        self.inner.parent.with_archives(f)
180    }
181
182    fn get_html_body(&self, d: &DocumentUri) -> Result<(Box<[Css]>, Box<str>), BackendError> {
183        self.inner.html.get(d).map_or_else(
184            || self.inner.parent.get_html_body(d),
185            |html| {
186                Ok((
187                    html.css.clone(),
188                    html.html[html.body.start..html.body.end]
189                        .to_string()
190                        .into_boxed_str(),
191                ))
192            },
193        )
194    }
195
196    fn get_html_body_async<A: AsyncEngine>(
197        &self,
198        uri: &ftml_uris::DocumentUri,
199    ) -> std::pin::Pin<
200        Box<
201            dyn Future<Output = Result<(Box<[ftml_ontology::utils::Css]>, Box<str>), BackendError>>
202                + Send,
203        >,
204    >
205    where
206        Self: Sized,
207    {
208        if let Some(html) = self.inner.html.get(uri) {
209            return Box::pin(std::future::ready(Ok((
210                html.css.clone(),
211                html.html[html.body.start..html.body.end]
212                    .to_string()
213                    .into_boxed_str(),
214            )))) as _;
215        }
216        Box::pin(self.inner.parent.get_html_body_async::<A>(uri)) as _
217    }
218
219    fn get_html_body_inner(&self, d: &DocumentUri) -> Result<(Box<[Css]>, Box<str>), BackendError> {
220        self.inner.html.get(d).map_or_else(
221            || self.inner.parent.get_html_body_inner(d),
222            |html| {
223                Ok((
224                    html.css.clone(),
225                    html.html[html.body.start + html.inner_offset..html.body.end - "</body>".len()]
226                        .to_string()
227                        .into_boxed_str(),
228                ))
229            },
230        )
231    }
232
233    fn get_html_body_inner_async<A: AsyncEngine>(
234        &self,
235        uri: &ftml_uris::DocumentUri,
236    ) -> std::pin::Pin<
237        Box<
238            dyn Future<Output = Result<(Box<[ftml_ontology::utils::Css]>, Box<str>), BackendError>>
239                + Send,
240        >,
241    >
242    where
243        Self: Sized,
244    {
245        if let Some(html) = self.inner.html.get(uri) {
246            return Box::pin(std::future::ready(Ok((
247                html.css.clone(),
248                html.html[html.body.start + html.inner_offset..html.body.end - "</body>".len()]
249                    .to_string()
250                    .into_boxed_str(),
251            )))) as _;
252        }
253        Box::pin(self.inner.parent.get_html_body_inner_async::<A>(uri)) as _
254    }
255
256    fn get_html_full(&self, d: &DocumentUri) -> Result<Box<str>, BackendError> {
257        self.inner.html.get(d).map_or_else(
258            || self.inner.parent.get_html_full(d),
259            |html| Ok(html.html.clone()),
260        )
261    }
262
263    fn get_html_fragment(
264        &self,
265        d: &DocumentUri,
266        range: DocumentRange,
267    ) -> Result<(Box<[Css]>, Box<str>), BackendError> {
268        self.inner.html.get(d).map_or_else(
269            || self.inner.parent.get_html_fragment(d, range),
270            |html| {
271                Ok((
272                    html.css.clone(),
273                    html.html[range.start..range.end]
274                        .to_string()
275                        .into_boxed_str(),
276                ))
277            },
278        )
279    }
280
281    fn get_html_fragment_async<A: AsyncEngine>(
282        &self,
283        uri: &ftml_uris::DocumentUri,
284        range: ftml_ontology::narrative::DocumentRange,
285    ) -> std::pin::Pin<
286        Box<
287            dyn Future<Output = Result<(Box<[ftml_ontology::utils::Css]>, Box<str>), BackendError>>
288                + Send,
289        >,
290    > {
291        if let Some(html) = self.inner.html.get(uri) {
292            return Box::pin(std::future::ready(Ok((
293                html.css.clone(),
294                html.html[html.body.start + html.inner_offset..html.body.end]
295                    .to_string()
296                    .into_boxed_str(),
297            ))));
298        }
299        Box::pin(self.inner.parent.get_html_fragment_async::<A>(uri, range)) as _
300    }
301
302    fn get_reference<T: bincode::Decode<()>>(&self, rf: &DocDataRef<T>) -> Result<T, BackendError>
303    where
304        Self: Sized,
305    {
306        let Some(html) = self.inner.html.get(&rf.in_doc) else {
307            return self.inner.parent.get_reference(rf);
308        };
309
310        let Some(bytes) = html.refs.get(rf.start..rf.end) else {
311            return Err(BackendError::OutOfRangeError(rf.start, rf.end));
312        };
313        let (r, _) = bincode::decode_from_slice(bytes, bincode::config::standard())?;
314        Ok(r)
315    }
316
317    #[cfg(feature = "rdf")]
318    #[inline]
319    fn get_notations<E: AsyncEngine>(
320        &self,
321        uri: &SymbolUri,
322    ) -> impl Iterator<Item = (DocumentElementUri, Notation)>
323    where
324        Self: Sized,
325    {
326        self.inner.parent.get_notations::<E>(uri)
327    }
328
329    #[cfg(feature = "rdf")]
330    #[inline]
331    fn get_var_notations<E: AsyncEngine>(
332        &self,
333        uri: &DocumentElementUri,
334    ) -> impl Iterator<Item = (DocumentElementUri, Notation)>
335    where
336        Self: Sized,
337    {
338        self.inner.parent.get_var_notations::<E>(uri)
339    }
340
341    /*
342
343
344    #[inline]
345    fn get_base_path(&self, id: &ArchiveId) -> Option<PathBuf> {
346        self.inner.parent.get_base_path(id)
347    }
348
349    #[inline]
350    fn submit_triples(
351        &self,
352        in_doc: &DocumentUri,
353        rel_path: &str,
354        iter: impl Iterator<Item = flams_ontology::rdf::Triple>,
355    ) where
356        Self: Sized,
357    {
358        self.inner.parent.submit_triples(in_doc, rel_path, iter);
359    }
360     */
361}