#![warn(
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
use std::env;
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::process::Command;
pub mod config;
pub mod errors;
mod comment;
mod diff;
mod format;
mod hir;
mod tmpfile;
use prettyprint::PrettyPrinter;
pub use crate::config::{Config, Opt};
pub use crate::errors::InspectError;
use crate::comment::comment_file;
pub use crate::diff::diff;
use crate::format::format;
use crate::hir::HIR;
pub use crate::tmpfile::tmpfile;
pub fn inspect(config: &Config) -> Result<(), InspectError> {
let output = match &config.files {
Some(files) => {
let hir0 = inspect_file(
PathBuf::from(files.0.clone()),
config.verbose,
config.unpretty.clone(),
)?;
let hir1 = inspect_file(
PathBuf::from(files.1.clone()),
config.verbose,
config.unpretty.clone(),
)?;
diff(try_format(hir0.output)?, try_format(hir1.output)?)?
}
None => inspect_single(config)?,
};
if config.plain {
println!("{}", output);
} else if config.unpretty.starts_with("flowgraph") {
if let Some(path) = &config.input {
let input_path = PathBuf::from(path);
let file_name = input_path.file_name().ok_or_else(|| {
InspectError::Flowgraph(String::from(
"Invalid path found. The input path should be a file.",
))
})?;
let mut output_path = PathBuf::from(file_name);
let ext = &config.format;
output_path.set_extension(&ext);
let tmp_file_path = tmpfile()?;
let mut file = File::create(&tmp_file_path)?;
file.write_all(output.as_bytes())?;
let output_str = output_path.to_str().ok_or_else(|| {
InspectError::Flowgraph(String::from("Failed to convert output path to string."))
})?;
let input_str = tmp_file_path.to_str().ok_or_else(|| {
InspectError::Flowgraph(String::from("Failed to convert temporary path to string."))
})?;
log::info!("Writing \"{}\"...", output_str);
let args = [&format!("-T{}", ext), "-o", output_str, input_str];
Command::new("dot")
.args(&args)
.spawn()
.map_err(|e| InspectError::DotExec(e))?;
}
} else {
let mut builder = PrettyPrinter::default();
builder.language("rust");
if let Some(theme) = &config.theme {
builder.theme(theme.clone());
}
let printer = builder.build()?;
let header = config.input.to_owned().unwrap_or(env::current_dir()?);
printer.string_with_header(output, header.to_string_lossy().to_string())?;
}
Ok(())
}
pub fn inspect_single(config: &Config) -> Result<String, InspectError> {
let hir = match config.input.clone() {
Some(input) => inspect_file(input, config.verbose, config.unpretty.clone()),
None => inspect_crate(config),
}?;
Ok(try_format(hir.output)?)
}
fn inspect_file(input: PathBuf, verbose: bool, unpretty: String) -> Result<HIR, InspectError> {
let input = if verbose {
let tmp = tmpfile()?;
fs::copy(&input, &tmp)?;
comment_file(&tmp)?;
tmp
} else {
input
};
hir::from_file(&input, &unpretty)
}
fn inspect_crate(config: &Config) -> Result<HIR, InspectError> {
if config.verbose {
unimplemented!(
"Verbose option doesn't work for crates yet. \
See https://github.com/mre/cargo-inspect/issues/5"
)
}
hir::from_crate(&config.unpretty)
}
fn try_format(input: String) -> Result<String, InspectError> {
let mut formatted = format(&input)?;
if formatted.is_empty() {
formatted = input;
}
Ok(formatted)
}