Skip to main content

boxferry_quadlet/
output.rs

1//! Validated native output file set.
2
3use std::fmt;
4
5use quadlet_lens::{
6    model::{DocumentSetError, NamedQuadletDocument, QuadletDocumentSet, UnitFileName},
7    render::GeneratedQuadletDocument,
8};
9
10/// One generated Quadlet file whose contents passed `QuadletLens` parse-back validation.
11#[derive(Clone, Eq, PartialEq)]
12pub struct QuadletFile {
13    name: UnitFileName,
14    text: String,
15}
16
17impl QuadletFile {
18    /// Returns the validated unit-file basename.
19    #[must_use]
20    pub const fn name(&self) -> &UnitFileName {
21        &self.name
22    }
23
24    /// Explicitly exposes the generated file contents.
25    ///
26    /// Generated files may contain environment values and other sensitive application data.
27    #[must_use]
28    pub fn text(&self) -> &str {
29        &self.text
30    }
31}
32
33impl fmt::Debug for QuadletFile {
34    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
35        formatter
36            .debug_struct("QuadletFile")
37            .field("name", &self.name)
38            .field("text", &"[REDACTED]")
39            .field("bytes", &self.text.len())
40            .finish()
41    }
42}
43
44/// Deterministically ordered generated files and their validated dependency graph.
45#[derive(Clone, Eq, PartialEq)]
46pub struct QuadletOutput {
47    files: Vec<QuadletFile>,
48    documents: QuadletDocumentSet,
49}
50
51impl QuadletOutput {
52    pub(crate) fn from_generated(generated: Vec<(String, GeneratedQuadletDocument)>) -> Result<Self, DocumentSetError> {
53        let mut files = Vec::with_capacity(generated.len());
54        let mut documents = Vec::with_capacity(generated.len());
55        for (name, generated) in generated {
56            let file_name = UnitFileName::new(name.clone())?;
57            documents.push(NamedQuadletDocument::new(name, generated.document().clone())?);
58            files.push(QuadletFile {
59                name: file_name,
60                text: generated.text().to_owned(),
61            });
62        }
63        let documents = QuadletDocumentSet::new(documents)?;
64        Ok(Self { files, documents })
65    }
66
67    /// Returns generated files in deterministic network, volume, optional pod, then service order.
68    #[must_use]
69    pub fn files(&self) -> &[QuadletFile] {
70        &self.files
71    }
72
73    /// Returns a generated file by exact basename.
74    #[must_use]
75    pub fn file(&self, name: &str) -> Option<&QuadletFile> {
76        self.files.iter().find(|file| file.name().as_str() == name)
77    }
78
79    /// Returns the parse-back-validated native document set and dependency graph.
80    #[must_use]
81    pub const fn document_set(&self) -> &QuadletDocumentSet {
82        &self.documents
83    }
84}
85
86impl fmt::Debug for QuadletOutput {
87    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
88        let names: Vec<_> = self.files.iter().map(|file| file.name().as_str()).collect();
89        formatter
90            .debug_struct("QuadletOutput")
91            .field("files", &names)
92            .field("complete_graph", &self.documents.graph().is_complete())
93            .finish()
94    }
95}