use crate::build;
use crate::log::Level;
use crate::session::{ColorConfig, Session};
use std::default::Default;
use std::env;
use std::env::current_dir;
use std::error::Error;
use std::io;
use std::path::{Path, PathBuf};
use std::rc::Rc;
#[cfg(test)]
mod test;
#[derive(Clone, Default)]
pub struct Configuration {
session: Session,
}
impl Configuration {
pub fn new() -> Configuration {
Configuration::default()
}
pub fn always_use_colors(&mut self) -> &mut Configuration {
self.session.color_config = ColorConfig::Yes;
self
}
pub fn never_use_colors(&mut self) -> &mut Configuration {
self.session.color_config = ColorConfig::No;
self
}
pub fn use_colors_if_tty(&mut self) -> &mut Configuration {
self.session.color_config = ColorConfig::IfTty;
self
}
pub fn set_in_dir<P>(&mut self, dir: P) -> &mut Self
where
P: Into<PathBuf>,
{
self.session.in_dir = Some(dir.into());
self
}
pub fn set_out_dir<P>(&mut self, dir: P) -> &mut Self
where
P: Into<PathBuf>,
{
self.session.out_dir = Some(dir.into());
self
}
pub fn use_cargo_dir_conventions(&mut self) -> &mut Self {
self.set_in_dir("src")
.set_out_dir(env::var("OUT_DIR").unwrap());
self
}
pub fn generate_in_source_tree(&mut self) -> &mut Self {
self.set_in_dir(Path::new(".")).set_out_dir(Path::new("."))
}
pub fn force_build(&mut self, val: bool) -> &mut Configuration {
self.session.force_build = val;
self
}
pub fn emit_rerun_directives(&mut self, val: bool) -> &mut Configuration {
self.session.emit_rerun_directives = val;
self
}
pub fn emit_comments(&mut self, val: bool) -> &mut Configuration {
self.session.emit_comments = val;
self
}
pub fn emit_whitespace(&mut self, val: bool) -> &mut Configuration {
self.session.emit_whitespace = val;
self
}
pub fn emit_report(&mut self, val: bool) -> &mut Configuration {
self.session.emit_report = val;
self
}
pub fn log_quiet(&mut self) -> &mut Configuration {
self.session.log.set_level(Level::Taciturn);
self
}
pub fn log_info(&mut self) -> &mut Configuration {
self.session.log.set_level(Level::Informative);
self
}
pub fn log_verbose(&mut self) -> &mut Configuration {
self.session.log.set_level(Level::Verbose);
self
}
pub fn log_debug(&mut self) -> &mut Configuration {
self.session.log.set_level(Level::Debug);
self
}
pub fn set_macro_recursion_limit(&mut self, val: u16) -> &mut Configuration {
self.session.macro_recursion_limit = val;
self
}
pub fn set_features<I>(&mut self, iterable: I) -> &mut Configuration
where
I: IntoIterator<Item = String>,
{
self.session.features = Some(iterable.into_iter().collect());
self
}
#[doc(hidden)]
pub fn unit_test(&mut self) -> &mut Configuration {
self.session.unit_test = true;
self
}
pub fn process(&self) -> Result<(), Box<dyn Error>> {
let root = if let Some(ref d) = self.session.in_dir {
d.as_path()
} else {
Path::new(".")
};
self.process_dir(root)
}
pub fn process_current_dir(&self) -> Result<(), Box<dyn Error>> {
self.process_dir(current_dir()?)
}
fn verify_no_in_dir_conflict(&self, dir_path: Option<PathBuf>) -> Result<(), Box<dyn Error>> {
if self.session.in_dir.is_some() && self.session.in_dir != dir_path {
eprintln!("Error: \"process_*()\" contradicts previously set in_dir");
return Err(Box::new(io::Error::new(
io::ErrorKind::InvalidInput,
"\"process_*()\" functions contradict previously set in_dir. The in_dir is set by either `set_in_dir()` or `use_cargo_dir_conventions()`. Either use `process()` instead, or omit `set_in_dir()`. (Note: in previous versions of lalrpop, this combination could affect the parser output dir. If you were relying on this behavior to output the parser in your source directory, you may want to use `set_out_dir()` to retain that behavior.",
)));
}
Ok(())
}
pub fn process_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Box<dyn Error>> {
let mut session = self.session.clone();
self.verify_no_in_dir_conflict(Some(path.as_ref().to_path_buf()))?;
session.in_dir = Some(path.as_ref().to_path_buf());
if session.out_dir.is_none() {
let out_dir = env::var_os("OUT_DIR").ok_or("missing OUT_DIR variable")?;
session.out_dir = Some(PathBuf::from(out_dir));
}
if self.session.features.is_none() {
session.features = Some(
env::vars()
.filter_map(|(feature_var, _)| {
let prefix = "CARGO_FEATURE_";
feature_var
.strip_prefix(prefix)
.map(|feature| feature.replace('_', "-").to_ascii_lowercase())
})
.collect(),
);
}
let session = Rc::new(session);
build::process_dir(session, path)?;
Ok(())
}
pub fn process_file<P: AsRef<Path>>(&self, path: P) -> Result<(), Box<dyn Error>> {
let session = Rc::new(self.session.clone());
self.verify_no_in_dir_conflict(None)?;
build::process_file(session, path)?;
Ok(())
}
}
pub fn process_root() -> Result<(), Box<dyn Error>> {
Configuration::new().process_current_dir()
}
pub fn process_src() -> Result<(), Box<dyn Error>> {
Configuration::new().set_in_dir("./src").process()
}
#[deprecated(
since = "0.20.2",
note = "use `Configuration::new().force_build(true).process_current_dir()` instead"
)]
pub fn process_root_unconditionally() -> Result<(), Box<dyn Error>> {
Configuration::new().force_build(true).process_current_dir()
}