flams_math_archives/
formats.rs

1use std::path::Path;
2
3use ftml_uris::{ArchiveId, DocumentUri, ModuleUri, UriPath};
4
5use crate::{
6    artifacts::{Artifact, FileOrString},
7    backend::AnyBackend,
8};
9
10pub mod __reexport {
11    pub use inventory::*;
12}
13
14#[derive(Copy, Clone, Debug, PartialEq, Eq)]
15pub enum FormatOrTargets<'a> {
16    Format(SourceFormatId),
17    Targets(&'a [BuildTargetId]),
18}
19
20#[derive(Copy, Clone, Debug)]
21pub struct SourceFormat {
22    pub name: &'static str,
23    pub description: &'static str,
24    pub targets: &'static [BuildTargetId],
25    pub file_extensions: &'static [&'static str],
26    pub dependencies: fn(BuildSpec) -> Vec<(BuildTargetId, TaskDependency)>,
27}
28impl SourceFormat {
29    #[inline]
30    #[must_use]
31    pub const fn id(&'static self) -> SourceFormatId {
32        SourceFormatId(self)
33    }
34}
35inventory::collect!(SourceFormat);
36#[macro_export]
37macro_rules! source_format {
38    ($i:ident { $($t:tt)* }) => {
39        pub static $i : $crate::formats::SourceFormat = $crate::formats::SourceFormat { $($t)* };
40        $crate::formats::__reexport::submit!{ $i }
41    };
42}
43
44impl SourceFormat {
45    #[inline]
46    pub fn all() -> impl Iterator<Item = SourceFormatId> {
47        inventory::iter.into_iter().map(SourceFormatId)
48    }
49    #[must_use]
50    pub fn get(name: &str) -> Option<SourceFormatId> {
51        Self::all().find(|e| e.name == name)
52    }
53}
54
55pub struct BuildResult {
56    pub log: FileOrString,
57    pub result: Result<Option<Box<dyn Artifact>>, Vec<TaskDependency>>,
58}
59impl BuildResult {
60    #[must_use]
61    pub fn err() -> Self {
62        Self {
63            log: FileOrString::Str(String::new().into_boxed_str()),
64            result: Err(Vec::new()),
65        }
66    }
67}
68impl Default for BuildResult {
69    fn default() -> Self {
70        Self {
71            log: FileOrString::Str(String::new().into_boxed_str()),
72            result: Ok(None),
73        }
74    }
75}
76
77pub struct BuildSpec<'a> {
78    pub uri: &'a DocumentUri,
79    pub source: either::Either<&'a Path, &'a str>,
80    pub backend: &'a AnyBackend,
81    pub rel_path: &'a UriPath,
82}
83
84#[derive(Debug, Clone, Hash, PartialEq, Eq)]
85pub struct TaskRef {
86    pub archive: ArchiveId,
87    pub rel_path: UriPath,
88    pub target: BuildTargetId,
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub enum TaskDependency {
93    Physical { task: TaskRef, strict: bool },
94    Logical { uri: ModuleUri, strict: bool },
95}
96
97#[derive(Copy, Clone, Debug)]
98pub struct BuildTarget {
99    pub name: &'static str,
100    pub description: &'static str,
101    // dependencies
102    // yields
103    pub run: fn(BuildSpec<'_>) -> BuildResult,
104}
105impl BuildTarget {
106    #[inline]
107    #[must_use]
108    pub const fn id(&'static self) -> BuildTargetId {
109        BuildTargetId(self)
110    }
111}
112inventory::collect!(BuildTarget);
113#[macro_export]
114macro_rules! build_target {
115    ($i:ident { $($t:tt)* }) => {
116        pub static $i : $crate::formats::BuildTarget = $crate::formats::BuildTarget { $($t)* };
117        $crate::formats::__reexport::submit!{ $i }
118    };
119}
120
121build_target!(CHECK {
122    name: "check",
123    description: "check content",
124    run: |_| BuildResult::default()
125});
126
127impl BuildTarget {
128    #[inline]
129    pub fn all() -> impl Iterator<Item = BuildTargetId> {
130        inventory::iter.into_iter().map(BuildTargetId)
131    }
132    #[must_use]
133    pub fn get(name: &str) -> Option<BuildTargetId> {
134        Self::all().find(|e| e.name == name)
135    }
136}
137
138#[derive(Copy, Clone)]
139pub struct SourceFormatId(&'static SourceFormat);
140impl std::ops::Deref for SourceFormatId {
141    type Target = &'static SourceFormat;
142    #[inline]
143    fn deref(&self) -> &Self::Target {
144        &self.0
145    }
146}
147impl PartialEq for SourceFormatId {
148    fn eq(&self, other: &Self) -> bool {
149        std::ptr::addr_eq(self.0, other.0)
150    }
151}
152impl Eq for SourceFormatId {}
153impl std::hash::Hash for SourceFormatId {
154    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
155        self.0.name.hash(state);
156    }
157}
158impl std::fmt::Debug for SourceFormatId {
159    #[inline]
160    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161        self.0.name.fmt(f)
162    }
163}
164
165impl std::fmt::Display for SourceFormatId {
166    #[inline]
167    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168        self.0.name.fmt(f)
169    }
170}
171
172impl ::serde::Serialize for SourceFormatId {
173    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
174    where
175        S: ::serde::Serializer,
176    {
177        serializer.serialize_str(self.0.name)
178    }
179}
180
181impl<'de> ::serde::Deserialize<'de> for SourceFormatId {
182    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
183    where
184        D: ::serde::Deserializer<'de>,
185    {
186        let s = String::deserialize(deserializer)?;
187        SourceFormat::get(&s).map_or_else(
188            || Err(::serde::de::Error::custom("Unknown source format")),
189            Ok,
190        )
191    }
192}
193
194#[derive(Copy, Clone)]
195pub struct BuildTargetId(&'static BuildTarget);
196impl std::ops::Deref for BuildTargetId {
197    type Target = &'static BuildTarget;
198    #[inline]
199    fn deref(&self) -> &Self::Target {
200        &self.0
201    }
202}
203impl PartialEq for BuildTargetId {
204    fn eq(&self, other: &Self) -> bool {
205        std::ptr::addr_eq(self.0, other.0)
206    }
207}
208impl Eq for BuildTargetId {}
209impl std::hash::Hash for BuildTargetId {
210    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
211        self.0.name.hash(state);
212    }
213}
214impl std::fmt::Debug for BuildTargetId {
215    #[inline]
216    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217        self.0.name.fmt(f)
218    }
219}
220
221impl std::fmt::Display for BuildTargetId {
222    #[inline]
223    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224        self.0.name.fmt(f)
225    }
226}
227
228impl ::serde::Serialize for BuildTargetId {
229    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
230    where
231        S: ::serde::Serializer,
232    {
233        serializer.serialize_str(self.0.name)
234    }
235}
236
237impl<'de> ::serde::Deserialize<'de> for BuildTargetId {
238    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
239    where
240        D: ::serde::Deserializer<'de>,
241    {
242        let s = String::deserialize(deserializer)?;
243        BuildTarget::get(&s).map_or_else(
244            || Err(::serde::de::Error::custom("Unknown build target")),
245            Ok,
246        )
247    }
248}