1#![warn(
3 missing_docs,
4 missing_debug_implementations,
5 missing_copy_implementations,
6 trivial_casts,
7 trivial_numeric_casts,
8 unsafe_code,
9 unstable_features,
10 unused_import_braces,
11 unused_qualifications
12)]
13
14use std::env;
15use std::fs;
16use std::fs::File;
17use std::io::Write;
18use std::path::PathBuf;
19use std::process::Command;
20
21pub mod config;
23
24pub mod errors;
26
27mod comment;
28mod diff;
29mod format;
30mod hir;
31mod tmpfile;
32
33use prettyprint::PrettyPrinter;
34
35pub use crate::config::{Config, Opt};
36pub use crate::errors::InspectError;
37
38use crate::comment::comment_file;
39pub use crate::diff::diff;
40use crate::format::format;
41use crate::hir::HIR;
42pub use crate::tmpfile::tmpfile;
43
44pub fn inspect(config: &Config) -> Result<(), InspectError> {
47 let output = match &config.files {
48 Some(files) => {
49 let hir0 = inspect_file(
50 PathBuf::from(files.0.clone()),
51 config.verbose,
52 config.unpretty.clone(),
53 )?;
54 let hir1 = inspect_file(
55 PathBuf::from(files.1.clone()),
56 config.verbose,
57 config.unpretty.clone(),
58 )?;
59 diff(try_format(hir0.output)?, try_format(hir1.output)?)?
60 }
61 None => inspect_single(config)?,
62 };
63
64 if config.plain {
65 println!("{}", output);
66 } else if config.unpretty.starts_with("flowgraph") {
67 if let Some(path) = &config.input {
68 let input_path = PathBuf::from(path);
69
70 let file_name = input_path.file_name().ok_or_else(|| {
72 InspectError::Flowgraph(String::from(
73 "Invalid path found. The input path should be a file.",
74 ))
75 })?;
76
77 let mut output_path = PathBuf::from(file_name);
78 let ext = &config.format;
79 output_path.set_extension(&ext);
80
81 let tmp_file_path = tmpfile()?;
83 let mut file = File::create(&tmp_file_path)?;
84 file.write_all(output.as_bytes())?;
85
86 let output_str = output_path.to_str().ok_or_else(|| {
88 InspectError::Flowgraph(String::from("Failed to convert output path to string."))
89 })?;
90
91 let input_str = tmp_file_path.to_str().ok_or_else(|| {
92 InspectError::Flowgraph(String::from("Failed to convert temporary path to string."))
93 })?;
94
95 log::info!("Writing \"{}\"...", output_str);
96
97 let args = [&format!("-T{}", ext), "-o", output_str, input_str];
98 Command::new("dot")
99 .args(&args)
100 .spawn()
101 .map_err(|e| InspectError::DotExec(e))?;
102 }
103 } else {
104 let mut builder = PrettyPrinter::default();
105 builder.language("rust");
106 if let Some(theme) = &config.theme {
107 builder.theme(theme.clone());
108 }
109
110 let printer = builder.build()?;
111 let header = config.input.to_owned().unwrap_or(env::current_dir()?);
112 printer.string_with_header(output, header.to_string_lossy().to_string())?;
113 }
114 Ok(())
115}
116
117pub fn inspect_single(config: &Config) -> Result<String, InspectError> {
120 let hir = match config.input.clone() {
121 Some(input) => inspect_file(input, config.verbose, config.unpretty.clone()),
122 None => inspect_crate(config),
123 }?;
124 Ok(try_format(hir.output)?)
125}
126
127fn inspect_file(input: PathBuf, verbose: bool, unpretty: String) -> Result<HIR, InspectError> {
129 let input = if verbose {
130 let tmp = tmpfile()?;
135 fs::copy(&input, &tmp)?;
136 comment_file(&tmp)?;
137 tmp
138 } else {
139 input
140 };
141 hir::from_file(&input, &unpretty)
142}
143
144fn inspect_crate(config: &Config) -> Result<HIR, InspectError> {
146 if config.verbose {
147 unimplemented!(
148 "Verbose option doesn't work for crates yet. \
149 See https://github.com/mre/cargo-inspect/issues/5"
150 )
151 }
153 hir::from_crate(&config.unpretty)
154}
155
156fn try_format(input: String) -> Result<String, InspectError> {
159 let mut formatted = format(&input)?;
160 if formatted.is_empty() {
161 formatted = input;
165 }
166 Ok(formatted)
167}