Skip to main content

flams_math_archives/
formats.rs

1use std::path::Path;
2
3#[cfg(feature = "rdf")]
4use ftml_uris::FtmlUri;
5use ftml_uris::{ArchiveId, DocumentUri, ModuleUri, UriPath};
6
7use crate::{
8    artifacts::{Artifact, FileOrString},
9    backend::AnyBackend,
10};
11
12pub mod __reexport {
13    pub use inventory::*;
14}
15
16#[derive(Copy, Clone, Debug, PartialEq, Eq)]
17pub enum FormatOrTargets<'a> {
18    Format(SourceFormatId),
19    Targets(&'a [BuildTargetId]),
20}
21
22#[derive(Copy, Clone, Debug)]
23pub struct SourceFormat {
24    pub name: &'static str,
25    pub description: &'static str,
26    pub targets: &'static [BuildTargetId],
27    pub file_extensions: &'static [&'static str],
28    pub dependencies: fn(BuildSpec) -> Vec<(BuildTargetId, TaskDependency)>,
29}
30impl SourceFormat {
31    #[inline]
32    #[must_use]
33    pub const fn id(&'static self) -> SourceFormatId {
34        SourceFormatId(self)
35    }
36}
37inventory::collect!(SourceFormat);
38#[macro_export]
39macro_rules! source_format {
40    ($i:ident { $($t:tt)* }) => {
41        pub static $i : $crate::formats::SourceFormat = $crate::formats::SourceFormat { $($t)* };
42        $crate::formats::__reexport::submit!{ $i }
43    };
44}
45
46impl SourceFormat {
47    #[inline]
48    pub fn all() -> impl Iterator<Item = SourceFormatId> {
49        inventory::iter.into_iter().map(SourceFormatId)
50    }
51    #[must_use]
52    pub fn get(name: &str) -> Option<SourceFormatId> {
53        Self::all().find(|e| e.name == name)
54    }
55}
56
57pub struct BuildResult {
58    pub log: FileOrString,
59    pub result: Result<Option<Box<dyn Artifact>>, Vec<TaskDependency>>,
60}
61impl BuildResult {
62    #[must_use]
63    pub fn err() -> Self {
64        Self {
65            log: FileOrString::Str(String::new().into_boxed_str()),
66            result: Err(Vec::new()),
67        }
68    }
69}
70impl Default for BuildResult {
71    fn default() -> Self {
72        Self {
73            log: FileOrString::Str(String::new().into_boxed_str()),
74            result: Ok(None),
75        }
76    }
77}
78
79pub struct BuildSpec<'a> {
80    pub uri: &'a DocumentUri,
81    pub source: either::Either<&'a Path, &'a str>,
82    pub backend: &'a AnyBackend,
83    pub rel_path: &'a UriPath,
84}
85
86#[derive(Debug, Clone, Hash, PartialEq, Eq)]
87pub struct TaskRef {
88    pub archive: ArchiveId,
89    pub rel_path: UriPath,
90    pub target: BuildTargetId,
91}
92
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub enum TaskDependency {
95    Physical { task: TaskRef, strict: bool },
96    Logical { uri: ModuleUri, strict: bool },
97}
98
99#[derive(Copy, Clone, Debug)]
100pub struct BuildTarget {
101    pub name: &'static str,
102    pub description: &'static str,
103    // dependencies
104    // yields
105    pub run: fn(BuildSpec<'_>) -> BuildResult,
106}
107impl BuildTarget {
108    #[inline]
109    #[must_use]
110    pub const fn id(&'static self) -> BuildTargetId {
111        BuildTargetId(self)
112    }
113}
114inventory::collect!(BuildTarget);
115#[macro_export]
116macro_rules! build_target {
117    ($i:ident { $($t:tt)* }) => {
118        pub static $i : $crate::formats::BuildTarget = $crate::formats::BuildTarget { $($t)* };
119        $crate::formats::__reexport::submit!{ $i }
120    };
121}
122
123impl BuildTarget {
124    #[inline]
125    pub fn all() -> impl Iterator<Item = BuildTargetId> {
126        inventory::iter.into_iter().map(BuildTargetId)
127    }
128    #[must_use]
129    pub fn get(name: &str) -> Option<BuildTargetId> {
130        Self::all().find(|e| e.name == name)
131    }
132}
133
134#[derive(Copy, Clone)]
135pub struct SourceFormatId(&'static SourceFormat);
136impl std::ops::Deref for SourceFormatId {
137    type Target = &'static SourceFormat;
138    #[inline]
139    fn deref(&self) -> &Self::Target {
140        &self.0
141    }
142}
143impl PartialEq for SourceFormatId {
144    fn eq(&self, other: &Self) -> bool {
145        std::ptr::addr_eq(self.0, other.0)
146    }
147}
148impl Eq for SourceFormatId {}
149impl std::hash::Hash for SourceFormatId {
150    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
151        self.0.name.hash(state);
152    }
153}
154impl std::fmt::Debug for SourceFormatId {
155    #[inline]
156    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157        self.0.name.fmt(f)
158    }
159}
160
161impl std::fmt::Display for SourceFormatId {
162    #[inline]
163    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164        self.0.name.fmt(f)
165    }
166}
167
168impl ::serde::Serialize for SourceFormatId {
169    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
170    where
171        S: ::serde::Serializer,
172    {
173        serializer.serialize_str(self.0.name)
174    }
175}
176
177impl<'de> ::serde::Deserialize<'de> for SourceFormatId {
178    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
179    where
180        D: ::serde::Deserializer<'de>,
181    {
182        let s = String::deserialize(deserializer)?;
183        SourceFormat::get(&s).map_or_else(
184            || Err(::serde::de::Error::custom("Unknown source format")),
185            Ok,
186        )
187    }
188}
189
190#[derive(Copy, Clone)]
191pub struct BuildTargetId(&'static BuildTarget);
192impl std::ops::Deref for BuildTargetId {
193    type Target = &'static BuildTarget;
194    #[inline]
195    fn deref(&self) -> &Self::Target {
196        &self.0
197    }
198}
199impl PartialEq for BuildTargetId {
200    fn eq(&self, other: &Self) -> bool {
201        std::ptr::addr_eq(self.0, other.0)
202    }
203}
204impl Eq for BuildTargetId {}
205impl std::hash::Hash for BuildTargetId {
206    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
207        self.0.name.hash(state);
208    }
209}
210impl std::fmt::Debug for BuildTargetId {
211    #[inline]
212    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213        self.0.name.fmt(f)
214    }
215}
216
217impl std::fmt::Display for BuildTargetId {
218    #[inline]
219    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220        self.0.name.fmt(f)
221    }
222}
223
224impl ::serde::Serialize for BuildTargetId {
225    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
226    where
227        S: ::serde::Serializer,
228    {
229        serializer.serialize_str(self.0.name)
230    }
231}
232
233impl<'de> ::serde::Deserialize<'de> for BuildTargetId {
234    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
235    where
236        D: ::serde::Deserializer<'de>,
237    {
238        let s = String::deserialize(deserializer)?;
239        BuildTarget::get(&s).map_or_else(
240            || Err(::serde::de::Error::custom("Unknown build target")),
241            Ok,
242        )
243    }
244}
245
246#[cfg(feature = "rdf")]
247source_format! { RDF {
248    name:"rdf",
249    description:"Import .ttl-RDF-files into the triple store",
250    targets:&[RDF_IMPORT.id()],
251    file_extensions: &["ttl"],
252    dependencies: |_| Vec::new()
253}}
254
255#[cfg(feature = "rdf")]
256build_target! { RDF_IMPORT {
257    name:"import-rdf",
258    description:"imports existent .ttl file",
259    run: |s| {
260        let v = match s.source {
261            either::Left(path) => {
262                let file = match std::fs::File::open(path) {
263                    Err(e) => return BuildResult {
264                            log: FileOrString::Str(format!("Error reading {}: {e}",path.display()).into_boxed_str()),
265                            result: Err(Vec::new())
266                        },
267                    Ok(f) => f
268                };
269                let buf = std::io::BufReader::new(file);
270                let reader = oxigraph::io::RdfParser::from_format(oxigraph::io::RdfFormat::Turtle)
271                    .for_reader(buf);
272                match reader.map(|q| q.map(|q| ulo::rdf_types::Triple {
273                    subject:q.subject,
274                    predicate:q.predicate,
275                    object:q.object
276                })).collect::<Result<Vec<_>,_>>() {
277                    Ok(v) => v,
278                    Err(e) => return BuildResult {
279                            log: FileOrString::Str(format!("Error reading {}: {e}",path.display()).into_boxed_str()),
280                            result: Err(Vec::new())
281                        }
282                }
283            }
284            either::Right(txt) => {
285                let reader = oxigraph::io::RdfParser::from_format(oxigraph::io::RdfFormat::Turtle)
286                    .for_reader(txt.as_bytes());
287                match reader.map(|q| q.map(|q| ulo::rdf_types::Triple {
288                    subject:q.subject,
289                    predicate:q.predicate,
290                    object:q.object
291                })).collect::<Result<Vec<_>,_>>() {
292                    Ok(v) => v,
293                    Err(e) => return BuildResult {
294                            log: FileOrString::Str(format!("Error reading ttl: {e}").into_boxed_str()),
295                            result: Err(Vec::new())
296                        }
297                }
298            }
299        };
300        BuildResult {
301            log: FileOrString::Str(String::new().into_boxed_str()),
302            result: Ok(Some(Box::new(v)))
303        }
304    }
305}}