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