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}