Skip to main content

appcore_filemaker/
source.rs

1// =============================================================================
2//        #######
3//     ###       ###     F: source.rs
4//    ##   ## ##   ##    P: AppCore-Runtime
5//         ## ##
6//                       C: 2026/08/30 05:00:00 by dnettoRaw
7//    ##   ## ##   ##    U: 2026/08/30 05:00:00 by dnettoRaw
8//      ###########      S: 1.0.2-rc
9// =============================================================================
10
11//! Defines bounded source contracts and behavior for this crate.
12
13use std::collections::BTreeMap;
14
15use serde::{Deserialize, Serialize};
16
17use crate::source_layout::default_gap;
18pub use crate::source_layout::ExclusionSource;
19pub use crate::source_page::{EdgeSource, PageLayerSource, PageSource};
20pub use crate::source_table::{TableSource, TableStyleRuleSource};
21pub use crate::source_text::TextSourceOptions;
22pub use crate::source_transform::{MirrorSource, TransformSource};
23
24use crate::{
25    Alignment, CollisionBounds, Color, Distribution, ImageOptions, LayoutConstraints, LayoutMode,
26    Length,
27};
28
29/// Top-level source model.
30#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
31#[serde(rename_all = "snake_case")]
32pub enum ModelKind {
33    /// Paginated document/report.
34    Document,
35    /// Free-form vector canvas.
36    Canvas,
37    /// Tabular dataset.
38    Dataset,
39}
40
41/// Version-one YAML frontend. This is never renderer input.
42#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
43#[serde(deny_unknown_fields)]
44pub struct TemplateSourceV1 {
45    /// Must equal `"1.0"`.
46    pub filemaker: String,
47    /// Source model.
48    pub model: ModelKind,
49    /// Stable logical template ID.
50    pub id: String,
51    /// Optional page/canvas declaration.
52    #[serde(default)]
53    pub page: Option<PageSource>,
54    /// Document-wide collision policy inherited by every page.
55    #[serde(default)]
56    pub collision: Option<CollisionSource>,
57    /// Explicit includes expanded before IR construction.
58    #[serde(default)]
59    pub includes: Vec<IncludeSource>,
60    /// Reusable component declarations.
61    #[serde(default)]
62    pub components: BTreeMap<String, ComponentSource>,
63    /// Theme token declarations.
64    #[serde(default)]
65    pub themes: BTreeMap<String, ThemeSource>,
66    /// Explicit active theme name.
67    #[serde(default)]
68    pub theme: Option<String>,
69    /// Template-level style applied after the active theme.
70    #[serde(default)]
71    pub style: StyleSource,
72    /// Named styles.
73    #[serde(default)]
74    pub styles: BTreeMap<String, StyleSource>,
75    /// Named guides.
76    #[serde(default)]
77    pub guides: BTreeMap<String, Length>,
78    /// Named layout regions.
79    #[serde(default)]
80    pub regions: BTreeMap<String, RegionSource>,
81    /// Named non-painted collision geometry repeated on every page.
82    #[serde(default)]
83    pub exclusions: BTreeMap<String, ExclusionSource>,
84    /// Typed input data schema.
85    #[serde(default)]
86    pub data_schema: BTreeMap<String, DataFieldSource>,
87    /// Root elements in stable source order.
88    #[serde(default)]
89    pub elements: Vec<ElementSource>,
90    /// Optional author intent for AI adapters. Core does not interpret it.
91    #[serde(default)]
92    pub ai: Option<AiSourcePolicy>,
93}
94
95/// Sandboxed include declaration.
96#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
97#[serde(deny_unknown_fields)]
98pub struct IncludeSource {
99    /// Logical resolver path.
100    pub path: String,
101    /// Optional namespace preventing ID collisions.
102    #[serde(default)]
103    pub namespace: Option<String>,
104}
105
106/// Component with typed/default props and element body.
107#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
108#[serde(deny_unknown_fields)]
109pub struct ComponentSource {
110    /// Default prop values as frontend values.
111    #[serde(default)]
112    pub props: BTreeMap<String, serde_json::Value>,
113    /// Named replaceable slots.
114    #[serde(default)]
115    pub slots: BTreeMap<String, Vec<ElementSource>>,
116    /// Component body.
117    #[serde(default)]
118    pub elements: Vec<ElementSource>,
119}
120
121/// Theme tokens and optional parent.
122#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
123#[serde(deny_unknown_fields)]
124pub struct ThemeSource {
125    /// Parent theme name.
126    #[serde(default)]
127    pub extends: Option<String>,
128    /// Token values.
129    #[serde(default)]
130    pub tokens: BTreeMap<String, serde_json::Value>,
131    /// Theme style layer.
132    #[serde(default)]
133    pub style: StyleSource,
134}
135
136/// Frontend style declaration. Typed conversion happens during expansion.
137#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
138#[serde(default, deny_unknown_fields)]
139pub struct StyleSource {
140    /// Fill color expression or token.
141    pub fill: Option<ColorSource>,
142    /// Stroke color expression or token.
143    pub stroke: Option<ColorSource>,
144    /// Stroke width.
145    pub stroke_width: Option<Length>,
146    /// Per-side visibility for table-cell borders.
147    pub stroke_sides: Option<crate::StrokeSides>,
148    /// Opacity in millionths.
149    pub opacity: Option<u32>,
150    /// Font family reference.
151    pub font: Option<String>,
152    /// Font size.
153    pub font_size: Option<Length>,
154    /// Text line-height ratio in millionths, for example 1250000 is 1.25.
155    pub line_height: Option<u32>,
156    /// Text color expression or token.
157    pub color: Option<ColorSource>,
158    /// Underline shaped text at its measured width.
159    pub underline: Option<bool>,
160}
161
162/// String/token or explicit typed color accepted by the YAML frontend.
163#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
164#[serde(untagged)]
165pub enum ColorSource {
166    /// Named, hex, functional, or `$token` spelling.
167    Text(String),
168    /// Tagged `Color` value such as `{ space: cmyk, ... }`.
169    Typed(Color),
170}
171
172/// Conditional style layer evaluated against the active binding context.
173#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
174#[serde(deny_unknown_fields)]
175pub struct ElementStyleRuleSource {
176    /// Deterministic boolean expression.
177    pub when: String,
178    /// Partial style overlaid when the expression is truthy.
179    pub style: StyleSource,
180}
181
182/// Named rectangular layout region.
183#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
184#[serde(deny_unknown_fields)]
185pub struct RegionSource {
186    /// Horizontal position.
187    pub x: Length,
188    /// Vertical position.
189    pub y: Length,
190    /// Width.
191    pub width: Length,
192    /// Height.
193    pub height: Length,
194    /// Optional inherited collision policy.
195    #[serde(default)]
196    pub collision: Option<CollisionSource>,
197}
198
199/// Supported typed data kinds.
200#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
201#[serde(rename_all = "snake_case")]
202pub enum DataTypeSource {
203    /// UTF-8 string.
204    String,
205    /// Signed integer.
206    Integer,
207    /// Exact decimal.
208    Decimal,
209    /// Boolean.
210    Boolean,
211    /// ISO date.
212    Date,
213    /// ISO date-time.
214    DateTime,
215    /// Duration.
216    Duration,
217    /// Exact decimal plus currency code object.
218    Currency,
219    /// Array.
220    Array,
221    /// Object.
222    Object,
223    /// Explicit null.
224    Null,
225}
226
227/// Typed input field declaration.
228#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
229#[serde(deny_unknown_fields)]
230pub struct DataFieldSource {
231    /// Required type.
232    #[serde(rename = "type")]
233    pub data_type: DataTypeSource,
234    /// Whether null is accepted.
235    #[serde(default)]
236    pub nullable: bool,
237    /// Optional deterministic computed expression.
238    #[serde(default)]
239    pub computed: Option<String>,
240}
241
242/// Declarative element frontend.
243#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
244#[serde(deny_unknown_fields)]
245pub struct ElementSource {
246    /// Unique stable ID.
247    pub id: String,
248    /// Element type.
249    #[serde(rename = "type")]
250    pub element_type: String,
251    /// Optional component to instantiate.
252    #[serde(default)]
253    pub component: Option<String>,
254    /// Component props.
255    #[serde(default)]
256    pub props: BTreeMap<String, serde_json::Value>,
257    /// Named slot content supplied to a component instance.
258    #[serde(default)]
259    pub slots: BTreeMap<String, Vec<ElementSource>>,
260    /// Horizontal source coordinate.
261    #[serde(default)]
262    pub x: Option<Length>,
263    /// Vertical source coordinate.
264    #[serde(default)]
265    pub y: Option<Length>,
266    /// Source width.
267    #[serde(default)]
268    pub width: Option<Length>,
269    /// Source height.
270    #[serde(default)]
271    pub height: Option<Length>,
272    /// Minimum, preferred, maximum, and aspect-ratio size intent.
273    #[serde(default)]
274    pub constraints: LayoutConstraints,
275    /// Optional horizontal alignment inside the resolved container.
276    #[serde(default)]
277    pub align_x: Option<Alignment>,
278    /// Optional vertical alignment inside the resolved container.
279    #[serde(default)]
280    pub align_y: Option<Alignment>,
281    /// Literal text before binding evaluation.
282    #[serde(default)]
283    pub text: Option<String>,
284    /// Ordered same-style inline text segments with explicit trailing gaps.
285    #[serde(default)]
286    pub text_segments: Vec<TextSegmentSource>,
287    /// Text overflow, line, minimum-size, and writing-mode intent.
288    #[serde(default)]
289    pub text_options: TextSourceOptions,
290    /// First-class table declaration, valid only for `type: table`.
291    #[serde(default)]
292    pub table: Option<TableSource>,
293    /// Asset reference.
294    #[serde(default)]
295    pub asset: Option<String>,
296    /// Image crop, fit, focal-point, and EXIF behavior.
297    #[serde(default)]
298    pub image: ImageOptions,
299    /// Simple vector path commands.
300    #[serde(default)]
301    pub path: Vec<PathCommandSource>,
302    /// Named style references in cascade order.
303    #[serde(default)]
304    pub styles: Vec<String>,
305    /// Inline style.
306    #[serde(default)]
307    pub style: StyleSource,
308    /// Ordered conditional styles evaluated after the compiled style layers.
309    #[serde(default)]
310    pub style_rules: Vec<ElementStyleRuleSource>,
311    /// Translation, rotation, scale, flip, mirror, and origin intent.
312    #[serde(default)]
313    pub transform: TransformSource,
314    /// Layout strategy.
315    #[serde(default)]
316    pub layout: LayoutMode,
317    /// Distribution of children on a flow's primary axis.
318    #[serde(default)]
319    pub distribute: Distribution,
320    /// Gap between flow children.
321    #[serde(default = "default_gap")]
322    pub gap: Length,
323    /// Data binding expression.
324    #[serde(default)]
325    pub binding: Option<String>,
326    /// Visibility condition expression.
327    #[serde(default)]
328    pub when: Option<String>,
329    /// Repeat array expression.
330    #[serde(default)]
331    pub repeat: Option<String>,
332    /// Named anchors.
333    #[serde(default)]
334    pub anchors: BTreeMap<String, String>,
335    /// Named containing region.
336    #[serde(default)]
337    pub region: Option<String>,
338    /// Child nodes.
339    #[serde(default)]
340    pub children: Vec<ElementSource>,
341    /// Whether runtime patches may modify this node.
342    #[serde(default)]
343    pub locked: bool,
344    /// Whether the node is initially hidden.
345    #[serde(default)]
346    pub hidden: bool,
347    /// Keep this element with following siblings in a vertical flow when the
348    /// complete contiguous block fits on one page.
349    #[serde(default)]
350    pub keep_with_next: bool,
351    /// Visual layer.
352    #[serde(default)]
353    pub layer: String,
354    /// Visual order within a layer.
355    #[serde(default)]
356    pub z_index: i32,
357    /// Collision declaration independent from layer ordering.
358    #[serde(default)]
359    pub collision: Option<CollisionSource>,
360    /// Expansion provenance populated by the compiler and absent from YAML.
361    #[serde(skip)]
362    #[doc(hidden)]
363    pub provenance_components: Vec<String>,
364    /// Logical include path populated by the compiler and absent from YAML.
365    #[serde(skip)]
366    #[doc(hidden)]
367    pub provenance_source: Option<String>,
368}
369
370/// One same-style inline segment. Exactly one of `text` and `binding` is set.
371#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
372#[serde(deny_unknown_fields)]
373pub struct TextSegmentSource {
374    /// Literal segment content.
375    #[serde(default)]
376    pub text: Option<String>,
377    /// Typed string binding expression for dynamic segment content.
378    #[serde(default)]
379    pub binding: Option<String>,
380    /// Explicit gap after this segment; only nonnegative absolute/logical units are accepted.
381    #[serde(default = "default_gap")]
382    pub gap_after: Length,
383}
384
385/// Geometry-first collision declaration or the shorthand `collision: false`.
386#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
387#[serde(untagged)]
388pub enum CollisionSource {
389    /// Enables or disables collision using the default policy.
390    Enabled(bool),
391    /// Supplies the complete collision policy.
392    Advanced(CollisionAdvancedSource),
393}
394
395impl Default for CollisionSource {
396    fn default() -> Self {
397        Self::Advanced(CollisionAdvancedSource::default())
398    }
399}
400
401/// Advanced geometry-first collision declaration.
402#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
403#[serde(default, deny_unknown_fields)]
404pub struct CollisionAdvancedSource {
405    /// Whether this node participates.
406    pub enabled: bool,
407    /// Collision group.
408    pub group: String,
409    /// Groups this node collides with; empty means all.
410    pub collides_with: Vec<String>,
411    /// Element IDs ignored by this node.
412    pub ignore: Vec<String>,
413    /// Higher values win movement conflicts.
414    pub priority: i32,
415    /// Whether the resolver may move this node.
416    pub movable: bool,
417    /// Resolved box used by the spatial index.
418    pub bounds: CollisionBounds,
419    /// Policy name: `push/error/overlay/next_page/shrink`.
420    pub policy: String,
421}
422
423impl Default for CollisionAdvancedSource {
424    fn default() -> Self {
425        Self {
426            enabled: true,
427            group: "default".to_owned(),
428            collides_with: Vec::new(),
429            ignore: Vec::new(),
430            priority: 0,
431            movable: true,
432            bounds: CollisionBounds::Layout,
433            policy: "push".to_owned(),
434        }
435    }
436}
437
438/// Vector path source commands.
439#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
440#[serde(tag = "command", rename_all = "snake_case", deny_unknown_fields)]
441pub enum PathCommandSource {
442    /// Move current position.
443    Move {
444        /// Horizontal coordinate.
445        x: Length,
446        /// Vertical coordinate.
447        y: Length,
448    },
449    /// Draw line.
450    Line {
451        /// Horizontal coordinate.
452        x: Length,
453        /// Vertical coordinate.
454        y: Length,
455    },
456    /// Draw cubic Bézier curve.
457    Curve {
458        /// First control x.
459        x1: Length,
460        /// First control y.
461        y1: Length,
462        /// Second control x.
463        x2: Length,
464        /// Second control y.
465        y2: Length,
466        /// End x.
467        x: Length,
468        /// End y.
469        y: Length,
470    },
471    /// Close current contour.
472    Close,
473}
474
475/// Author-provided policy consumed only by the optional AI bridge.
476#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
477#[serde(deny_unknown_fields)]
478pub struct AiSourcePolicy {
479    /// Compact statement of document purpose.
480    #[serde(default)]
481    pub purpose: String,
482    /// Bounded textual edit rules.
483    #[serde(default)]
484    pub rules: Vec<String>,
485    /// IDs the bridge may edit. Empty means policy default.
486    #[serde(default)]
487    pub editable: Vec<String>,
488    /// IDs the bridge must never edit.
489    #[serde(default)]
490    pub locked: Vec<String>,
491}