Skip to main content

powerio_pkg/
provenance.rs

1//! Producer, origin, source descriptors, and source maps.
2//!
3//! These answer the trust questions a compiler artifact must answer: which tool
4//! produced it, what the source was, and which canonical field came from which
5//! source record by what kind of mapping.
6
7use std::collections::BTreeMap;
8
9use serde::{Deserialize, Serialize};
10
11/// The tool and build that produced the package.
12#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
13#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
14pub struct Producer {
15    pub tool: String,
16    pub version: String,
17    #[serde(default, skip_serializing_if = "Option::is_none")]
18    pub git_commit: Option<String>,
19    #[serde(default, skip_serializing_if = "Vec::is_empty")]
20    pub features: Vec<String>,
21}
22
23impl Producer {
24    /// The producer for packages built by this crate version of PowerIO.
25    pub fn powerio() -> Self {
26        Self {
27            tool: "powerio".to_owned(),
28            version: env!("CARGO_PKG_VERSION").to_owned(),
29            git_commit: None,
30            features: Vec::new(),
31        }
32    }
33}
34
35/// Where the package came from. Internally tagged on `kind` in JSON, so a reader
36/// distinguishes an in-memory model, a single text file (with or without
37/// retained source), a folder dataset, a partially decoded binary, a derived
38/// product of a lowering pass, or a composite of several sources.
39///
40/// The `hash` field is named consistently across all `Origin` variants.
41#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
42#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
43#[serde(tag = "kind", rename_all = "snake_case")]
44#[non_exhaustive]
45pub enum Origin {
46    /// Built in process, no source artifact.
47    InMemory,
48    File {
49        path: String,
50        format: String,
51        #[serde(default, skip_serializing_if = "Option::is_none")]
52        hash: Option<String>,
53        /// Whether the original source text was retained for a byte-exact
54        /// same-format echo. The retained text itself is not embedded in the
55        /// package; this only records that it exists at the frontend.
56        #[serde(default)]
57        retained_source: bool,
58    },
59    Folder {
60        path: String,
61        format: String,
62        #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
63        file_hashes: BTreeMap<String, String>,
64    },
65    BinaryFile {
66        path: String,
67        format: String,
68        #[serde(default, skip_serializing_if = "Option::is_none")]
69        hash: Option<String>,
70        #[serde(default, skip_serializing_if = "Vec::is_empty")]
71        decoded_sections: Vec<String>,
72    },
73    /// A model produced by a lowering/normalization pass from another package.
74    Derived {
75        #[serde(default, skip_serializing_if = "Option::is_none")]
76        parent_package_id: Option<String>,
77        pass: String,
78        #[serde(default, skip_serializing_if = "serde_json::Map::is_empty")]
79        options: serde_json::Map<String, serde_json::Value>,
80    },
81    /// Several sources combined, e.g. a static case plus a profile set.
82    Composite { sources: Vec<String> },
83}
84
85/// A declared source artifact, referenced from source maps and diagnostics by
86/// its `id`. (`sources[]` in the package.)
87#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
88#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
89pub struct SourceDescriptor {
90    pub id: String,
91    pub kind: String,
92    #[serde(default, skip_serializing_if = "Option::is_none")]
93    pub path: Option<String>,
94    #[serde(default, skip_serializing_if = "Option::is_none")]
95    pub format: Option<String>,
96    #[serde(default, skip_serializing_if = "Option::is_none")]
97    pub hash: Option<String>,
98}
99
100/// A pointer into one source artifact: where a canonical field came from.
101#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
102#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
103pub struct SourceRef {
104    pub source_id: String,
105    #[serde(default, skip_serializing_if = "Option::is_none")]
106    pub line: Option<u32>,
107    #[serde(default, skip_serializing_if = "Option::is_none")]
108    pub column: Option<u32>,
109    /// Byte offset, for binary sources.
110    #[serde(default, skip_serializing_if = "Option::is_none")]
111    pub byte_offset: Option<u64>,
112    /// Record / section / object type, e.g. `bus`.
113    #[serde(default, skip_serializing_if = "Option::is_none")]
114    pub record: Option<String>,
115    /// Canonical field / property name, e.g. `vm`.
116    #[serde(default, skip_serializing_if = "Option::is_none")]
117    pub field: Option<String>,
118    /// Raw token / value, when safe to embed.
119    #[serde(default, skip_serializing_if = "Option::is_none")]
120    pub raw_token: Option<String>,
121}
122
123impl SourceRef {
124    /// Create a reference to a declared source artifact.
125    pub fn new(source_id: impl Into<String>) -> Self {
126        Self {
127            source_id: source_id.into(),
128            line: None,
129            column: None,
130            byte_offset: None,
131            record: None,
132            field: None,
133            raw_token: None,
134        }
135    }
136
137    /// Set the field or property name.
138    #[must_use]
139    pub fn with_field(mut self, field: impl Into<String>) -> Self {
140        self.field = Some(field.into());
141        self
142    }
143
144    /// Set the source record, section, or object type.
145    #[must_use]
146    pub fn with_record(mut self, record: impl Into<String>) -> Self {
147        self.record = Some(record.into());
148        self
149    }
150
151    /// Set the source line number.
152    #[must_use]
153    pub fn with_line(mut self, line: u32) -> Self {
154        self.line = Some(line);
155        self
156    }
157}
158
159/// How a canonical field relates to its source value.
160#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
161#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
162#[serde(rename_all = "snake_case")]
163#[non_exhaustive]
164pub enum MappingKind {
165    /// Copied verbatim from the source.
166    Exact,
167    /// Materialized from a format default rather than the source text.
168    Defaulted,
169    /// Inferred from other source data.
170    Inferred,
171    /// Converted into canonical units (e.g. ohms to per unit).
172    ConvertedUnits,
173    /// Produced by a lowering pass (e.g. positive-sequence equivalent).
174    Lowered,
175    /// One canonical field aggregated from several source records.
176    Aggregated,
177    /// One source record split into several canonical fields/elements.
178    Split,
179    /// Synthesized with no direct source (e.g. a generated bus id).
180    Synthetic,
181    /// Extra data from the source preserved verbatim.
182    RetainedExtra,
183}
184
185/// How confident the source map entry is.
186#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
187#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
188#[serde(rename_all = "snake_case")]
189pub enum Confidence {
190    Exact,
191    High,
192    Medium,
193    Low,
194}
195
196/// One `element_path -> source` mapping.
197#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
198#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
199pub struct SourceMapEntry {
200    /// JSON pointer (or best-effort locator) into the package payload.
201    pub element_path: String,
202    pub source_ref: SourceRef,
203    pub mapping_kind: MappingKind,
204    pub confidence: Confidence,
205}