trace.rsannotatedtrace.rssource196 lines · 7.2 KB · raw
1use serde::Serialize;
2
3use crate::{Domain, Known, Network, Next, Then};
4
5/// The sources of a host's facts, as callbacks: [`Network::run`] calls them,
6/// and they are the only place a fact is supplied from.
7pub trait Host<D: Domain> {
8    /// One request for every fact the network wants from outside it. Returns
9    /// what it taught; a fact left out was not supplied.
10    fn ask(&mut self, facts: &[D::Fact]) -> Vec<(D::Fact, D::Value)>;
11    /// Does an effect, and returns what it taught. It must teach
12    /// [`Domain::teaches`] its effect, or the run stops.
13    fn perform(&mut self, effect: D::Effect) -> Vec<(D::Fact, D::Value)>;
14}
15
16/// How a run ended.
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub enum Outcome<D: Domain> {
19    /// No rule has anything left to do: what to show is in the notes of the
20    /// rules that hold.
21    Done,
22    /// A rule ended the query outright.
23    Ended(D::End),
24    /// The host did not supply these facts, so the run could not go on.
25    Stuck(Vec<D::Fact>),
26}
27
28/// A rule that decided a step, and what it stood on.
29#[derive(Clone, Debug, PartialEq, Eq)]
30pub struct Firing<D: Domain> {
31    /// Index into [`Network::terminals`].
32    pub rule: usize,
33    pub name: String,
34    pub then: Then<D>,
35    /// The facts the rule's tests are on, with what each was: not what any of
36    /// them was about.
37    pub on: Vec<(D::Fact, D::Value)>,
38}
39
40/// One thing that happened in a run.
41#[derive(Clone, Debug, PartialEq, Eq)]
42pub enum Event<D: Domain> {
43    /// These facts were asked for in one request.
44    Asked(Vec<D::Fact>),
45    /// A rule decided the next step.
46    Fired(Firing<D>),
47    /// A source taught these.
48    Learned(Vec<(D::Fact, D::Value)>),
49    Stopped(Outcome<D>),
50}
51
52/// Every event of a run, in order.
53#[derive(Clone, Debug, PartialEq, Eq)]
54pub struct Trace<D: Domain> {
55    pub events: Vec<Event<D>>,
56}
57
58impl<D: Domain> Default for Trace<D> {
59    fn default() -> Self {
60        Trace { events: Vec::new() }
61    }
62}
63
64/// An event as data to log: names, ids and counts, never what a fact was
65/// about. `step` is the event's place in its trace, from 0.
66#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
67pub struct Record {
68    pub step: usize,
69    /// `asked`, `fired`, `learned`, `done`, `ended` or `stuck`.
70    pub event: &'static str,
71    /// For `fired`: the rule's index and name.
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub rule: Option<usize>,
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub name: Option<String>,
76    /// For `fired`: the kind (`do`, `end`, `note`) and name of what it does,
77    /// and for `ended` the end's name.
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub then: Option<String>,
80    /// The facts the event is about: asked for, stood on, learned or missing.
81    pub facts: Vec<String>,
82    /// For `fired` and `learned`: the value of each fact, in the same order.
83    pub values: Vec<String>,
84    pub count: usize,
85}
86
87fn named<D: Domain>(pairs: &[(D::Fact, D::Value)]) -> (Vec<String>, Vec<String>) {
88    (
89        pairs.iter().map(|(fact, _)| D::fact_name(*fact).to_owned()).collect(),
90        pairs.iter().map(|(_, value)| D::value_name(*value).to_owned()).collect(),
91    )
92}
93
94fn fact_names<D: Domain>(facts: &[D::Fact]) -> Vec<String> {
95    facts.iter().map(|fact| D::fact_name(*fact).to_owned()).collect()
96}
97
98impl<D: Domain> Event<D> {
99    pub fn record(&self, step: usize) -> Record {
100        let blank = Record { step, event: "", rule: None, name: None, then: None, facts: Vec::new(), values: Vec::new(), count: 0 };
101        match self {
102            Event::Asked(facts) => Record { event: "asked", facts: fact_names::<D>(facts), count: facts.len(), ..blank },
103            Event::Fired(firing) => {
104                let (facts, values) = named::<D>(&firing.on);
105                let then = match firing.then {
106                    Then::Do(effect) => format!("do {}", D::effect_name(effect)),
107                    Then::End(end) => format!("end {}", D::end_name(end)),
108                    Then::Note(note) => format!("note {}", D::note_name(note)),
109                };
110                Record {
111                    event: "fired",
112                    rule: Some(firing.rule),
113                    name: Some(firing.name.clone()),
114                    then: Some(then),
115                    count: facts.len(),
116                    facts,
117                    values,
118                    ..blank
119                }
120            }
121            Event::Learned(pairs) => {
122                let (facts, values) = named::<D>(pairs);
123                Record { event: "learned", count: facts.len(), facts, values, ..blank }
124            }
125            Event::Stopped(Outcome::Done) => Record { event: "done", ..blank },
126            Event::Stopped(Outcome::Ended(end)) => Record { event: "ended", then: Some(D::end_name(*end)), ..blank },
127            Event::Stopped(Outcome::Stuck(facts)) => {
128                Record { event: "stuck", facts: fact_names::<D>(facts), count: facts.len(), ..blank }
129            }
130        }
131    }
132}
133
134impl<D: Domain> Trace<D> {
135    /// The trace as records, ready to log as JSON lines.
136    pub fn records(&self) -> Vec<Record> {
137        self.events.iter().enumerate().map(|(step, event)| event.record(step)).collect()
138    }
139
140    /// The rules that decided a step, in order, as indices into the network's
141    /// terminals: what a drawing numbers.
142    pub fn fired(&self) -> Vec<usize> {
143        self.events
144            .iter()
145            .filter_map(|event| match event {
146                Event::Fired(firing) => Some(firing.rule),
147                _ => None,
148            })
149            .collect()
150    }
151}
152
153/// What a run came to.
154#[derive(Clone, Debug)]
155pub struct Run<D: Domain> {
156    pub outcome: Outcome<D>,
157    pub known: Known<D>,
158    pub trace: Trace<D>,
159}
160
161impl<D: Domain> Network<D> {
162    /// Does what [`Network::decide`] says until the query ends, asking `host`
163    /// for facts and for effects, and recording a trace.
164    ///
165    /// Stops with [`Outcome::Stuck`] rather than looping when the host leaves
166    /// a wanted fact unsupplied.
167    pub fn run(&self, mut known: Known<D>, host: &mut impl Host<D>) -> Run<D> {
168        let mut trace = Trace::default();
169        let outcome = loop {
170            let (by, next) = self.decide(&known);
171            if let Some(rule) = by {
172                trace.events.push(Event::Fired(self.explain(rule, &known)));
173            }
174            let (wanted, taught) = match next {
175                Next::Done => break Outcome::Done,
176                Next::End(end) => break Outcome::Ended(end),
177                Next::Ask(facts) => {
178                    trace.events.push(Event::Asked(facts.clone()));
179                    let taught = host.ask(&facts);
180                    (facts, taught)
181                }
182                Next::Do(effect) => (vec![D::teaches(effect)], host.perform(effect)),
183            };
184            for (fact, value) in &taught {
185                known.learn(*fact, *value);
186            }
187            trace.events.push(Event::Learned(taught));
188            let missing: Vec<D::Fact> = wanted.into_iter().filter(|fact| known.get(*fact).is_none()).collect();
189            if !missing.is_empty() {
190                break Outcome::Stuck(missing);
191            }
192        };
193        trace.events.push(Event::Stopped(outcome.clone()));
194        Run { outcome, known, trace }
195    }
196}