lalrpop/api/
mod.rs

1use crate::build;
2use crate::log::Level;
3use crate::session::{ColorConfig, Session};
4use std::default::Default;
5use std::env;
6use std::env::current_dir;
7use std::error::Error;
8use std::path::{Path, PathBuf};
9use std::rc::Rc;
10
11/// Configure various aspects of how LALRPOP works.
12/// Intended for use within a `build.rs` script.
13/// To get the default configuration, use `Configuration::new`.
14#[derive(Clone, Default)]
15pub struct Configuration {
16    session: Session,
17}
18
19impl Configuration {
20    /// Creates the default configuration.
21    ///
22    /// equivalent to `Configuration::default`.
23    pub fn new() -> Configuration {
24        Configuration::default()
25    }
26
27    /// Always use ANSI colors in output, even if output does not appear to be a TTY.
28    pub fn always_use_colors(&mut self) -> &mut Configuration {
29        self.session.color_config = ColorConfig::Yes;
30        self
31    }
32
33    /// Never use ANSI colors in output, even if output appears to be a TTY.
34    pub fn never_use_colors(&mut self) -> &mut Configuration {
35        self.session.color_config = ColorConfig::No;
36        self
37    }
38
39    /// Use ANSI colors in output if output appears to be a TTY, but
40    /// not otherwise. This is the default.
41    pub fn use_colors_if_tty(&mut self) -> &mut Configuration {
42        self.session.color_config = ColorConfig::IfTty;
43        self
44    }
45
46    /// Specify a custom directory to search for input files.
47    ///
48    /// This directory is recursively searched for `.lalrpop` files to be
49    /// considered as input files.  This configuration setting also
50    /// impacts where output files are placed; paths are made relative
51    /// to the input path before being resolved relative to the output
52    /// path.  By default, the input directory is the current working
53    /// directory.
54    pub fn set_in_dir<P>(&mut self, dir: P) -> &mut Self
55    where
56        P: Into<PathBuf>,
57    {
58        self.session.in_dir = Some(dir.into());
59        self
60    }
61
62    /// Specify a custom directory to use when writing output files.
63    ///
64    /// By default, the output directory is the same as the input
65    /// directory.
66    pub fn set_out_dir<P>(&mut self, dir: P) -> &mut Self
67    where
68        P: Into<PathBuf>,
69    {
70        self.session.out_dir = Some(dir.into());
71        self
72    }
73
74    /// Apply `cargo` directory location conventions.
75    ///
76    /// This sets the input directory to `src` and the output directory to
77    /// `$OUT_DIR`.
78    pub fn use_cargo_dir_conventions(&mut self) -> &mut Self {
79        self.set_in_dir("src")
80            .set_out_dir(env::var("OUT_DIR").unwrap());
81        self
82    }
83
84    /// Write output files in the same directory of the input files.
85    ///
86    /// If this option is enabled, you have to load the parser as a module:
87    ///
88    /// ```no_run
89    /// mod parser; // synthesized from parser.lalrpop
90    /// ```
91    ///
92    /// This was the default behaviour up to version 0.15.
93    pub fn generate_in_source_tree(&mut self) -> &mut Self {
94        self.set_in_dir(Path::new(".")).set_out_dir(Path::new("."))
95    }
96
97    /// If true, always convert `.lalrpop` files into `.rs` files, even if the
98    /// `.rs` file is newer. Default is false.
99    pub fn force_build(&mut self, val: bool) -> &mut Configuration {
100        self.session.force_build = val;
101        self
102    }
103
104    /// If true, print `rerun-if-changed` directives to standard output.
105    ///
106    /// If this is set, Cargo will only rerun the build script if any of the processed
107    /// `.lalrpop` files are changed. This option is independent of
108    /// [`Self::force_build()`], although it would be usual to set [`Self::force_build()`] and
109    /// [`Self::emit_rerun_directives()`] at the same time.
110    ///
111    /// While many build scripts will want to set this to `true`, the default is
112    /// false, because emitting any rerun directives to Cargo will cause the
113    /// script to only be rerun when Cargo thinks it is needed. This could lead
114    /// to hard-to-find bugs if other parts of the build script do not emit
115    /// directives correctly, or need to be rerun unconditionally.
116    pub fn emit_rerun_directives(&mut self, val: bool) -> &mut Configuration {
117        self.session.emit_rerun_directives = val;
118        self
119    }
120
121    /// If true, emit comments into the generated code.
122    ///
123    /// This makes the generated code significantly larger. Default is false.
124    pub fn emit_comments(&mut self, val: bool) -> &mut Configuration {
125        self.session.emit_comments = val;
126        self
127    }
128
129    /// If false, shrinks the generated code by removing redundant white space.
130    /// Default is true.
131    pub fn emit_whitespace(&mut self, val: bool) -> &mut Configuration {
132        self.session.emit_whitespace = val;
133        self
134    }
135
136    /// If true, emit report file about generated code.
137    pub fn emit_report(&mut self, val: bool) -> &mut Configuration {
138        self.session.emit_report = val;
139        self
140    }
141
142    /// Minimal logs: only for errors that halt progress.
143    pub fn log_quiet(&mut self) -> &mut Configuration {
144        self.session.log.set_level(Level::Taciturn);
145        self
146    }
147
148    /// Informative logs: give some high-level indications of
149    /// progress (default).
150    pub fn log_info(&mut self) -> &mut Configuration {
151        self.session.log.set_level(Level::Informative);
152        self
153    }
154
155    /// Verbose logs: more than info, but still not overwhelming.
156    pub fn log_verbose(&mut self) -> &mut Configuration {
157        self.session.log.set_level(Level::Verbose);
158        self
159    }
160
161    /// Debug logs: better redirect this to a file. Intended for
162    /// debugging LALRPOP itself.
163    pub fn log_debug(&mut self) -> &mut Configuration {
164        self.session.log.set_level(Level::Debug);
165        self
166    }
167
168    /// Set the max macro recursion depth.
169    ///
170    /// As lalrpop is resolving a macro, it may discover new macros uses in the
171    /// macro definition to resolve.  Typically deep recursion indicates a
172    /// recursive macro use that is non-resolvable.  The default resolution
173    /// depth is 200.
174    pub fn set_macro_recursion_limit(&mut self, val: u16) -> &mut Configuration {
175        self.session.macro_recursion_limit = val;
176        self
177    }
178
179    /// Sets the features used during compilation, disables the use of cargo features.
180    /// (Default: Loaded from `CARGO_FEATURE_{}` environment variables).
181    pub fn set_features<I>(&mut self, iterable: I) -> &mut Configuration
182    where
183        I: IntoIterator<Item = String>,
184    {
185        self.session.features = Some(iterable.into_iter().collect());
186        self
187    }
188
189    /// Enables "unit-testing" configuration. This is only for
190    /// lalrpop-test.
191    #[doc(hidden)]
192    pub fn unit_test(&mut self) -> &mut Configuration {
193        self.session.unit_test = true;
194        self
195    }
196
197    /// Process all files according to the `set_in_dir` and
198    /// `set_out_dir` configuration.
199    pub fn process(&self) -> Result<(), Box<dyn Error>> {
200        let root = if let Some(ref d) = self.session.in_dir {
201            d.as_path()
202        } else {
203            Path::new(".")
204        };
205        self.process_dir(root)
206    }
207
208    /// Process all files in the current directory, which -- unless you
209    /// have changed it -- is typically the root of the crate being compiled.
210    pub fn process_current_dir(&self) -> Result<(), Box<dyn Error>> {
211        self.process_dir(current_dir()?)
212    }
213
214    /// Process all `.lalrpop` files in `path`.
215    pub fn process_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Box<dyn Error>> {
216        let mut session = self.session.clone();
217
218        // If in/out dir are empty, use cargo conventions by default.
219        // See https://github.com/lalrpop/lalrpop/issues/280
220        if session.in_dir.is_none() {
221            let mut in_dir = env::current_dir()?;
222            in_dir.push("src");
223            session.in_dir = Some(in_dir);
224        }
225
226        if session.out_dir.is_none() {
227            let out_dir = env::var_os("OUT_DIR").ok_or("missing OUT_DIR variable")?;
228            session.out_dir = Some(PathBuf::from(out_dir));
229        }
230
231        if self.session.features.is_none() {
232            // Pick up the features cargo sets for build scripts
233            session.features = Some(
234                env::vars()
235                    .filter_map(|(feature_var, _)| {
236                        let prefix = "CARGO_FEATURE_";
237                        feature_var
238                            .strip_prefix(prefix)
239                            .map(|feature| feature.replace('_', "-").to_ascii_lowercase())
240                    })
241                    .collect(),
242            );
243        }
244
245        let session = Rc::new(session);
246        build::process_dir(session, path)?;
247        Ok(())
248    }
249
250    /// Process the given `.lalrpop` file.
251    pub fn process_file<P: AsRef<Path>>(&self, path: P) -> Result<(), Box<dyn Error>> {
252        let session = Rc::new(self.session.clone());
253        build::process_file(session, path)?;
254        Ok(())
255    }
256}
257
258/// Process all files in the current directory.
259///
260/// Unless you have changed it this is typically the root of the crate being compiled.
261/// If your project only builds one crate and your files are in a ./src directory, you should use
262/// `process_src()` instead
263///
264/// Equivalent to `Configuration::new().process_current_dir()`.
265pub fn process_root() -> Result<(), Box<dyn Error>> {
266    Configuration::new().process_current_dir()
267}
268
269/// Process all files in ./src.
270///
271/// In many cargo projects which build only one crate, this is the normal
272/// location for source files.  If you are running lalrpop from a top level build.rs in a
273/// project that builds multiple crates, you may want `process_root()` instead.
274/// See `Configuration` if you would like more fine-grain control over lalrpop.
275pub fn process_src() -> Result<(), Box<dyn Error>> {
276    Configuration::new().set_in_dir("./src").process()
277}
278
279/// Deprecated in favor of `Configuration`.
280///
281/// Instead, consider using:
282///
283/// ```rust
284/// Configuration::new().force_build(true).process_current_dir()
285/// ```
286///
287#[deprecated(
288    since = "1.0.0",
289    note = "use `Configuration::new().force_build(true).process_current_dir()` instead"
290)]
291pub fn process_root_unconditionally() -> Result<(), Box<dyn Error>> {
292    Configuration::new().force_build(true).process_current_dir()
293}