Skip to main content

aion_package/structure/
error.rs

1//! Error taxonomy for structure extraction and bounded Gleam regeneration.
2//!
3//! Every variant carries the offending module name, identifier, or reason as
4//! structured data so a consumer can render actionable guidance. There are no
5//! silent empty graphs: a missing or unreadable entry source, an unknown
6//! activity, an out-of-bounds delta, or an unsafe regenerated name is always a
7//! loud, typed failure.
8
9/// Errors produced while extracting a workflow graph model from a package or
10/// regenerating Gleam from a bounded structural delta.
11#[derive(thiserror::Error, Debug, PartialEq, Eq)]
12pub enum StructureError {
13    /// The manifest names an entry module whose Gleam source is absent from the
14    /// package `src/` set, so the primitive structure cannot be derived.
15    ///
16    /// A package may legitimately ship without source (beam-only deploys), but
17    /// structure extraction requires the verbatim entry-module source to read.
18    #[error(
19        "entry module `{module}` has no Gleam source in the package; structure extraction \
20         requires the verbatim entry-module source"
21    )]
22    MissingEntrySource {
23        /// Logical entry-module name named by the manifest.
24        module: String,
25    },
26
27    /// The entry-module source bytes are not valid UTF-8 and cannot be scanned
28    /// for the workflow vocabulary.
29    #[error("entry module `{module}` source is not valid UTF-8")]
30    EntrySourceNotUtf8 {
31        /// Logical entry-module name whose bytes failed to decode.
32        module: String,
33    },
34
35    /// The entry-module source never imports `aion/workflow`, so it invokes
36    /// none of the recorded primitives and has no extractable structure.
37    ///
38    /// This is reported rather than returning an empty graph, so a workflow the
39    /// extractor cannot understand fails loudly instead of rendering as blank.
40    #[error(
41        "entry module `{module}` does not import `aion/workflow`; the extractor only \
42         understands workflows that compose the `aion/workflow` primitive vocabulary"
43    )]
44    NoWorkflowImport {
45        /// Logical entry-module name that lacked the import.
46        module: String,
47    },
48
49    /// A `run` node names an activity the package manifest does not declare, so
50    /// the extracted graph would not correspond to the workflow's real
51    /// activities.
52    #[error(
53        "extracted `run` node references activity `{activity}`, which the manifest does not \
54         declare; the graph would not match the workflow's recorded activities"
55    )]
56    UnknownActivity {
57        /// The activity name found in source but absent from the manifest.
58        activity: String,
59    },
60
61    /// The manifest names an entry function whose definition is absent from the
62    /// entry-module source, so the control-flow walk has no root to start from.
63    ///
64    /// Reported rather than returning an empty graph: a workflow whose entry
65    /// function the extractor cannot locate fails loudly instead of rendering
66    /// as blank.
67    #[error(
68        "entry function `{function}` is not defined in entry module `{module}`; the control-flow \
69         walk requires the entry function's body to start from"
70    )]
71    EntryFunctionNotFound {
72        /// Logical entry-module name searched.
73        module: String,
74        /// Entry-function name named by the manifest but not found.
75        function: String,
76    },
77
78    /// A structural delta fell outside the bounded round-trip vocabulary, so it
79    /// is refused rather than synthesising unbounded code (CN6, ADR-014).
80    #[error("structural delta is outside the bounded round-trip vocabulary: {reason}")]
81    UnboundedDelta {
82        /// Why the delta is not part of the bounded set.
83        reason: String,
84    },
85
86    /// A delta targeted a node id that is not present in the graph.
87    #[error("structural delta targets node {id}, which is not present in the graph")]
88    DeltaTargetMissing {
89        /// The missing target node id.
90        id: usize,
91    },
92
93    /// A regenerated identifier (an activity or module name) would not be a
94    /// valid Gleam `snake_case` identifier, so emitting it would produce code
95    /// that does not type-check.
96    #[error("cannot regenerate Gleam: name `{name}` is not a valid Gleam identifier: {reason}")]
97    RegenInvalidName {
98        /// The offending name.
99        name: String,
100        /// Why the name cannot be used.
101        reason: String,
102    },
103}