flams_system/building/
mod.rs

1use std::{
2    num::NonZeroU32,
3    path::{Path, PathBuf},
4    str::FromStr,
5};
6
7use either::Either;
8use flams_math_archives::{
9    backend::AnyBackend,
10    formats::{BuildSpec, BuildTargetId, TaskDependency, TaskRef},
11};
12use flams_utils::{
13    triomphe::Arc,
14    vecmap::{VecMap, VecSet},
15};
16use ftml_ontology::utils::time::Eta;
17use ftml_uris::{ArchiveId, ArchiveUri, DocumentUri, Language, ModuleUri, UriPath, UriWithArchive};
18use parking_lot::RwLock;
19
20mod queue;
21pub mod queue_manager;
22pub use queue::QueueName;
23mod queueing;
24
25pub(crate) static BUILD_QUEUE_SPAN: std::sync::LazyLock<tracing::Span> = std::sync::LazyLock::new(
26    || tracing::info_span!(target:"build queue",parent:None,"Build Queue"),
27);
28
29#[cfg(all(test, feature = "tokio"))]
30mod tests;
31
32pub use queue::Queue;
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
35pub enum TaskState {
36    Running,
37    Queued,
38    Blocked,
39    Done,
40    Failed,
41    None,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub enum Dependency {
46    Physical {
47        task: TaskRef,
48        strict: bool,
49    },
50    Logical {
51        uri: ModuleUri,
52        strict: bool,
53    },
54    Resolved {
55        task: BuildTask,
56        step: BuildTargetId,
57        strict: bool,
58    },
59}
60impl From<TaskDependency> for Dependency {
61    fn from(value: TaskDependency) -> Self {
62        match value {
63            TaskDependency::Logical { uri, strict } => Self::Logical { uri, strict },
64            TaskDependency::Physical { task, strict } => Self::Physical { task, strict },
65        }
66    }
67}
68
69#[derive(Copy, Clone, Debug, PartialEq, Eq)]
70pub struct BuildTaskId(NonZeroU32);
71impl From<BuildTaskId> for u32 {
72    #[inline]
73    fn from(id: BuildTaskId) -> Self {
74        id.0.get()
75    }
76}
77
78#[derive(Debug, PartialEq, Eq)]
79struct BuildTaskI {
80    id: BuildTaskId,
81    uri: DocumentUri,
82    steps: Box<[BuildStep]>,
83    source: Either<PathBuf, String>,
84    rel_path: UriPath,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct BuildTask(Arc<BuildTaskI>);
89impl BuildTask {
90    #[inline]
91    /// # Errors
92    pub fn new(
93        id: BuildTaskId,
94        archive: ArchiveUri,
95        steps: Box<[BuildStep]>,
96        source: Either<PathBuf, String>,
97        rel_path: UriPath,
98    ) -> eyre::Result<Self> {
99        let uri = DocumentUri::from_archive_relpath(archive, rel_path.as_ref())
100            .map_err(eyre::Report::new)?;
101        Ok(Self(Arc::new(BuildTaskI {
102            uri,
103            id,
104            steps,
105            source,
106            rel_path,
107        })))
108    }
109
110    #[must_use]
111    #[inline]
112    pub fn document_uri(&self) -> &DocumentUri {
113        &self.0.uri
114    }
115
116    #[must_use]
117    pub fn as_build_spec<'a>(&'a self, backend: &'a AnyBackend) -> BuildSpec<'a> {
118        BuildSpec {
119            uri: &self.0.uri,
120            source: self.source(),
121            backend,
122            rel_path: self.rel_path(),
123        }
124    }
125
126    #[must_use]
127    pub fn as_task_ref(&self, target: BuildTargetId) -> TaskRef {
128        TaskRef {
129            archive: self.0.uri.archive_id().clone(),
130            rel_path: self.0.rel_path.clone(),
131            target,
132        }
133    }
134
135    #[inline]
136    #[must_use]
137    pub fn source(&self) -> Either<&Path, &str> {
138        match &self.0.source {
139            Either::Left(p) => Either::Left(p),
140            Either::Right(s) => Either::Right(s),
141        }
142    }
143
144    #[inline]
145    #[must_use]
146    pub fn archive(&self) -> &ArchiveUri {
147        self.0.uri.archive_uri()
148    }
149
150    #[inline]
151    #[must_use]
152    pub fn rel_path(&self) -> &UriPath {
153        &self.0.rel_path
154    }
155
156    #[inline]
157    #[must_use]
158    pub fn steps(&self) -> &[BuildStep] {
159        &self.0.steps
160    }
161
162    #[inline]
163    #[must_use]
164    pub fn get_step(&self, target: BuildTargetId) -> Option<&BuildStep> {
165        self.0.steps.iter().find(|s| s.0.target == target)
166    }
167
168    #[must_use]
169    #[allow(clippy::cast_possible_truncation)]
170    pub fn as_message(&self) -> QueueEntry {
171        /*let idx = self.steps().iter().enumerate().find(|s|
172            matches!(&*s.1.0.state.read(),TaskState::Running | TaskState::Queued | TaskState::Blocked | TaskState::Failed)
173        );
174        let idx = if let Some((idx,_)) = idx {(idx - 1) as u8} else {self.steps().len() as u8};
175        */
176        QueueEntry {
177            id: self.0.id,
178            archive: self.0.uri.archive_id().clone(),
179            rel_path: self.0.rel_path.clone(),
180            steps: self
181                .steps()
182                .iter()
183                .map(|s| (s.0.target, *s.0.state.read()))
184                .collect(),
185        }
186    }
187}
188
189#[derive(Debug)]
190struct BuildStepI {
191    //task:std::sync::Weak<BuildTaskI>,
192    target: BuildTargetId,
193    state: RwLock<TaskState>,
194    //yields:RwLock<Vec<ModuleUri>>,
195    requires: RwLock<VecSet<Dependency>>,
196    dependents: RwLock<Vec<(BuildTaskId, BuildTargetId)>>,
197}
198impl PartialEq for BuildStepI {
199    fn eq(&self, other: &Self) -> bool {
200        self.target == other.target
201    }
202}
203impl Eq for BuildStepI {}
204
205#[derive(Debug, Clone, PartialEq, Eq)]
206pub struct BuildStep(Arc<BuildStepI>);
207impl BuildStep {
208    pub fn add_dependency(&self, dep: Dependency) {
209        self.0.requires.write().insert(dep);
210    }
211    /*
212    #[must_use]
213    pub fn get_task(&self) -> BuildTask {
214        BuildTask(self.0.task.upgrade().unwrap_or_else(|| unreachable!()))
215    }
216    */
217}
218/*
219pub trait BuildArtifact: Any + 'static {
220    fn get_type_id() -> BuildArtifactTypeId
221    where
222        Self: Sized;
223    /// #### Errors
224    fn load(p: &Path) -> Result<Self, std::io::Error>
225    where
226        Self: Sized;
227    fn get_type(&self) -> BuildArtifactTypeId;
228    /// ### Errors
229    fn write(&self, path: &Path) -> Result<(), std::io::Error>;
230    fn as_any(&self) -> &dyn Any;
231}
232
233pub enum BuildResultArtifact {
234    File(BuildArtifactTypeId, PathBuf),
235    Data(Box<dyn BuildArtifact>),
236    None,
237}
238
239pub struct BuildResult {
240    pub log: Either<String, PathBuf>,
241    pub result: Result<BuildResultArtifact, Vec<Dependency>>,
242}
243impl BuildResult {
244    #[must_use]
245    #[inline]
246    pub const fn empty() -> Self {
247        Self {
248            log: Either::Left(String::new()),
249            result: Ok(BuildResultArtifact::None),
250        }
251    }
252    #[must_use]
253    #[inline]
254    pub const fn err() -> Self {
255        Self {
256            log: Either::Left(String::new()),
257            result: Err(Vec::new()),
258        }
259    }
260
261    #[inline]
262    pub fn with_err(s: String) -> Self {
263        Self {
264            log: Either::Left(s),
265            result: Err(Vec::new()),
266        }
267    }
268}
269
270*/
271
272#[derive(Debug, Clone)]
273pub struct QueueEntry {
274    pub id: BuildTaskId,
275    pub archive: ArchiveId,
276    pub rel_path: UriPath,
277    pub steps: VecMap<BuildTargetId, TaskState>,
278}
279
280#[derive(Debug, Clone)]
281pub enum QueueMessage {
282    Idle(Vec<QueueEntry>),
283    Started {
284        running: Vec<QueueEntry>,
285        queue: Vec<QueueEntry>,
286        blocked: Vec<QueueEntry>,
287        failed: Vec<QueueEntry>,
288        done: Vec<QueueEntry>,
289    },
290    Finished {
291        failed: Vec<QueueEntry>,
292        done: Vec<QueueEntry>,
293    },
294    TaskStarted {
295        id: BuildTaskId,
296        target: BuildTargetId,
297    },
298    TaskSuccess {
299        id: BuildTaskId,
300        target: BuildTargetId,
301        eta: Eta,
302    },
303    TaskFailed {
304        id: BuildTaskId,
305        target: BuildTargetId,
306        eta: Eta,
307    },
308}