Skip to main content

ftml_solver_trace/
lib.rs

1pub mod results;
2
3use ftml_ontology::terms::{ComponentVar, Term, Variable};
4use ftml_uris::{FtmlUri, Uri, UriRef};
5#[cfg(feature = "colors")]
6use owo_colors::OwoColorize;
7use std::borrow::Cow;
8use std::{fmt::Write, marker::PhantomData};
9
10#[cfg(feature = "full")]
11pub trait CheckerRule: std::fmt::Debug + Send + Sync + std::any::Any {
12    fn priority(&self) -> isize {
13        0
14    }
15    fn display(&self) -> Vec<Displayable>;
16    fn as_box_dyn(&self) -> Box<dyn CheckerRule>;
17    fn as_dyn(&self) -> &dyn CheckerRule;
18    fn as_any(&self) -> &dyn std::any::Any;
19    fn eq(&self, o: &dyn CheckerRule) -> bool;
20}
21
22#[cfg(feature = "full")]
23pub trait SizedSolverRule:
24    std::fmt::Debug + Send + Sync + std::any::Any + Clone + Sized + PartialEq + Eq
25{
26    fn priority(&self) -> isize {
27        0
28    }
29    fn display(&self) -> Vec<Displayable>;
30}
31
32#[cfg(feature = "full")]
33impl<T: SizedSolverRule> CheckerRule for T {
34    #[allow(clippy::inline_always)]
35    #[inline(always)]
36    fn priority(&self) -> isize {
37        <Self as SizedSolverRule>::priority(self)
38    }
39
40    #[inline]
41    fn display(
42        &self,
43        //f: &mut std::fmt::Formatter,
44    ) -> Vec<Displayable> {
45        <T as SizedSolverRule>::display(self)
46    }
47
48    fn as_box_dyn(&self) -> Box<dyn CheckerRule> {
49        Box::new(self.clone()) as _
50    }
51    fn as_dyn(&self) -> &dyn CheckerRule {
52        self as _
53    }
54    fn as_any(&self) -> &dyn std::any::Any {
55        self as _
56    }
57    fn eq(&self, o: &dyn CheckerRule) -> bool {
58        o.as_any().downcast_ref::<T>().is_some_and(|v| v == self)
59    }
60}
61
62#[cfg(feature = "full")]
63#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
64pub enum MessageLevel {
65    Failure,
66    Comment,
67    Header,
68    Emph,
69}
70
71#[cfg(feature = "full")]
72#[derive(Clone, Copy, Default)]
73pub struct Indent(pub usize);
74#[cfg(feature = "full")]
75impl Indent {
76    pub const fn increase(&mut self) {
77        self.0 += 1;
78    }
79    pub const fn decrease(&mut self) {
80        self.0 = self.0.saturating_sub(1);
81    }
82}
83#[cfg(feature = "full")]
84impl std::fmt::Display for Indent {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        if self.0 == 0 {
87            return Ok(());
88        }
89        for _ in 0..self.0 - 1 {
90            f.write_str("  │")?;
91        }
92        f.write_str("  ├─")?;
93        Ok(())
94    }
95}
96
97#[cfg(feature = "full")]
98#[derive(Debug)]
99pub enum CheckLogCow<'t> {
100    Owned(PreCheckLog),
101    Borrowed(RefCheckLog<'t>),
102}
103#[cfg(feature = "full")]
104impl<'t> From<RefCheckLog<'t>> for CheckLogCow<'t> {
105    #[inline]
106    fn from(value: RefCheckLog<'t>) -> Self {
107        Self::Borrowed(value)
108    }
109}
110#[cfg(feature = "full")]
111impl From<PreCheckLog> for CheckLogCow<'_> {
112    #[inline]
113    fn from(value: PreCheckLog) -> Self {
114        Self::Owned(value)
115    }
116}
117
118macro_rules! tasks {
119    (
120        $(
121            $name:ident($($field:ident : $tp:ident),*) => $res:tt
122        ),* $(,)?
123    ) => {
124
125        #[cfg(feature = "full")]
126        #[derive(Debug)]
127        pub enum RefCheckLog<'t> {
128            $(
129                $name {
130                    $($field: tasks!(@TPBORROW $tp),)*
131                    steps:Box<[CheckLogCow<'t>]>,
132                    context: Box<[Cow<'t, ComponentVar>]>,
133                    result: Option<$res>,
134                },
135            )*
136            Rule{
137                rule: &'t dyn CheckerRule,
138                steps:Box<[CheckLogCow<'t>]>,
139            },
140            Strategy{
141                name: &'static str,
142                steps:Box<[CheckLogCow<'t>]>,
143                success:bool
144            },
145            Msg(Vec<DisplayableRef<'t>>, MessageLevel),
146        }
147        #[cfg(feature = "full")]
148        impl RefCheckLog<'_> {
149            pub fn into_owned(self,term:&impl Fn(Term) -> Term) -> PreCheckLog {
150                match self {
151                    $(
152                        Self::$name{$($field,)* steps,context,result} => PreCheckLog::$name{
153                            $($field:tasks!(@CONV $tp $field term),)*
154                            steps: steps.into_iter().map(|t| CheckLogCow::into_owned(t,term)).collect(),
155                            context: context.into_iter().map(Cow::into_owned).collect(),
156                            result,
157
158                        },
159                    )*
160                    Self::Msg(txt,lvl) => PreCheckLog::Msg(txt.into_iter().map(|s| s.into_owned(term)).collect(),lvl),
161                    Self::Rule{rule,steps} => PreCheckLog::Rule{
162                        rule:rule.as_box_dyn(),
163                        steps: steps.into_iter().map(|t| CheckLogCow::into_owned(t,term)).collect(),
164                    },
165                    Self::Strategy{name,steps,success} => PreCheckLog::Strategy{
166                        name,
167                        steps: steps.into_iter().map(|t| CheckLogCow::into_owned(t,term)).collect(),
168                        success
169                    }
170                }
171            }
172        }
173        #[cfg(feature = "full")]
174        #[derive(Debug)]
175        pub enum PreCheckLog {
176            $(
177                $name {
178                    $($field: tasks!(@TPOWN $tp),)*
179                    steps:Vec<Self>,
180                    context:Box<[ComponentVar]>,
181                    result:Option<$res>
182                },
183            )*
184            Rule{
185                rule:Box<dyn CheckerRule>,
186                steps:Vec<Self>,
187            },
188            Strategy{
189                name: &'static str,
190                steps:Vec<Self>,
191                success:bool
192            },
193            //Dyn(Box<dyn CheckTraceDisplayable>)
194            Msg(Vec<Displayable>, MessageLevel),
195            //Count(&'static str,usize)
196            //Interpolated(Box<[DisplayableElem]>, MessageLevel),
197        }
198
199        #[cfg(feature = "full")]
200        impl CheckLog {
201            pub fn from_pre(v:PreCheckLog,terms:&mut impl FnMut(Term) -> Term) -> Self {
202                use PreCheckLog as P;
203                match v {
204                    $(
205                        P::$name{
206                            $($field,)*steps,context,result
207                        } => Self::$name{
208                            $( $field:tasks!(@FROMPRE $tp $field terms),)*
209                            context,result:result.map(|r| tasks!(@FROMPRE $res r terms)),
210                            steps:steps.into_iter().map(|e| Self::from_pre(e,terms)).collect()
211                        },
212                    )*
213                    P::Rule{ rule, steps } => Self::Rule {
214                        header:Displayable::map(rule.display(),terms),
215                        steps:steps.into_iter().map(|e| Self::from_pre(e,terms)).collect()
216                    },
217                    P::Strategy{ name, steps, success } => Self::Strategy {
218                        name:name.to_string(),
219                        steps:steps.into_iter().map(|e| Self::from_pre(e,terms)).collect(),
220                        success
221                    },
222                    P::Msg(s, MessageLevel::Comment) => Self::Comment(s),
223                    P::Msg(s, MessageLevel::Emph) => Self::Emph(s),
224                    P::Msg(s, MessageLevel::Header) => Self::Header(s),
225                    P::Msg(s, MessageLevel::Failure) => Self::Fail(s),
226                    //P::Count(s, u) =>
227                    //    Self::Comment(format!("{s} {u}"))
228                }
229            }
230        }
231        #[cfg(feature = "full")]
232        impl<'t> CheckingTask<'t> {
233            pub fn close<R:Clone>(self,res:Option<&R>,steps:Box<[CheckLogCow<'t>]>,context:&[Cow<'t,ComponentVar>]) -> RefCheckLog<'t> {
234                let context = context.iter().map(Cow::clone).collect();
235                match self {
236                    $(
237                        Self::$name( $($field),* ) => RefCheckLog::$name {
238                            $($field,)*
239                            steps,
240                            context,
241                            result: res.map(|r| unsafe{&*std::ptr::from_ref(r).cast::<$res>()}.clone() )
242                        },
243                    )*
244                    Self::Strategy(name) => RefCheckLog::Strategy {
245                        name,
246                        steps,success:res.is_some_and(|v| {
247                            if std::mem::size_of::<R>() == 1 {
248                                // SAFETY: => boolean
249                                unsafe{
250                                    std::mem::transmute_copy::<R,bool>(v)
251                                }
252                            } else {
253                                true
254                            }
255                        })
256                    },
257                    Self::Rule(rule) => RefCheckLog::Rule { rule, steps }
258                }
259            }
260        }
261        #[cfg(feature = "full")]
262        impl<'t> CheckLogCow<'t> {
263            pub fn into_owned(self,term:&impl Fn(Term) -> Term) -> PreCheckLog {
264                match self {
265                    Self::Owned(o) => o,
266                    Self::Borrowed(b) => b.into_owned(term)
267                }
268            }
269        }
270
271        #[cfg(feature = "full")]
272        #[derive(Copy,Clone,Debug)]
273        pub enum CheckingTask<'t> {
274            $(
275                $name($(tasks!(@TPBORROW $tp)),*)
276            ),*,
277            Rule(&'t dyn CheckerRule),
278            Strategy(&'static str),
279        }
280
281        #[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
282        pub enum CheckLog {
283            $(
284                $name {
285                    $($field: tasks!(@TPOWN $tp),)*
286                    steps:Vec<Self>,
287                    context:Box<[ComponentVar]>,
288                    result:Option<$res>
289                },
290            )*
291            Comment(Vec<Displayable>),
292            Emph(Vec<Displayable>),
293            Header(Vec<Displayable>),
294            Fail(Vec<Displayable>),
295            Strategy {
296                name: String,
297                steps: Vec<Self>,
298                success: bool,
299            },
300            Rule {
301                header: Vec<Displayable>,
302                steps: Vec<Self>,
303            },
304        }
305        #[cfg(feature = "full")]
306        impl CheckLog {
307            pub(crate) fn display_i(&self,displayer:&mut impl TraceDisplay) -> std::fmt::Result {
308                let mut curr = std::slice::from_ref(self).iter();
309                let mut stack = Vec::new();
310                let mut indent = Indent::default();
311                loop {
312                    while let Some(next) = curr.next() {
313                        if displayer.line(next,indent)? == std::ops::ControlFlow::Continue(()) {
314                            match next {
315                                $(
316                                    Self::$name{ $($field,)* steps, context,result } => {
317                                        displayer.task(CheckingTask::$name($($field),*),context,result.is_some())?;
318                                        tasks!(@DISPL result displayer $res);
319                                        indent.increase();
320                                        stack.push(std::mem::replace(&mut curr,steps.iter()));
321                                    }
322                                )*
323                                Self::Rule{header,steps} => {
324                                    for e in header {
325                                        displayer.displayable(e,None)?;
326                                    }
327                                    indent.increase();
328                                    stack.push(std::mem::replace(&mut curr,steps.iter()));
329                                }
330                                Self::Strategy{name,steps,success} => {
331                                    displayer.strategy(name,&[],*success)?;
332                                    indent.increase();
333                                    stack.push(std::mem::replace(&mut curr,steps.iter()));
334                                }
335                                Self::Comment(s) => {
336                                    for s in s {
337                                        displayer.displayable(s,Some(MessageLevel::Comment))?;
338                                    }
339                                }
340                                Self::Emph(s) => {
341                                    for s in s {
342                                        displayer.displayable(s,Some(MessageLevel::Emph))?;
343                                    }
344                                }
345                                Self::Header(s) => {
346                                    for s in s {
347                                        displayer.displayable(s,Some(MessageLevel::Header))?;
348                                    }
349                                }
350                                Self::Fail(s) => {
351                                    for s in s {
352                                        displayer.displayable(s,Some(MessageLevel::Failure))?;
353                                    }
354                                }
355                            }
356                        }
357                    }
358                    if let Some(next) = stack.pop() {
359                        indent.decrease();
360                        curr = next;
361                    } else {
362                        break
363                    }
364                }
365                Ok(())
366            }
367        }
368
369    };
370    (@DISPL $res:ident $disp:ident Term) => {
371        if let Some(t) = $res {
372            $disp.string(": ",None)?;
373            $disp.term(t,None)?;
374        }
375    };
376    (@DISPL $res:ident $disp:ident bool) => {};
377    (@TPBORROW Term) => {&'t Term};
378    (@TPOWN Term) => {Term};
379    (@TPBORROW str) => {&'t str};
380    (@TPOWN str) => {Box<str>};
381    (@FROMPRE Term $name:ident $f:ident) => {$f($name)};
382    (@FROMPRE str $name:ident $f:ident) => {$name};
383    (@FROMPRE bool $name:ident $f:ident) => {$name};
384    (@CONV Term $name:ident $f:ident) => {$name.clone()};//{ $f($name.clone()) };
385    (@CONV str $name:ident $f:ident) => { $name.to_string().into_boxed_str() };
386    //(@TPBORROW SolverRule) => {&'t dyn SolverRule};
387    //(@TPOWN SolverRule) => {Box<dyn SolverRule>};
388    (@CONV SolverRule $name:ident $f:ident) => { $name.as_box_dyn() };
389}
390
391#[cfg(feature = "full")]
392impl PreCheckLog {
393    pub fn push(&mut self, msg: Self) {
394        if let Some(steps) = self.steps_mut() {
395            steps.push(msg);
396        }
397    }
398    pub const fn steps_mut(&mut self) -> Option<&mut Vec<Self>> {
399        match self {
400            Self::Equality { steps, .. }
401            | Self::HasType { steps, .. }
402            | Self::Inference { steps, .. }
403            | Self::Inhabitable { steps, .. }
404            | Self::Rule { steps, .. }
405            | Self::Simplify { steps, .. }
406            | Self::Strategy { steps, .. }
407            | Self::Subtype { steps, .. }
408            | Self::Universe { steps, .. }
409            | Self::VariableInference { steps, .. }
410            | Self::Proving { steps, .. } => Some(steps),
411            Self::Msg(_, _) /*| Self::Count(_, _)*/ => None,
412        }
413    }
414}
415
416impl CheckLog {
417    pub fn filter_failures(&mut self, preserve_siblings: bool) {
418        let Some(steps) = self.steps_mut() else {
419            return;
420        };
421        if preserve_siblings {
422            for s in steps {
423                if s.success() {
424                    s.filter_failures(false);
425                } else {
426                    s.filter_failures(true);
427                }
428            }
429        } else {
430            *steps = std::mem::take(steps)
431                .into_iter()
432                .filter(|s| !s.success())
433                .map(|mut s| {
434                    s.filter_failures(preserve_siblings);
435                    s
436                })
437                .collect();
438        }
439    }
440    pub const fn steps_mut(&mut self) -> Option<&mut Vec<Self>> {
441        match self {
442            Self::Equality { steps, .. }
443            | Self::HasType { steps, .. }
444            | Self::Inference { steps, .. }
445            | Self::Inhabitable { steps, .. }
446            | Self::Rule { steps, .. }
447            | Self::Simplify { steps, .. }
448            | Self::Strategy { steps, .. }
449            | Self::Subtype { steps, .. }
450            | Self::Universe { steps, .. }
451            | Self::VariableInference { steps, .. }
452            | Self::Proving { steps, .. } => Some(steps),
453            Self::Comment(_) | Self::Emph(_) | Self::Header(_) | Self::Fail(_) => None,
454        }
455    }
456    pub fn success(&self) -> bool {
457        match self {
458            Self::Equality { result, .. }
459            | Self::HasType { result, .. }
460            | Self::Inhabitable { result, .. }
461            | Self::Subtype { result, .. }
462            | Self::Universe { result, .. } => *result == Some(true),
463            Self::Rule { steps, .. } => steps.iter().all(Self::success),
464            Self::Inference { result, .. }
465            | Self::VariableInference { result, .. }
466            | Self::Simplify { result, .. }
467            | Self::Proving { result, .. } => result.is_some(),
468            Self::Strategy { success, .. } => *success,
469            Self::Comment(_) | Self::Header(_) | Self::Emph(_) | Self::Fail(_) => false,
470        }
471    }
472    pub fn add_failure(&mut self, s: &'static str) {
473        if let Some(steps) = self.steps_mut() {
474            steps.push(Self::Fail(vec![s.to_string().into()]));
475        }
476    }
477}
478
479#[cfg(feature = "full")]
480#[derive(Debug, Clone)]
481pub enum DisplayableRef<'r> {
482    Num(i128),
483    //Space,
484    String(Cow<'static, str>),
485    Term(Cow<'r, Term>),
486    Uri(either::Either<UriRef<'r>, Uri>),
487    Var(Cow<'r, Variable>),
488}
489#[cfg(feature = "full")]
490impl DisplayableRef<'_> {
491    pub fn into_owned(self, term: &impl Fn(Term) -> Term) -> Displayable {
492        match self {
493            Self::Num(i) => Displayable::Num(i),
494            Self::String(s) => Displayable::String(s.to_string()),
495            Self::Term(t) => Displayable::Term(term(t.into_owned())),
496            Self::Uri(u) => Displayable::Uri(match u {
497                either::Left(u) => u.owned(),
498                either::Right(u) => u,
499            }),
500            Self::Var(v) => Displayable::Var(v.into_owned()),
501        }
502    }
503}
504
505#[allow(clippy::large_enum_variant)]
506#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
507pub enum Displayable {
508    //Log(CheckLog),
509    Num(i128),
510    //Space,
511    String(String),
512    Term(Term),
513    Uri(Uri),
514    Var(Variable),
515}
516impl Displayable {
517    fn map(v: Vec<Self>, terms: &mut impl FnMut(Term) -> Term) -> Vec<Self> {
518        v.into_iter()
519            .map(|e| {
520                if let Self::Term(t) = e {
521                    Self::Term(terms(t))
522                } else {
523                    e
524                }
525            })
526            .collect()
527    }
528}
529
530tasks! {
531    Simplify(term:Term) => Term,
532    Proving(term:Term) => Term,
533    Inference(term: Term) => Term,
534    VariableInference(var: str) => Term,
535    //Simplify(term:Term) => Term,
536    Inhabitable(term: Term) => bool,
537    Universe(term:Term) => bool,
538    Subtype(sub:Term,sup:Term) => bool,
539    HasType(tm:Term,tp:Term) => bool,
540    Equality(lhs:Term,rhs:Term) => bool,
541}
542
543#[cfg(feature = "full")]
544impl<'b> PartialEq<CheckingTask<'b>> for CheckingTask<'_> {
545    fn eq(&self, other: &CheckingTask<'b>) -> bool {
546        match (self, other) {
547            (Self::Simplify(t), CheckingTask::Simplify(t2))
548            | (Self::Proving(t), CheckingTask::Proving(t2))
549            | (Self::Inference(t), CheckingTask::Inference(t2))
550            | (Self::Inhabitable(t), CheckingTask::Inhabitable(t2))
551            | (Self::Universe(t), CheckingTask::Universe(t2)) => t.alpha_equal(t2),
552            (Self::VariableInference(v), CheckingTask::VariableInference(v2)) => v == v2,
553            (Self::Subtype(a, b), CheckingTask::Subtype(a2, b2))
554            | (Self::HasType(a, b), CheckingTask::HasType(a2, b2))
555            | (Self::Equality(a, b), CheckingTask::Equality(a2, b2)) => {
556                a.alpha_equal(a2) && b.alpha_equal(b2)
557            }
558            _ => false,
559        }
560    }
561}
562
563#[cfg(feature = "full")]
564impl CheckLog {
565    #[must_use]
566    pub fn display<D: FmtTraceDisplay>(&self) -> impl std::fmt::Display + use<'_, D> {
567        TraceDisplayer::<'_, D> {
568            trace: self,
569            d: PhantomData,
570        }
571    }
572    #[cfg(feature = "colors")]
573    #[must_use]
574    pub fn colored(&self) -> impl std::fmt::Display {
575        TraceDisplayer {
576            d: PhantomData::<ColorDisplay>,
577            trace: self,
578        }
579    }
580}
581#[cfg(feature = "full")]
582impl std::fmt::Display for CheckLog {
583    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
584        self.display::<()>().fmt(f)
585    }
586}
587
588#[cfg(feature = "full")]
589pub trait FmtTraceDisplay {
590    fn new(f: &mut std::fmt::Formatter<'_>) -> impl TraceDisplay;
591}
592
593#[cfg(feature = "full")]
594pub trait TraceDisplay {
595    // /// ### Errors
596    //fn rule(&mut self, rule: &dyn CheckerRule) -> std::fmt::Result;
597
598    /// ### Errors
599    fn displayable(&mut self, d: &Displayable, lvl: Option<MessageLevel>) -> std::fmt::Result {
600        match d {
601            Displayable::Num(i) => self.num(*i, lvl),
602            //Displayable::Space => self.space(),
603            Displayable::String(s) => self.string(s, lvl),
604            Displayable::Term(t) => self.term(t, lvl),
605            Displayable::Uri(u) => self.uri(u.as_uri(), lvl),
606            Displayable::Var(v) => self.variable(v, lvl),
607        }
608    }
609
610    /// ### Errors
611    fn line(
612        &mut self,
613        _: &CheckLog,
614        indent: Indent,
615    ) -> Result<std::ops::ControlFlow<()>, std::fmt::Error>; /* {
616    f.write_char('\n')?;
617    self.indent(indent, None, f)?;
618    Ok(std::ops::ControlFlow::Continue(()))
619    }*/
620
621    /// ### Errors
622    fn task(
623        &mut self,
624        task: CheckingTask<'_>,
625        context: &[ComponentVar],
626        success: bool,
627    ) -> std::fmt::Result;
628
629    /// ### Errors
630    fn strategy(&mut self, name: &str, context: &[ComponentVar], success: bool)
631    -> std::fmt::Result;
632
633    /// ### Errors
634    fn uri(&mut self, uri: ftml_uris::UriRef, lvl: Option<MessageLevel>) -> std::fmt::Result;
635
636    /// ### Errors
637    fn term(&mut self, term: &Term, lvl: Option<MessageLevel>) -> std::fmt::Result;
638
639    /// ### Errors
640    fn string(&mut self, s: &str, lvl: Option<MessageLevel>) -> std::fmt::Result;
641
642    /// ### Errors
643    fn variable(&mut self, var: &Variable, lvl: Option<MessageLevel>) -> std::fmt::Result;
644
645    /// ### Errors
646    fn num(&mut self, num: i128, lvl: Option<MessageLevel>) -> std::fmt::Result;
647
648    /// ### Errors
649    fn indent(&mut self, indent: Indent, lvl: Option<MessageLevel>) -> std::fmt::Result;
650
651    /// ### Errors
652    fn space(&mut self) -> std::fmt::Result;
653}
654#[cfg(feature = "full")]
655impl FmtTraceDisplay for () {
656    #[allow(clippy::inline_always)]
657    #[inline(always)]
658    fn new(f: &mut std::fmt::Formatter<'_>) -> impl TraceDisplay {
659        f
660    }
661}
662#[cfg(feature = "full")]
663impl TraceDisplay for &mut std::fmt::Formatter<'_> {
664    fn line(
665        &mut self,
666        _: &CheckLog,
667        indent: Indent,
668        //f: &mut std::fmt::Formatter<'_>,
669    ) -> Result<std::ops::ControlFlow<()>, std::fmt::Error> {
670        self.write_char('\n')?;
671        self.indent(indent, None)?;
672        Ok(std::ops::ControlFlow::Continue(()))
673    }
674    fn space(&mut self) -> std::fmt::Result {
675        self.write_char(' ')
676    }
677    /*
678    fn rule(&mut self, rule: &dyn CheckerRule) -> std::fmt::Result {
679        self.write_str("Using rule: ")?;
680        rule.display(self, None)
681    }
682     */
683
684    fn strategy(&mut self, name: &str, _: &[ComponentVar], _: bool) -> std::fmt::Result {
685        write!(self, "Strategy: {name}")
686    }
687    fn task(
688        &mut self,
689        task: CheckingTask<'_>,
690        context: &[ComponentVar],
691        success: bool,
692        //f: &mut std::fmt::Formatter<'_>,
693    ) -> std::fmt::Result {
694        fn do_context(
695            context: &[ComponentVar],
696            mut f: &mut std::fmt::Formatter<'_>,
697        ) -> std::fmt::Result {
698            if context.is_empty() {
699                return Ok(());
700            }
701            f.write_str("{... ")?;
702            let mut first = true;
703            for ComponentVar { var, tp, df } in context {
704                if first {
705                    first = false;
706                } else {
707                    f.write_str(", ")?;
708                }
709                f.variable(var, None)?;
710                if let Some(tp) = tp {
711                    f.write_str(" : ")?;
712                    f.term(tp, None)?;
713                }
714                if let Some(df) = df {
715                    f.write_str(" : ")?;
716                    f.term(df, None)?;
717                }
718            }
719            f.write_str(" } ")
720        }
721        if success {
722            self.write_str("[SUCCESS] ")?;
723        } else {
724            self.write_str("[FAILED] ")?;
725        }
726        match task {
727            CheckingTask::Simplify(t) => {
728                self.write_str("Simplifying ")?;
729                do_context(context, self)?;
730                self.term(t, None)
731            }
732            CheckingTask::Proving(t) => {
733                self.write_str("Proving ")?;
734                do_context(context, self)?;
735                self.term(t, None)
736            }
737            CheckingTask::Inference(t) => {
738                self.write_str("Inferring type of ")?;
739                do_context(context, self)?;
740                self.term(t, None)
741            }
742            CheckingTask::VariableInference(t) => {
743                self.write_str("Inferring type of variable ")?;
744                do_context(context, self)?;
745                self.write_str(t)
746            }
747            CheckingTask::Inhabitable(tm) => {
748                self.write_str("Checking inhabitability ")?;
749                do_context(context, self)?;
750                self.write_str("⊢ INH ")?;
751                self.term(tm, None)
752            }
753            CheckingTask::Universe(tm) => {
754                self.write_str("Checking universe ")?;
755                do_context(context, self)?;
756                self.write_str("⊢ UNIV ")?;
757                self.term(tm, None)
758            }
759            CheckingTask::Subtype(sub, sup) => {
760                self.write_str("Checking subtyping ")?;
761                do_context(context, self)?;
762                self.write_str("⊢ ")?;
763                self.term(sub, None)?;
764                self.write_str(" <: ")?;
765                self.term(sup, None)
766            }
767            CheckingTask::HasType(tm, tp) => {
768                self.write_str("Checking typing ")?;
769                do_context(context, self)?;
770                self.write_str("⊢ ")?;
771                self.term(tm, None)?;
772                self.write_str(" : ")?;
773                self.term(tp, None)
774            }
775            CheckingTask::Equality(lhs, rhs) => {
776                self.write_str("Checking equality ")?;
777                do_context(context, self)?;
778                self.write_str("⊢ ")?;
779                self.term(lhs, None)?;
780                self.write_str(" == ")?;
781                self.term(rhs, None)
782            }
783            CheckingTask::Rule(rl) => {
784                for e in rl.display() {
785                    self.displayable(&e, None)?;
786                }
787                Ok(())
788            }
789            CheckingTask::Strategy(s) => self.strategy(s, context, success),
790        }
791    }
792    fn uri(&mut self, uri: ftml_uris::UriRef, _: Option<MessageLevel>) -> std::fmt::Result {
793        match uri {
794            ftml_uris::UriRef::Symbol(s) => {
795                std::fmt::Display::fmt(&s.module.name, self)?;
796                self.write_char('?')?;
797                std::fmt::Display::fmt(&s.name, self)
798            }
799            _ => std::fmt::Display::fmt(&uri, self),
800        }
801    }
802    fn indent(&mut self, indent: Indent, _: Option<MessageLevel>) -> std::fmt::Result {
803        write!(self, "{indent} ")
804    }
805    fn term(&mut self, term: &Term, _: Option<MessageLevel>) -> std::fmt::Result {
806        <_ as std::fmt::Debug>::fmt(&term.debug_short(), self)
807    }
808    fn string(&mut self, s: &str, lvl: Option<MessageLevel>) -> std::fmt::Result {
809        if lvl == Some(MessageLevel::Failure) {
810            self.write_str("[FAILED] ")?;
811        }
812        self.write_str(s)
813    }
814    fn variable(&mut self, var: &Variable, _: Option<MessageLevel>) -> std::fmt::Result {
815        self.write_str(var.name())
816    }
817    fn num(&mut self, num: i128, _: Option<MessageLevel>) -> std::fmt::Result {
818        <i128 as std::fmt::Display>::fmt(&num, self)
819    }
820}
821
822#[cfg(feature = "colors")]
823pub struct ColorDisplay<'a, 'b>(&'a mut std::fmt::Formatter<'b>);
824#[cfg(feature = "colors")]
825impl FmtTraceDisplay for ColorDisplay<'_, '_> {
826    fn new(f: &mut std::fmt::Formatter<'_>) -> impl TraceDisplay {
827        ColorDisplay(f)
828    }
829}
830#[cfg(feature = "colors")]
831impl TraceDisplay for ColorDisplay<'_, '_> {
832    fn line(
833        &mut self,
834        _: &CheckLog,
835        indent: Indent,
836    ) -> Result<std::ops::ControlFlow<()>, std::fmt::Error> {
837        self.0.write_char('\n')?;
838        self.indent(indent, None)?;
839        Ok(std::ops::ControlFlow::Continue(()))
840    }
841
842    fn space(&mut self) -> std::fmt::Result {
843        self.0.write_char(' ')
844    }
845
846    /*
847    fn rule(&mut self, rule: &dyn CheckerRule) -> std::fmt::Result {
848        write!(self.0, "{} ", "Using rule: ".italic())?;
849        rule.display(self, None)
850    }
851     */
852
853    fn strategy(&mut self, name: &str, _: &[ComponentVar], _: bool) -> std::fmt::Result {
854        write!(self.0, "Strategy: {}", name.italic())
855    }
856
857    fn task(
858        &mut self,
859        task: CheckingTask<'_>,
860        context: &[ComponentVar],
861        success: bool,
862    ) -> std::fmt::Result {
863        fn do_context(context: &[ComponentVar], f: &mut ColorDisplay<'_, '_>) -> std::fmt::Result {
864            if context.is_empty() {
865                return Ok(());
866            }
867            f.0.write_str("{... ")?;
868            let mut first = true;
869            for ComponentVar { var, tp, df } in context {
870                if first {
871                    first = false;
872                } else {
873                    f.0.write_str(", ")?;
874                }
875                f.variable(var, None)?;
876                if let Some(tp) = tp {
877                    f.0.write_str(" : ")?;
878                    f.term(tp, None)?;
879                }
880                if let Some(df) = df {
881                    f.0.write_str(" : ")?;
882                    f.term(df, None)?;
883                }
884            }
885            f.0.write_str(" } ")
886        }
887        if success {
888            write!(self.0, "{} ", "[SUCCESS]".green())?;
889        } else {
890            write!(self.0, "{} ", "[FAILED]".red())?;
891        }
892        match task {
893            CheckingTask::Simplify(t) => {
894                write!(self.0, "{} ", "Simplifying".bright_white().bold())?;
895                do_context(context, self)?;
896                self.term(t, None)
897            }
898            CheckingTask::Proving(t) => {
899                write!(self.0, "{} ", "Proving".bright_white().bold())?;
900                do_context(context, self)?;
901                self.term(t, None)
902            }
903            CheckingTask::Inference(t) => {
904                write!(self.0, "{} ", "Inferring type of".bright_white().bold())?;
905                do_context(context, self)?;
906                self.term(t, None)
907            }
908            CheckingTask::VariableInference(t) => {
909                write!(
910                    self.0,
911                    "{} ",
912                    "Inferring type of variable".bright_white().bold()
913                )?;
914                do_context(context, self)?;
915                self.0.write_str(t)
916            }
917            CheckingTask::Inhabitable(tm) => {
918                write!(
919                    self.0,
920                    "{} ",
921                    "Checking inhabitability".bright_white().bold()
922                )?;
923                do_context(context, self)?;
924                write!(self.0, "{} ", "⊢ INH".bright_white().bold())?;
925                self.term(tm, None)
926            }
927            CheckingTask::Universe(tm) => {
928                write!(self.0, "{} ", "Checking universe".bright_white().bold())?;
929                do_context(context, self)?;
930                write!(self.0, "{} ", "⊢ UNIV".bright_white().bold())?;
931                self.term(tm, None)
932            }
933            CheckingTask::Subtype(sub, sup) => {
934                write!(self.0, "{} ", "Checking subtyping".bright_white().bold())?;
935                do_context(context, self)?;
936                write!(self.0, "{} ", "⊢".bright_white().bold())?;
937                self.term(sub, None)?;
938                write!(self.0, " {} ", "<:".bright_white().bold())?;
939                self.term(sup, None)
940            }
941            CheckingTask::HasType(tm, tp) => {
942                write!(self.0, "{} ", "Checking typing".bright_white().bold())?;
943                do_context(context, self)?;
944                write!(self.0, "{} ", "⊢".bright_white().bold())?;
945                self.term(tm, None)?;
946                write!(self.0, " {} ", ":".bright_white().bold())?;
947                self.term(tp, None)
948            }
949            CheckingTask::Equality(lhs, rhs) => {
950                write!(self.0, "{} ", "Checking equality".bright_white().bold())?;
951                do_context(context, self)?;
952                write!(self.0, "{} ", "⊢".bright_white().bold())?;
953                self.term(lhs, None)?;
954                write!(self.0, " {} ", "==".bright_white().bold())?;
955                self.term(rhs, None)
956            }
957            CheckingTask::Rule(rl) => {
958                for e in rl.display() {
959                    self.displayable(&e, None)?;
960                }
961                Ok(())
962            }
963            CheckingTask::Strategy(s) => self.strategy(s, context, success),
964        }
965    }
966    fn uri(&mut self, uri: ftml_uris::UriRef, _: Option<MessageLevel>) -> std::fmt::Result {
967        match uri {
968            ftml_uris::UriRef::Symbol(s) => {
969                std::fmt::Display::fmt(&s.module.name, self.0)?;
970                self.0.write_char('?')?;
971                std::fmt::Display::fmt(&s.name, self.0)
972            }
973            _ => std::fmt::Display::fmt(&uri, self.0),
974        }
975    }
976    fn indent(&mut self, indent: Indent, _: Option<MessageLevel>) -> std::fmt::Result {
977        write!(self.0, "{} ", indent.blue())
978    }
979    fn term(&mut self, term: &Term, _: Option<MessageLevel>) -> std::fmt::Result {
980        write!(self.0, "{:?}", term.debug_short().yellow())
981    }
982    fn string(&mut self, s: &str, lvl: Option<MessageLevel>) -> std::fmt::Result {
983        if lvl == Some(MessageLevel::Failure) {
984            write!(self.0, "{} ", "[FAILED]".red())?;
985            write!(self.0, "{}", s.red())
986        } else {
987            write!(self.0, "{}", s.bright_black())
988        }
989    }
990    fn variable(&mut self, var: &Variable, _: Option<MessageLevel>) -> std::fmt::Result {
991        self.0.write_str(var.name())
992    }
993    fn num(&mut self, num: i128, _: Option<MessageLevel>) -> std::fmt::Result {
994        <i128 as std::fmt::Display>::fmt(&num, self.0)
995    }
996}
997
998#[cfg(feature = "full")]
999struct TraceDisplayer<'d, D: FmtTraceDisplay> {
1000    trace: &'d CheckLog,
1001    d: PhantomData<D>,
1002}
1003#[cfg(feature = "full")]
1004impl<D: FmtTraceDisplay> std::fmt::Display for TraceDisplayer<'_, D> {
1005    #[inline]
1006    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1007        self.trace.display_i(&mut D::new(f))
1008    }
1009}
1010
1011#[cfg(feature = "full")]
1012impl From<i128> for DisplayableRef<'_> {
1013    fn from(value: i128) -> Self {
1014        Self::Num(value)
1015    }
1016}
1017#[cfg(feature = "full")]
1018impl From<usize> for DisplayableRef<'_> {
1019    fn from(value: usize) -> Self {
1020        Self::Num(value as _)
1021    }
1022}
1023#[cfg(feature = "full")]
1024impl From<&'static str> for DisplayableRef<'_> {
1025    fn from(value: &'static str) -> Self {
1026        Self::String(Cow::Borrowed(value))
1027    }
1028}
1029#[cfg(feature = "full")]
1030impl From<String> for DisplayableRef<'_> {
1031    fn from(value: String) -> Self {
1032        Self::String(Cow::Owned(value))
1033    }
1034}
1035#[cfg(feature = "full")]
1036impl<'r> From<&'r Term> for DisplayableRef<'r> {
1037    fn from(value: &'r Term) -> Self {
1038        Self::Term(Cow::Borrowed(value))
1039    }
1040}
1041#[cfg(feature = "full")]
1042impl From<Term> for DisplayableRef<'_> {
1043    fn from(value: Term) -> Self {
1044        Self::Term(Cow::Owned(value))
1045    }
1046}
1047#[cfg(feature = "full")]
1048impl<'r> From<UriRef<'r>> for DisplayableRef<'r> {
1049    fn from(value: UriRef<'r>) -> Self {
1050        Self::Uri(either::Left(value))
1051    }
1052}
1053#[cfg(feature = "full")]
1054impl From<Uri> for DisplayableRef<'_> {
1055    fn from(value: Uri) -> Self {
1056        Self::Uri(either::Right(value))
1057    }
1058}
1059#[cfg(feature = "full")]
1060impl<'r> From<&'r Variable> for DisplayableRef<'r> {
1061    fn from(value: &'r Variable) -> Self {
1062        Self::Var(Cow::Borrowed(value))
1063    }
1064}
1065#[cfg(feature = "full")]
1066impl From<Variable> for DisplayableRef<'_> {
1067    fn from(value: Variable) -> Self {
1068        Self::Var(Cow::Owned(value))
1069    }
1070}
1071
1072impl<T: FtmlUri> From<&T> for Displayable {
1073    fn from(value: &T) -> Self {
1074        Self::Uri(value.as_uri().owned())
1075    }
1076}
1077impl From<&str> for Displayable {
1078    fn from(value: &str) -> Self {
1079        Self::String(value.to_string())
1080    }
1081}
1082impl From<String> for Displayable {
1083    fn from(value: String) -> Self {
1084        Self::String(value)
1085    }
1086}
1087impl From<Term> for Displayable {
1088    fn from(value: Term) -> Self {
1089        Self::Term(value)
1090    }
1091}
1092
1093impl From<i128> for Displayable {
1094    fn from(value: i128) -> Self {
1095        Self::Num(value)
1096    }
1097}
1098impl From<usize> for Displayable {
1099    fn from(value: usize) -> Self {
1100        Self::Num(value as _)
1101    }
1102}
1103
1104#[cfg(feature = "full")]
1105#[macro_export]
1106macro_rules! trace {
1107    ($($e:expr),* $(,)? ) => {
1108        {vec![$(
1109            $e.into()
1110        ),*]
1111        }
1112    }
1113}
1114
1115#[cfg(feature = "full")]
1116#[macro_export]
1117macro_rules! traceref {
1118    (FAIL $($e:expr),* $(,)? ) => {
1119        $crate::RefCheckLog::Msg(
1120            vec![$( $e.into() ),*],
1121            $crate::MessageLevel::Failure
1122        )
1123    };
1124    ($($e:expr),* $(,)? ) => {
1125        $crate::RefCheckLog::Msg(
1126            vec![$( $e.into() ),*],
1127            $crate::MessageLevel::Comment
1128        )
1129    }
1130}