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