1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//! Internal configuration and session-specific settings. This is similar
//! to `configuration::Configuration`, but it is not exported outside the
//! crate. Note that all fields are public and so forth for convenience.

use crate::log::{Level, Log};
use crate::style::{self, Style};
use std::collections::BTreeSet;
use std::default::Default;
use std::path;

// These two, ubiquitous types are defined here so that their fields can be private
// across crate, but visible within the crate:

#[derive(Copy, Clone, Default)]
pub enum ColorConfig {
    /// Use ANSI colors.
    Yes,

    /// Do NOT use ANSI colors.
    No,

    /// Use them if we detect a TTY output (default).
    #[default]
    IfTty,
}

/// Various options to control debug output. Although this struct is
/// technically part of LALRPOP's exported interface, it is not
/// considered part of the semver guarantees as end-users are not
/// expected to use it.
#[derive(Clone)]
pub struct Session {
    pub log: Log,

    pub force_build: bool,

    pub in_dir: Option<path::PathBuf>,

    pub out_dir: Option<path::PathBuf>,

    /// Emit `rerun-if-changed` directives for Cargo
    pub emit_rerun_directives: bool,

    /// Emit comments in generated code explaining the states and so
    /// forth.
    pub emit_comments: bool,

    /// Emit whitespace in the generated code to improve readability.
    pub emit_whitespace: bool,

    /// Emit report file about generated code
    pub emit_report: bool,

    pub color_config: ColorConfig,

    /// Stop after you find `max_errors` errors. If this value is 0,
    /// report *all* errors. Note that we MAY always report more than
    /// this value if we so choose.
    pub max_errors: usize,

    // Styles to use when formatting error reports
    /// Applied to the heading in a message.
    pub heading: Style,

    /// Applied to symbols in an ambiguity report (where there is no cursor)
    pub ambig_symbols: Style,

    /// Applied to symbols before the cursor in a local ambiguity report
    pub observed_symbols: Style,

    /// Applied to symbols at the cursor in a local ambiguity report,
    /// if it is a non-terminal
    pub cursor_symbol: Style,

    /// Applied to symbols after the cursor in a local ambiguity report
    pub unobserved_symbols: Style,

    /// Applied to terminal symbols, in addition to the above styles
    pub terminal_symbol: Style,

    /// Applied to nonterminal symbols, in addition to the above styles
    pub nonterminal_symbol: Style,

    /// Style to use when printing "Hint:"
    pub hint_text: Style,

    /// Unit testing (lalrpop-test) configuration
    pub unit_test: bool,

    /// Features used for conditional compilation
    pub features: Option<BTreeSet<String>>,
}

impl Session {
    pub fn new() -> Session {
        Session {
            log: Log::new(Level::Informative),
            in_dir: None,
            out_dir: None,
            force_build: false,
            emit_rerun_directives: false,
            emit_comments: false,
            emit_whitespace: true,
            emit_report: false,
            color_config: ColorConfig::default(),
            max_errors: 1,
            heading: style::FG_WHITE.with(style::BOLD),
            ambig_symbols: style::FG_WHITE,
            observed_symbols: style::FG_BRIGHT_GREEN,
            cursor_symbol: style::FG_BRIGHT_WHITE,
            unobserved_symbols: style::FG_BRIGHT_RED,
            terminal_symbol: style::BOLD,
            nonterminal_symbol: style::DEFAULT,
            hint_text: style::FG_BRIGHT_MAGENTA.with(style::BOLD),
            unit_test: false,
            features: Default::default(),
        }
    }

    /// A session suitable for use in testing.
    #[cfg(test)]
    pub fn test() -> Session {
        Session {
            log: Log::new(Level::Debug),
            in_dir: None,
            out_dir: None,
            force_build: false,
            emit_rerun_directives: false,
            emit_comments: false,
            emit_whitespace: true,
            emit_report: false,
            color_config: ColorConfig::IfTty,
            max_errors: 1,
            heading: Style::new(),
            ambig_symbols: Style::new(),
            observed_symbols: Style::new(),
            cursor_symbol: Style::new(),
            unobserved_symbols: Style::new(),
            terminal_symbol: Style::new(),
            nonterminal_symbol: Style::new(),
            hint_text: Style::new(),
            unit_test: true,
            features: Default::default(),
        }
    }

    /// Indicates whether we should stop after `actual_errors` number
    /// of errors have been reported.
    pub fn stop_after(&self, actual_errors: usize) -> bool {
        self.max_errors != 0 && actual_errors >= self.max_errors
    }

    pub fn log<M>(&self, level: Level, message: M)
    where
        M: FnOnce() -> String,
    {
        self.log.log(level, message)
    }

    pub fn emit_rerun_directive(&self, path: &path::Path) {
        if self.emit_rerun_directives {
            if let Some(display) = path.to_str() {
                println!("cargo:rerun-if-changed={}", display)
            } else {
                println!("cargo:warning=LALRPOP is unable to inform Cargo that {} is a dependency because its filename cannot be represented in UTF-8. This is probably because it contains an unpaired surrogate character on Windows. As a result, your build script will not be rerun when it changes.", path.to_string_lossy());
            }
        }
    }
}

impl Default for Session {
    fn default() -> Self {
        Session::new()
    }
}