1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//! Step 2 — Circuit JSON parser (partial model).
//!
//! # Architecture choice: Serde and heterogeneous JSON
//!
//! tscircuit's Circuit JSON is an **array of heterogeneous objects**: every
//! element carries a discriminating `"type"` field (`source_component`,
//! `source_net`, `source_port`, `source_pad`, `source_trace`, ...).
//!
//! ```json
//! [
//! { "type": "source_component", ... },
//! { "type": "source_net", ... }
//! ]
//! ```
//!
//! This is the canonical *internally tagged enum* format for Serde:
//!
//! * `#[serde(tag = "type")]` names the discriminator: Serde reads `type`
//! first, then delegates deserialization of the **rest** of the object to
//! the matching variant. No hand-rolled "read `type`, `match`, then
//! `from_value`" — Serde does it in one pass.
//! * Variants are **newtype variants** (`Component(Component)`): each one
//! wraps its strongly typed struct. Serde accepts a struct behind an
//! internally tagged variant (its fields are read as if flattened into the
//! original object).
//! * `#[serde(rename = "...")]` maps the JSON literal to a readable Rust
//! name (`Component`, `Net`, `Pad`, ...). The JSON tag rarely moves; the
//! Rust name has to stay legible everywhere else in the code.
//! * `#[serde(other)]` on the `Unknown` variant: element types Phase 1 does
//! not model (`silkscreen_text`, `footprint`, `schematic_box`, ...) are
//! **skipped instead of failing the whole parse**. This is the central
//! robustness choice: the linter must complain about electrical rules, not
//! about our model being partial. (Serde constraint: `other` only exists
//! for internally/adjacent tagged enums, on a unit variant — exactly our
//! case. A `rename` on `Unknown` would only affect serialization, which
//! never happens here.)
//! * Serde silently ignores unknown fields **inside** a variant: the partial
//! model can grow (geometry, phase-2 pin-muxing, ...) without breaking
//! existing files.
//!
//! # Field names: snake_case is the source of truth
//!
//! circuit-json is snake_case, enforced by tscircuit's own
//! `check-snake-case` CI. Every identifier field therefore accepts the
//! canonical snake_case key (`source_component_id`, `source_net_id`,
//! `connected_source_port_ids`, ...), and the camelCase aliases
//! (`sourceComponentId`, `connectedSourcePortIds`) are kept purely as
//! defensive tolerance for older files or alternative generators — they cost
//! nothing. Passing *both* spellings for one field is a hard
//! `duplicate field` error: ambiguous input must not parse quietly.
//!
//! # `None` never means "not applicable"
//!
//! Every identifier field is `Option`, because the model is deliberately
//! *partial* at the deserialization level. But canonical circuit-json always
//! carries these identifiers, so `None` marks input that deviates from the
//! format — and a linter that silently skips what it cannot read would
//! validate malformed input. The linter's `malformed_element` rule surfaces
//! exactly that case, which is what keeps the whole pipeline fail-closed.
//!
//! The parse diagnostic (rustc-style label on the Serde position) lives in
//! the `diagnostic` submodule: only the data model stays here.
pub use JsonDiagnostic;
pub use ;
use Path;
use Deserialize;
/// One element of the Circuit JSON array.
///
/// Each variant maps to one possible value of the `"type"` field.
/// The circuit AST: the raw element array plus typed accessors.
///
/// Instead of `match`ing over `elements` everywhere, the per-type *views*
/// live here. The linter — and later the `mcu::` module — consume these
/// iterators without knowing anything about the JSON shape.
/// Deserializes the Circuit JSON array.
///
/// `path` only names the file inside the diagnostic.