Skip to main content

rsynko_memory/
processing.rs

1use crate::ProcessingSyntax;
2use crate::{Artifact, ProcessingProgram, ProcessingStep, ProcessorId};
3use rsynko_media::*;
4use std::collections::BTreeMap;
5use thiserror::Error;
6
7/// Stores artifacts by identity for the in-memory processing interpreter.
8#[derive(Clone, Debug, Default, PartialEq)]
9pub struct ArtifactSet(BTreeMap<String, Artifact>);
10
11impl ArtifactSet {
12    /// Inserts an artifact by identity and returns the previous artifact, if any.
13    pub fn insert(&mut self, artifact: Artifact) -> Option<Artifact> {
14        self.0.insert(artifact.id.clone(), artifact)
15    }
16
17    /// Observes one artifact by identity.
18    #[must_use]
19    pub fn get(&self, id: &str) -> Option<&Artifact> {
20        self.0.get(id)
21    }
22
23    /// Observes one artifact mutably by identity.
24    pub fn get_mut(&mut self, id: &str) -> Option<&mut Artifact> {
25        self.0.get_mut(id)
26    }
27
28    /// Observes the number of stored artifacts.
29    #[must_use]
30    pub fn len(&self) -> usize {
31        self.0.len()
32    }
33
34    /// Observes whether no artifacts are stored.
35    #[must_use]
36    pub fn is_empty(&self) -> bool {
37        self.0.is_empty()
38    }
39}
40
41/// Denotes an inspectable artifact transformation in the reference interpreter.
42#[derive(Clone, Debug, PartialEq, Eq)]
43pub enum ReferenceArtifactTransform {
44    /// Denotes the identity transformation.
45    Identity,
46    /// Denotes moving one artifact to another path.
47    Move {
48        /// Identifies the artifact to move.
49        artifact: String,
50        /// Names its resulting path.
51        path: String,
52    },
53    /// Denotes a transformation that always fails.
54    Fail(String),
55}
56
57/// Interprets one named artifact transformation in memory.
58#[derive(Clone, Debug, PartialEq, Eq)]
59pub struct ReferenceProcessor {
60    id: ProcessorId,
61    transform: ReferenceArtifactTransform,
62}
63
64impl ReferenceProcessor {
65    /// Constructs a deterministic artifact transformation.
66    #[must_use]
67    pub fn new(id: impl Into<String>, transform: ReferenceArtifactTransform) -> Self {
68        Self {
69            id: ProcessorId::new(id),
70            transform,
71        }
72    }
73}
74
75/// Denotes failures of the deterministic processing interpreter.
76#[derive(Debug, Error, PartialEq, Eq)]
77pub enum ReferenceProcessingError {
78    /// Denotes application of an unregistered processor.
79    #[error("unknown processor {0:?}")]
80    UnknownProcessor(ProcessorId),
81    /// Denotes failure of a registered processor.
82    #[error("processor {processor:?} failed: {message}")]
83    ProcessorFailed {
84        /// Names the failing processor.
85        processor: ProcessorId,
86        /// Preserves its failure message.
87        message: String,
88    },
89}
90
91/// Interprets processing programs over an in-memory artifact set.
92#[derive(Debug, Default)]
93pub struct ReferenceProcessorEnv {
94    artifacts: ArtifactSet,
95    processors: BTreeMap<ProcessorId, ReferenceProcessor>,
96    trace: Vec<ProcessingStep>,
97}
98
99impl ReferenceProcessorEnv {
100    /// Constructs a processing environment with initial artifacts.
101    #[must_use]
102    pub fn new(artifacts: ArtifactSet) -> Self {
103        Self {
104            artifacts,
105            processors: BTreeMap::default(),
106            trace: Vec::default(),
107        }
108    }
109
110    /// Registers a processor by semantic identity.
111    pub fn register(&mut self, processor: ReferenceProcessor) -> Option<ReferenceProcessor> {
112        self.processors.insert(processor.id.clone(), processor)
113    }
114
115    /// Observes the current artifact set.
116    #[must_use]
117    pub fn artifacts(&self) -> &ArtifactSet {
118        &self.artifacts
119    }
120
121    /// Observes successfully applied steps in application order.
122    #[must_use]
123    pub fn trace(&self) -> &[ProcessingStep] {
124        &self.trace
125    }
126}
127
128impl ProcessingSorts for ReferenceProcessorEnv {
129    type Processor = ();
130    type Step = ProcessingStep;
131    type Program = ProcessingProgram;
132}
133
134impl ProcessingApplyAlg for ReferenceProcessorEnv {
135    type Error = ReferenceProcessingError;
136
137    fn apply_processing_step(&mut self, step: &ProcessingStep) -> Result<(), Self::Error> {
138        let processor = self
139            .processors
140            .get(&step.processor)
141            .ok_or_else(|| ReferenceProcessingError::UnknownProcessor(step.processor.clone()))?;
142        match &processor.transform {
143            ReferenceArtifactTransform::Identity => {}
144            ReferenceArtifactTransform::Move { artifact, path } => {
145                let item = self.artifacts.get_mut(artifact).ok_or_else(|| {
146                    ReferenceProcessingError::ProcessorFailed {
147                        processor: step.processor.clone(),
148                        message: format!("missing artifact {artifact}"),
149                    }
150                })?;
151                item.metadata.insert(ARTIFACT_LOCATION, path.clone());
152            }
153            ReferenceArtifactTransform::Fail(message) => {
154                return Err(ReferenceProcessingError::ProcessorFailed {
155                    processor: step.processor.clone(),
156                    message: message.clone(),
157                });
158            }
159        }
160        self.trace.push(step.clone());
161        Ok(())
162    }
163}
164
165impl ProcessingProgramViewAlg for ReferenceProcessorEnv {
166    fn processing_steps<'a>(program: &'a Self::Program) -> impl Iterator<Item = &'a Self::Step>
167    where
168        Self::Step: 'a,
169    {
170        ProcessingSyntax::processing_steps(program)
171    }
172
173    fn processing_stage(step: &Self::Step) -> ProcessingStage {
174        ProcessingSyntax::processing_stage(step)
175    }
176}