README.md
6.5 kB · markdown
BRIDGE
scripts/bridge.shrunscargo run -q -p bridgeunder the cargo lock: it parsespkgs/mrlyrs/src(notsrc/bin) withsyn, writesbridge/manifest.json, prints the skip report and the per-unit counts, then runs every backend listed inBACKENDSinmain.rs.- It exits 1 on a name collision, on a skippable written function missing from
skip.txt, or on askip.txtline naming nothing; the manifest is written first either way. model.rsis the contract:Manifest { krate, version, modules, types, consts, functions }, serde withpreserve_order; read the JSON or the types.
NAMES
- A path is the item's shortest public path after
pub use(paths, groups, renames, globs); ties go to the defining module;core::Tensor,math::two::census,Error. - A function reachable only through a private module with no re-export is
private: listed, never exported. - Every public module is listed;
itemscounts the entries homed there. A module with items may not share a path with a function; fix it inmrlyrsby re-exporting the module's leftovers. - Hand types export their methods as free functions at the type's defining module, self first:
Tensor::ofiscore::tensor::of;Cell6d::widthismath::six::width, the nearest public module. - Plain types and enums keep associated functions under the type name, self first when there is one:
gen::recipe::Tile::new,life::Boundary::wrap(boundary),life::Fate::all(). - A class keeps its methods as methods:
life::Life::step. - Two entries with one path and disjoint
dimsare one exported name that dispatches on the cell's dimension, 2 or 3; group bypath. - A public trait of the crate (
math::name::Named) adds every method it declares, required or default, to each type with animpl Named for X:X::to_jsonandX::checkedself first,X::from_jsonandX::from_urlstatic,Selfread as X, docs anddefined_atfrom the trait fn,source: trait, andtraitholding the trait's path. Trait consts (KIND,LISTS,BARE) do not cross. - A backend calls a trait method through the trait, never as an inherent method:
<mrlyrs::life::Rule as mrlyrs::math::name::Named>::from_json(text)and<Rule as Named>::to_json(&rule);use mrlyrs::math::name::Named;in scope also works. A type with trait self methods is a class. DisplayandFromStrare std traits and do not cross;named_enum!types cross as their word instead.Default, derived or animpl Default for X, adds a staticX::default,source: default: a class returns an instance, plain data its dict or object, an enum its word; the CLI door isX.default.- A const-generic type's
defaultlands on each alias that fixes N (gen::build::Config2d::default); hand types get none.
KINDS
hand: Tensor, Cell, CellNd (Cell2d, Cell3d), Cell6d, Color, Code, Rng; eachhand.rsowns their crossing per the plan's CROSSING.Rng.choice(seq)andRng.shuffle(list)are written in bothhand.rs: Rust'schoiceandshufflerun on the index list, so a seed draws what Rust draws; the generic Rust fns stay inskip.txt.class: a struct, or an enum with data, that has a self-taking method; holds the Rust value, public fields as getters and setters, methods as methods; never leaves its wasm unit. A class deriving Deserialize should get a from-data constructor in each backend.- A setter reads its value the way a parameter of that type crosses in; a field holding a borrow (
Preset::name: &'static str) stays read-only. - A field under
#[serde(skip)],skip_serializingorskip_deserializingcannot round-trip as data, so its type is aclasstoo; the field recordsserde_skip, its getter carries it, the data form (to_dict,toJSON, the CLI's JSON) drops it as serde does. plain: a struct or data enum with Serialize and Deserialize, no self methods and no skipped field; a dict, an object, JSON; no per-type code.- Plain input is strict: a missing key is an error. serde fills missing keys from
Defaultonly for a type carrying#[serde(default)], in every bridge; no type carries it today, so start fromX.default()and change keys. enum: a fieldless enum with Serialize and Deserialize; a string;namedcarries thenamed_enum!words, andall()crosses.uncrossable: anything else (Error,Result,Pen); a function touching one is skipped with the reason.- A function is
ok,skipwith a reason, orprivate. Skips: a type generic, a closure,impl Trait, a fn pointer or private type, a&'staticor explicit-lifetime return, a&mutborrow returned, an iterator, a&mutplain or slice argument, a class from another unit. - An elided borrow return (
fn shape(&self) -> &[usize]) is copied out and crosses; a&mut Rng,&mut Tensoror&mut Classargument is mutated in place. - The macro-written
name()returns&'static strand is skipped by rule, noskip.txtline: the enum is its word.skip.txtlists written functions only, one path then one clause per line. - Consts are listed with a
Tyand skip on the same rules (font::pens::UPPERSholds&'static); noskip.txtline for a const.
TY
unit,scalar(bool u8 u16 u32 u64 usize i8 i16 i32 i64 f32 f64 char),str,string,u128,i128,code,json(serde_json Value or Map).vec,slice(&[T],mutable),option,tuple,array(len),ref(mutable,lifetime),result(the crate'sResult<T>),map(HashMap or BTreeMap, scalar or string keys),set(BTreeSet, a sorted list).hand{name, dim},plain{path},enum{path},class{path, dim},opaque{path, an uncrossable public type},unknown{text, forces skip}.u128,i128andcodeare a Python int and a decimal string in JS and the CLI;charis a one-character string.
PLUG IN
- Write
bridge/src/<py|js|cli>.rswithpub fn write(manifest: &Manifest, root: &Path) -> Result<()>(model::Result, aStringerror); addmod x;and("x", x::write)toBACKENDSinmain.rs. rootis the workspace root; write generated files underpkgs/and never hand-edit them; a wrong line is a generator fix.units.txtnames the wasm units, one per line; a unit is the first segment of a path;allis every unit.docsare the///lines for docstrings;defined_atisfile:lineunderpkgs/mrlyrs/src;derivesandserdeattributes are recorded verbatim.cargo test -p bridge: one test per rule insrc/tests.rs, a determinism test, and the count test: entries =pub fngrep lines - lines insidemacro_rules+pub const fn+ 2 per named enum + trait fns ximpl Namedblocks + 1 perdefault.