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    /// Opacity in millionths.
147    pub opacity: Option<u32>,
148    /// Font family reference.
149    pub font: Option<String>,
150    /// Font size.
151    pub font_size: Option<Length>,
152    /// Text color expression or token.
153    pub color: Option<ColorSource>,
154}
155
156/// String/token or explicit typed color accepted by the YAML frontend.
157#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
158#[serde(untagged)]
159pub enum ColorSource {
160    /// Named, hex, functional, or `$token` spelling.
161    Text(String),
162    /// Tagged `Color` value such as `{ space: cmyk, ... }`.
163    Typed(Color),
164}
165
166/// Conditional style layer evaluated against the active binding context.
167#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
168#[serde(deny_unknown_fields)]
169pub struct ElementStyleRuleSource {
170    /// Deterministic boolean expression.
171    pub when: String,
172    /// Partial style overlaid when the expression is truthy.
173    pub style: StyleSource,
174}
175
176/// Named rectangular layout region.
177#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
178#[serde(deny_unknown_fields)]
179pub struct RegionSource {
180    /// Horizontal position.
181    pub x: Length,
182    /// Vertical position.
183    pub y: Length,
184    /// Width.
185    pub width: Length,
186    /// Height.
187    pub height: Length,
188    /// Optional inherited collision policy.
189    #[serde(default)]
190    pub collision: Option<CollisionSource>,
191}
192
193/// Supported typed data kinds.
194#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
195#[serde(rename_all = "snake_case")]
196pub enum DataTypeSource {
197    /// UTF-8 string.
198    String,
199    /// Signed integer.
200    Integer,
201    /// Exact decimal.
202    Decimal,
203    /// Boolean.
204    Boolean,
205    /// ISO date.
206    Date,
207    /// ISO date-time.
208    DateTime,
209    /// Duration.
210    Duration,
211    /// Exact decimal plus currency code object.
212    Currency,
213    /// Array.
214    Array,
215    /// Object.
216    Object,
217    /// Explicit null.
218    Null,
219}
220
221/// Typed input field declaration.
222#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
223#[serde(deny_unknown_fields)]
224pub struct DataFieldSource {
225    /// Required type.
226    #[serde(rename = "type")]
227    pub data_type: DataTypeSource,
228    /// Whether null is accepted.
229    #[serde(default)]
230    pub nullable: bool,
231    /// Optional deterministic computed expression.
232    #[serde(default)]
233    pub computed: Option<String>,
234}
235
236/// Declarative element frontend.
237#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
238#[serde(deny_unknown_fields)]
239pub struct ElementSource {
240    /// Unique stable ID.
241    pub id: String,
242    /// Element type.
243    #[serde(rename = "type")]
244    pub element_type: String,
245    /// Optional component to instantiate.
246    #[serde(default)]
247    pub component: Option<String>,
248    /// Component props.
249    #[serde(default)]
250    pub props: BTreeMap<String, serde_json::Value>,
251    /// Named slot content supplied to a component instance.
252    #[serde(default)]
253    pub slots: BTreeMap<String, Vec<ElementSource>>,
254    /// Horizontal source coordinate.
255    #[serde(default)]
256    pub x: Option<Length>,
257    /// Vertical source coordinate.
258    #[serde(default)]
259    pub y: Option<Length>,
260    /// Source width.
261    #[serde(default)]
262    pub width: Option<Length>,
263    /// Source height.
264    #[serde(default)]
265    pub height: Option<Length>,
266    /// Minimum, preferred, maximum, and aspect-ratio size intent.
267    #[serde(default)]
268    pub constraints: LayoutConstraints,
269    /// Optional horizontal alignment inside the resolved container.
270    #[serde(default)]
271    pub align_x: Option<Alignment>,
272    /// Optional vertical alignment inside the resolved container.
273    #[serde(default)]
274    pub align_y: Option<Alignment>,
275    /// Literal text before binding evaluation.
276    #[serde(default)]
277    pub text: Option<String>,
278    /// Text overflow, line, minimum-size, and writing-mode intent.
279    #[serde(default)]
280    pub text_options: TextSourceOptions,
281    /// First-class table declaration, valid only for `type: table`.
282    #[serde(default)]
283    pub table: Option<TableSource>,
284    /// Asset reference.
285    #[serde(default)]
286    pub asset: Option<String>,
287    /// Image crop, fit, focal-point, and EXIF behavior.
288    #[serde(default)]
289    pub image: ImageOptions,
290    /// Simple vector path commands.
291    #[serde(default)]
292    pub path: Vec<PathCommandSource>,
293    /// Named style references in cascade order.
294    #[serde(default)]
295    pub styles: Vec<String>,
296    /// Inline style.
297    #[serde(default)]
298    pub style: StyleSource,
299    /// Ordered conditional styles evaluated after the compiled style layers.
300    #[serde(default)]
301    pub style_rules: Vec<ElementStyleRuleSource>,
302    /// Translation, rotation, scale, flip, mirror, and origin intent.
303    #[serde(default)]
304    pub transform: TransformSource,
305    /// Layout strategy.
306    #[serde(default)]
307    pub layout: LayoutMode,
308    /// Distribution of children on a flow's primary axis.
309    #[serde(default)]
310    pub distribute: Distribution,
311    /// Gap between flow children.
312    #[serde(default = "default_gap")]
313    pub gap: Length,
314    /// Data binding expression.
315    #[serde(default)]
316    pub binding: Option<String>,
317    /// Visibility condition expression.
318    #[serde(default)]
319    pub when: Option<String>,
320    /// Repeat array expression.
321    #[serde(default)]
322    pub repeat: Option<String>,
323    /// Named anchors.
324    #[serde(default)]
325    pub anchors: BTreeMap<String, String>,
326    /// Named containing region.
327    #[serde(default)]
328    pub region: Option<String>,
329    /// Child nodes.
330    #[serde(default)]
331    pub children: Vec<ElementSource>,
332    /// Whether runtime patches may modify this node.
333    #[serde(default)]
334    pub locked: bool,
335    /// Whether the node is initially hidden.
336    #[serde(default)]
337    pub hidden: bool,
338    /// Visual layer.
339    #[serde(default)]
340    pub layer: String,
341    /// Visual order within a layer.
342    #[serde(default)]
343    pub z_index: i32,
344    /// Collision declaration independent from layer ordering.
345    #[serde(default)]
346    pub collision: Option<CollisionSource>,
347    /// Expansion provenance populated by the compiler and absent from YAML.
348    #[serde(skip)]
349    #[doc(hidden)]
350    pub provenance_components: Vec<String>,
351    /// Logical include path populated by the compiler and absent from YAML.
352    #[serde(skip)]
353    #[doc(hidden)]
354    pub provenance_source: Option<String>,
355}
356
357/// Geometry-first collision declaration or the shorthand `collision: false`.
358#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
359#[serde(untagged)]
360pub enum CollisionSource {
361    /// Enables or disables collision using the default policy.
362    Enabled(bool),
363    /// Supplies the complete collision policy.
364    Advanced(CollisionAdvancedSource),
365}
366
367impl Default for CollisionSource {
368    fn default() -> Self {
369        Self::Advanced(CollisionAdvancedSource::default())
370    }
371}
372
373/// Advanced geometry-first collision declaration.
374#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
375#[serde(default, deny_unknown_fields)]
376pub struct CollisionAdvancedSource {
377    /// Whether this node participates.
378    pub enabled: bool,
379    /// Collision group.
380    pub group: String,
381    /// Groups this node collides with; empty means all.
382    pub collides_with: Vec<String>,
383    /// Element IDs ignored by this node.
384    pub ignore: Vec<String>,
385    /// Higher values win movement conflicts.
386    pub priority: i32,
387    /// Whether the resolver may move this node.
388    pub movable: bool,
389    /// Resolved box used by the spatial index.
390    pub bounds: CollisionBounds,
391    /// Policy name: `push/error/overlay/next_page/shrink`.
392    pub policy: String,
393}
394
395impl Default for CollisionAdvancedSource {
396    fn default() -> Self {
397        Self {
398            enabled: true,
399            group: "default".to_owned(),
400            collides_with: Vec::new(),
401            ignore: Vec::new(),
402            priority: 0,
403            movable: true,
404            bounds: CollisionBounds::Layout,
405            policy: "push".to_owned(),
406        }
407    }
408}
409
410/// Vector path source commands.
411#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
412#[serde(tag = "command", rename_all = "snake_case", deny_unknown_fields)]
413pub enum PathCommandSource {
414    /// Move current position.
415    Move {
416        /// Horizontal coordinate.
417        x: Length,
418        /// Vertical coordinate.
419        y: Length,
420    },
421    /// Draw line.
422    Line {
423        /// Horizontal coordinate.
424        x: Length,
425        /// Vertical coordinate.
426        y: Length,
427    },
428    /// Draw cubic Bézier curve.
429    Curve {
430        /// First control x.
431        x1: Length,
432        /// First control y.
433        y1: Length,
434        /// Second control x.
435        x2: Length,
436        /// Second control y.
437        y2: Length,
438        /// End x.
439        x: Length,
440        /// End y.
441        y: Length,
442    },
443    /// Close current contour.
444    Close,
445}
446
447/// Author-provided policy consumed only by the optional AI bridge.
448#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
449#[serde(deny_unknown_fields)]
450pub struct AiSourcePolicy {
451    /// Compact statement of document purpose.
452    #[serde(default)]
453    pub purpose: String,
454    /// Bounded textual edit rules.
455    #[serde(default)]
456    pub rules: Vec<String>,
457    /// IDs the bridge may edit. Empty means policy default.
458    #[serde(default)]
459    pub editable: Vec<String>,
460    /// IDs the bridge must never edit.
461    #[serde(default)]
462    pub locked: Vec<String>,
463}