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}