Skip to main content

trellis_core/
model.rs

1use std::collections::BTreeSet;
2
3/// Supported model-test graph shape generated for oracle checks.
4#[derive(Copy, Clone, Debug, Eq, PartialEq)]
5pub enum ModelTopology {
6    /// One canonical scalar input feeding a pure derived chain.
7    ScalarChain,
8    /// A set input feeding a collection, resource planner, and output.
9    SetResourceOutput,
10}
11
12/// Deterministic mutation generated for model tests.
13#[derive(Clone, Debug, Eq, PartialEq)]
14pub enum ModelStep {
15    /// Replace the model input set with these members.
16    SetMembers(BTreeSet<u8>),
17    /// Ask the graph to rebaseline its materialized output.
18    RebaselineOutput,
19    /// Close the primary scope.
20    ClosePrimaryScope,
21}
22
23/// Generated deterministic graph shape and input sequence.
24#[derive(Clone, Debug, Eq, PartialEq)]
25pub struct ModelScript {
26    /// Graph shape selected by the generator.
27    pub topology: ModelTopology,
28    /// Ordered model mutations.
29    pub steps: Vec<ModelStep>,
30}
31
32/// Small deterministic generator for repeatable model tests.
33#[derive(Clone, Debug)]
34pub struct ModelGenerator {
35    state: u64,
36}
37
38impl ModelGenerator {
39    /// Creates a model generator from a stable seed.
40    pub const fn new(seed: u64) -> Self {
41        Self { state: seed }
42    }
43
44    /// Generates a deterministic supported model script.
45    pub fn script(&mut self, step_count: usize) -> ModelScript {
46        let topology = if self.next_u8().is_multiple_of(2) {
47            ModelTopology::ScalarChain
48        } else {
49            ModelTopology::SetResourceOutput
50        };
51        let mut steps = Vec::with_capacity(step_count);
52        let close_at = step_count.saturating_sub(1);
53        for index in 0..step_count {
54            let roll = self.next_u8();
55            let step = if index == close_at && step_count > 2 && roll.is_multiple_of(5) {
56                ModelStep::ClosePrimaryScope
57            } else if roll.is_multiple_of(7) {
58                ModelStep::RebaselineOutput
59            } else {
60                ModelStep::SetMembers(self.members())
61            };
62            steps.push(step);
63        }
64        ModelScript { topology, steps }
65    }
66
67    fn members(&mut self) -> BTreeSet<u8> {
68        let width = usize::from(self.next_u8() % 4);
69        let mut members = BTreeSet::new();
70        for _ in 0..width {
71            members.insert(self.next_u8() % 6);
72        }
73        members
74    }
75
76    fn next_u8(&mut self) -> u8 {
77        self.state = self
78            .state
79            .wrapping_mul(6_364_136_223_846_793_005)
80            .wrapping_add(1);
81        (self.state >> 32) as u8
82    }
83}