use cargo_cyclonedx::generator::{Generator, SbomGenerator};
use cargo_cyclonedx::traits::ToXml;
use std::{
fs::File,
io::{self, LineWriter},
path::PathBuf,
};
use anyhow::Result;
use env_logger::Builder;
use log::{LevelFilter, SetLoggerError};
use structopt::StructOpt;
use xml_writer::XmlWriter;
mod format;
use format::Format;
mod cli;
use cli::{Args, Opts};
fn main() -> anyhow::Result<()> {
let Opts::Bom(args) = Opts::from_args();
setup_logging(&args)?;
real_main(args)
}
fn real_main(args: Args) -> anyhow::Result<()> {
let manifest = locate_manifest(&args)?;
let generator = SbomGenerator { all: args.all };
log::trace!("SBOM generation started");
let boms = generator.create_sboms(manifest)?;
log::trace!("SBOM generation finished");
match args.format {
Format::Json => {
for (manifest_path, bom) in boms {
let path = manifest_path.with_file_name("bom.json");
log::info!("Outputting {}", path.display());
serde_json::to_writer_pretty(File::create(path)?, &bom)
.map_err(anyhow::Error::from)?;
}
}
Format::Xml => {
for (manifest_path, bom) in boms {
let path = manifest_path.with_file_name("bom.xml");
log::info!("Outputting {}", path.display());
let file = File::create(path)?;
let file = LineWriter::new(file);
let mut xml = XmlWriter::new(file);
bom.to_xml(&mut xml)?;
xml.close()?;
xml.flush()?;
let _actual = xml.into_inner();
}
}
}
Ok(())
}
fn setup_logging(args: &Args) -> Result<(), SetLoggerError> {
let mut builder = Builder::new();
builder.filter_module("cargo::", LevelFilter::Error);
let level_filter = if args.quiet {
LevelFilter::Off
} else {
match args.verbose {
0 => LevelFilter::Error,
1 => LevelFilter::Info,
2 => LevelFilter::Debug,
_ => LevelFilter::Trace,
}
};
builder.filter_level(level_filter);
builder.parse_default_env(); builder.try_init()?;
Ok(())
}
fn locate_manifest(args: &Args) -> Result<PathBuf, io::Error> {
if let Some(manifest_path) = &args.manifest_path {
let manifest_path = manifest_path.canonicalize()?;
log::info!(
"Using manually specified Cargo.toml manifest located at: {}",
manifest_path.to_string_lossy()
);
Ok(manifest_path.clone())
} else {
let manifest_path = std::env::current_dir()?.join("Cargo.toml");
log::info!(
"Using Cargo.toml manifest located at: {}",
manifest_path.to_string_lossy()
);
Ok(manifest_path)
}
}