use anyhow::{format_err, Result};
use std::fs;
use std::path::PathBuf;
use structopt::StructOpt;
use stylua_lib::{format_code, Config};
#[derive(StructOpt, Debug)]
#[structopt(name = "stylua", about = "A utility to format Lua code")]
struct Opt {
#[structopt(long = "config-path", parse(from_os_str))]
config_path: Option<PathBuf>,
#[structopt(long, default_value = "**/*.lua")]
pattern: String,
#[structopt(parse(from_os_str))]
files: Vec<PathBuf>,
}
fn format_file(path: &PathBuf, config: Config) -> Result<()> {
match fs::read(path) {
Ok(contents) => {
let contents = String::from_utf8_lossy(&contents);
let formatted_contents = match format_code(&contents, config) {
Ok(formatted) => formatted,
Err(error) => {
return Err(format_err!(
"error: could not format file {}: {}",
path.display(),
error
))
}
};
match fs::write(path, formatted_contents) {
Ok(_) => Ok(()),
Err(error) => Err(format_err!(
"error: could not write to file {}: {}",
path.display(),
error
)),
}
}
Err(error) => Err(format_err!(
"error: could not open file {}: {}",
path.display(),
error
)),
}
}
fn format(opt: Opt) -> Result<i32> {
if opt.files.is_empty() {
return Err(format_err!("error: no files provided"));
}
let config: Config = match opt.config_path {
Some(config_path) => match fs::read_to_string(config_path) {
Ok(contents) => match toml::from_str(&contents) {
Ok(config) => config,
Err(error) => {
return Err(format_err!(
"error: config file not in correct format: {}",
error
));
}
},
Err(error) => {
return Err(format_err!("error: couldn't read config file: {}", error));
}
},
None => match fs::read_to_string("stylua.toml") {
Ok(contents) => match toml::from_str(&contents) {
Ok(config) => config,
Err(error) => {
return Err(format_err!(
"error: config file not in correct format: {}",
error
));
}
},
Err(_) => Config::default(),
},
};
let mut errors = vec![];
for file_path in opt.files.iter() {
if file_path.exists() {
if file_path.is_file() {
match format_file(file_path, config) {
Ok(_) => continue,
Err(error) => errors.push(error),
}
} else if file_path.is_dir() {
let glob_pattern = format!("{}/{}", file_path.to_string_lossy(), opt.pattern);
match glob::glob(&glob_pattern) {
Ok(entries) => {
for entry in entries {
match entry {
Ok(path) => match format_file(&path, config) {
Ok(_) => continue,
Err(error) => errors.push(error),
},
Err(error) => {
errors.push(format_err!("error: failed to read file {}", error))
}
}
}
}
Err(error) => errors.push(format_err!(
"error: failed to read glob pattern {}: {}",
glob_pattern,
error
)),
}
} else {
errors.push(format_err!("error: unknown path {}", file_path.display()))
}
} else {
errors.push(format_err!("error: file {} not found", file_path.display()));
continue;
}
}
if !errors.is_empty() {
for error in errors.iter() {
eprintln!("{}", error.to_string());
}
return Ok(1);
}
Ok(0)
}
fn main() {
let opt = Opt::from_args();
let exit_code = match format(opt) {
Ok(code) => code,
Err(e) => {
eprintln!("{}", e.to_string());
1
}
};
std::process::exit(exit_code);
}