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 { id: ProcessorId::new(id), transform }
69    }
70}
71
72/// Denotes failures of the deterministic processing interpreter.
73#[derive(Debug, Error, PartialEq, Eq)]
74pub enum ReferenceProcessingError {
75    /// Denotes application of an unregistered processor.
76    #[error("unknown processor {0:?}")]
77    UnknownProcessor(ProcessorId),
78    /// Denotes failure of a registered processor.
79    #[error("processor {processor:?} failed: {message}")]
80    ProcessorFailed {
81        /// Names the failing processor.
82        processor: ProcessorId,
83        /// Preserves its failure message.
84        message: String,
85    },
86}
87
88/// Interprets processing programs over an in-memory artifact set.
89#[derive(Debug, Default)]
90pub struct ReferenceProcessorEnv {
91    artifacts: ArtifactSet,
92    processors: BTreeMap<ProcessorId, ReferenceProcessor>,
93    trace: Vec<ProcessingStep>,
94}
95
96impl ReferenceProcessorEnv {
97    /// Constructs a processing environment with initial artifacts.
98    #[must_use]
99    pub fn new(artifacts: ArtifactSet) -> Self {
100        Self { artifacts, processors: BTreeMap::default(), trace: Vec::default() }
101    }
102
103    /// Registers a processor by semantic identity.
104    pub fn register(&mut self, processor: ReferenceProcessor) -> Option<ReferenceProcessor> {
105        self.processors.insert(processor.id.clone(), processor)
106    }
107
108    /// Observes the current artifact set.
109    #[must_use]
110    pub fn artifacts(&self) -> &ArtifactSet {
111        &self.artifacts
112    }
113
114    /// Observes successfully applied steps in application order.
115    #[must_use]
116    pub fn trace(&self) -> &[ProcessingStep] {
117        &self.trace
118    }
119}
120
121impl ProcessingSorts for ReferenceProcessorEnv {
122    type Processor = ();
123    type Step = ProcessingStep;
124    type Program = ProcessingProgram;
125}
126
127impl ProcessingApplyAlg for ReferenceProcessorEnv {
128    type Error = ReferenceProcessingError;
129
130    fn apply_processing_step(&mut self, step: &ProcessingStep) -> Result<(), Self::Error> {
131        let processor = self
132            .processors
133            .get(&step.processor)
134            .ok_or_else(|| ReferenceProcessingError::UnknownProcessor(step.processor.clone()))?;
135        match &processor.transform {
136            ReferenceArtifactTransform::Identity => {}
137            ReferenceArtifactTransform::Move { artifact, path } => {
138                let item =
139                    self.artifacts.get_mut(artifact).ok_or_else(|| ReferenceProcessingError::ProcessorFailed {
140                        processor: step.processor.clone(),
141                        message: format!("missing artifact {artifact}"),
142                    })?;
143                item.metadata.insert(ARTIFACT_LOCATION, path.clone());
144            }
145            ReferenceArtifactTransform::Fail(message) => {
146                return Err(ReferenceProcessingError::ProcessorFailed {
147                    processor: step.processor.clone(),
148                    message: message.clone(),
149                });
150            }
151        }
152        self.trace.push(step.clone());
153        Ok(())
154    }
155}
156
157impl ProcessingProgramViewAlg for ReferenceProcessorEnv {
158    fn processing_steps<'a>(program: &'a Self::Program) -> impl Iterator<Item = &'a Self::Step>
159    where
160        Self::Step: 'a,
161    {
162        ProcessingSyntax::processing_steps(program)
163    }
164
165    fn processing_stage(step: &Self::Step) -> ProcessingStage {
166        ProcessingSyntax::processing_stage(step)
167    }
168}