Skip to main content

lexigram_lib/
build.rs

1// Copyright (c) 2025 Redglyph (@gmail.com). All Rights Reserved.
2
3use std::error::Error;
4use std::fmt::{Display, Formatter};
5use lexigram_core::log::{BufLog, LogReader, LogStatus};
6
7// ---------------------------------------------------------------------------------------------
8
9#[derive(Debug)]
10pub enum BuildErrorSource {
11    RuleTreeSet,
12    Dfa,
13    DfaBuilder,
14    LexerGen,
15    Lexi,
16    ProdRuleSet,
17    ParserGen,
18    Gram,
19    Lexigram,
20    BuildGram,
21}
22
23#[derive(Debug)]
24pub struct BuildError {
25    log: BufLog,
26    source: BuildErrorSource,
27}
28
29impl BuildError {
30    pub fn new(log: BufLog, source: BuildErrorSource) -> Self {
31        BuildError { log, source }
32    }
33
34    pub fn get_log(self) -> BufLog {
35        self.log
36    }
37}
38
39impl Display for BuildError {
40    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
41        writeln!(f, "Errors have occurred in {:?}:\n{}", self.source, self.log.get_messages_str())
42    }
43}
44
45impl Error for BuildError {
46}
47
48pub trait HasBuildErrorSource {
49    const SOURCE: BuildErrorSource;
50
51    fn get_build_error_source() -> BuildErrorSource {
52        Self::SOURCE
53    }
54}
55
56// ---------------------------------------------------------------------------------------------
57// Local from/into and try_from/try_into
58// - we have to redefine our own From/Into traits because the standard lib has a blanket
59//   implementation that automatically generates TryFrom from From, which is always Ok...
60// - we have to redefine our own TryFrom/TryInto traits, since it's otherwise not allowed to
61//   implement a foreign trait on anything else than a local type (a local trait isn't enough)
62
63pub trait BuildFrom<S>: Sized {
64    /// Converts to this type from the input type.
65    #[must_use]
66    fn build_from(source: S) -> Self;
67}
68
69pub trait BuildInto<T>: Sized {
70    /// Converts this type into the (usually inferred) input type.
71    #[must_use]
72    fn build_into(self) -> T;
73}
74
75impl<S, T> BuildInto<T> for S
76where
77    T: BuildFrom<S>,
78{
79    /// Calls `T::from(self)` to convert a `S` into a `T`.
80    #[inline]
81    fn build_into(self) -> T { T::build_from(self) }
82}
83
84impl<S> BuildFrom<S> for S {
85    fn build_from(source: S) -> Self {
86        source
87    }
88}
89
90// ---------------------------------------------------------------------------------------------------------
91
92pub trait TryBuildFrom<T>: Sized {
93    /// The type returned in the event of a conversion error.
94    type Error;
95
96    /// Performs the conversion.
97    fn try_build_from(source: T) -> Result<Self, Self::Error>;
98}
99
100pub trait TryBuildInto<T>: Sized {
101    /// The type returned in the event of a conversion error.
102    type Error;
103
104    /// Performs the conversion.
105    fn try_build_into(self) -> Result<T, Self::Error>;
106}
107
108impl<S, T> TryBuildInto<T> for S
109where
110    T: TryBuildFrom<S>,
111{
112    type Error = T::Error;
113
114    #[inline]
115    fn try_build_into(self) -> Result<T, T::Error> { T::try_build_from(self) }
116}
117
118impl<S, T> TryBuildFrom<S> for T
119where
120    S: LogReader<Item = BufLog> + HasBuildErrorSource,
121    T: LogReader<Item = BufLog> + BuildFrom<S> + HasBuildErrorSource,
122{
123    type Error = BuildError;
124
125    fn try_build_from(source: S) -> Result<Self, Self::Error> {
126        const VERBOSE: bool = false;
127        if VERBOSE {
128            println!("try_build_from <{}> -> <{}>: source messages\n{}",
129                     std::any::type_name::<S>(), std::any::type_name::<T>(),
130                     source.get_log().get_messages_str());
131        }
132        if source.get_log().has_no_errors() {
133            let target = T::build_from(source);
134            if VERBOSE {
135                println!("try_build_from <{}> -> <{}>: target messages\n{}",
136                         std::any::type_name::<S>(), std::any::type_name::<T>(),
137                         target.get_log().get_messages_str());
138            }
139            if target.get_log().has_no_errors() {
140                Ok(target)
141            } else {
142                Err(BuildError::new(target.give_log(), S::get_build_error_source()))
143            }
144        } else {
145            Err(BuildError::new(source.give_log(), S::get_build_error_source()))
146        }
147    }
148}