Skip to main content

wyrd/authoring/
weave.rs

1//! Author graph definitions and the validated immutable [`Weave`].
2//!
3//! [`WeaveDef`] is the editable/serializable form. [`Weave`] is produced only
4//! after structural validation (unique ids, port directions, no fan-in, DAG).
5//! Runtime bind consumes a `Weave`; it is not executable by itself.
6
7use std::string::String;
8use std::vec::Vec;
9
10use crate::foundation::{KnotKind, NumericPath};
11
12use crate::ValidationError;
13
14#[cfg(feature = "schema")]
15use schemars::JsonSchema;
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Serialize};
18#[cfg(feature = "schema")]
19use std::borrow::ToOwned;
20
21/// Serializable author reference to a named knot port.
22#[derive(Clone, Debug, PartialEq, Eq)]
23#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24#[cfg_attr(feature = "schema", derive(JsonSchema))]
25pub struct PortRefDef {
26    /// Author knot id (must exist in the same weave or pattern inner graph).
27    pub knot: String,
28    /// Catalog port name on that knot (for example `"out"`, `"in_0"`).
29    pub port: String,
30}
31
32impl PortRefDef {
33    /// Build a port reference from author knot and catalog port names.
34    pub fn new(knot: impl Into<String>, port: impl Into<String>) -> Self {
35        Self {
36            knot: knot.into(),
37            port: port.into(),
38        }
39    }
40}
41
42/// Serializable knot definition.
43#[derive(Clone, Debug, PartialEq)]
44#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
45#[cfg_attr(feature = "schema", derive(JsonSchema))]
46pub struct KnotDef {
47    /// Unique author id within the weave (non-empty, no `/` in pattern inners).
48    pub id: String,
49    /// Knot kind and parameters from the closed catalog.
50    pub kind: KnotKind,
51}
52
53/// Serializable directed connection.
54#[derive(Clone, Debug, PartialEq, Eq)]
55#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
56#[cfg_attr(feature = "schema", derive(JsonSchema))]
57pub struct ThreadDef {
58    /// Source port (must be an output on the referenced knot).
59    pub from: PortRefDef,
60    /// Destination port (must be an input on the referenced knot).
61    pub to: PortRefDef,
62}
63
64/// Editable and serializable graph definition. Convert it to [`Weave`] before execution.
65#[derive(Clone, Debug, PartialEq)]
66#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
67#[cfg_attr(feature = "schema", derive(JsonSchema))]
68pub struct WeaveDef {
69    /// Author weave id (also mixed into `Random` PRNG seed at bind).
70    pub id: String,
71    /// Declared numeric path; must match the compiled feature at validate.
72    pub numeric: NumericPath,
73    /// Knot definitions in author insertion order.
74    pub knots: Vec<KnotDef>,
75    /// Directed connections between catalog ports.
76    pub threads: Vec<ThreadDef>,
77}
78
79/// Immutable, structurally validated graph ready for runtime bind.
80#[derive(Clone, Debug, PartialEq)]
81pub struct Weave {
82    id: String,
83    numeric: NumericPath,
84    knots: Vec<KnotDef>,
85    threads: Vec<ThreadDef>,
86}
87
88impl Weave {
89    /// Start a [`crate::WeaveBuilder`] for this weave id.
90    pub fn builder(id: impl Into<String>) -> Result<crate::WeaveBuilder, crate::BuildError> {
91        crate::WeaveBuilder::new(id)
92    }
93
94    /// Compose a generated graph through a scoped typed [`crate::Composer`].
95    ///
96    /// Common helpers use [`crate::BoolWire`], [`crate::LevelWire`], and
97    /// [`crate::CountWire`] so fixed-domain mistakes cannot be expressed. The
98    /// closure may use `knot`/`input`/`output`/`thread` for any catalog entry;
99    /// all lowering and final validation remains owned by `WeaveBuilder`.
100    pub fn compose(
101        id: impl Into<String>,
102        compose: impl FnOnce(&mut crate::Composer) -> Result<(), crate::BuildError>,
103    ) -> Result<Self, crate::ComposeError> {
104        let mut composer = crate::Composer::new(id)?;
105        compose(&mut composer)?;
106        Ok(composer.build()?)
107    }
108
109    /// Author weave id (also mixed into Random PRNG seed at bind).
110    pub fn id(&self) -> &str {
111        &self.id
112    }
113
114    /// Declared numeric path (must match the compiled feature).
115    pub fn numeric(&self) -> NumericPath {
116        self.numeric
117    }
118
119    /// Ordered knot definitions (dense runtime indices follow this order at bind).
120    pub fn knots(&self) -> &[KnotDef] {
121        &self.knots
122    }
123
124    /// Directed threads in author order.
125    pub fn threads(&self) -> &[ThreadDef] {
126        &self.threads
127    }
128
129    /// Clone into the serializable definition form.
130    pub fn to_def(&self) -> WeaveDef {
131        WeaveDef {
132            id: self.id.clone(),
133            numeric: self.numeric,
134            knots: self.knots.clone(),
135            threads: self.threads.clone(),
136        }
137    }
138
139    pub(crate) fn from_validated(def: WeaveDef) -> Self {
140        Self {
141            id: def.id,
142            numeric: def.numeric,
143            knots: def.knots,
144            threads: def.threads,
145        }
146    }
147}
148
149impl TryFrom<WeaveDef> for Weave {
150    type Error = ValidationError;
151
152    fn try_from(def: WeaveDef) -> Result<Self, Self::Error> {
153        crate::authoring::validate::validate_def(&def)?;
154        Ok(Self::from_validated(def))
155    }
156}
157
158impl From<Weave> for WeaveDef {
159    fn from(weave: Weave) -> Self {
160        Self {
161            id: weave.id,
162            numeric: weave.numeric,
163            knots: weave.knots,
164            threads: weave.threads,
165        }
166    }
167}