use crate::config::Config;
use crate::error::LichenError;
use crate::models::ApplyArgs;
use crate::models::Authors;
use crate::models::License; use crate::utils;
use jiff::civil::Date;
use log::{debug, info, trace};
use regex::Regex;
use std::path::PathBuf;
#[derive(Debug)]
pub struct ApplySettings {
pub license: License,
pub prefer_block: bool,
pub multiple: bool,
pub authors: Option<Authors>,
pub exclude: Option<Regex>,
pub targets: Vec<PathBuf>,
pub date: Date,
pub dry_run: bool,
}
impl ApplySettings {
pub fn new(cli: &ApplyArgs, cfg: &Config, index: Option<usize>) -> Result<Self, LichenError> {
let license = if let Some(cli_lic) = cli.license_args.license {
cli_lic
} else if let Some(idx) = index {
let lic = cfg
.licenses
.as_ref()
.expect("If an index is passed, assume there is a license")
.get(idx)
.ok_or(LichenError::InvalidIndex(idx))?;
lic.id
} else {
return Err(LichenError::MissingLicense);
};
let default_target = vec![PathBuf::from(".")];
let targets: Vec<PathBuf> = if let Some(cli_targets) = cli.file_args.targets.clone() {
cli_targets
} else if let Some(idx) = index {
cfg.licenses
.as_ref()
.expect("If an index is passed, assume there is a license")
.get(idx)
.and_then(|lic| lic.targets.clone())
.unwrap_or(default_target)
} else {
default_target
};
let authors: Option<Authors> = if let Some(cli_authors) = cli.license_args.authors.clone() {
Some(cli_authors)
} else if let Some(idx) = index {
cfg.licenses
.as_ref()
.expect("If an index is passed, assume there is a license")
.get(idx)
.and_then(|lic| lic.authors.clone())
} else {
None
};
let date = if let Some(cli_date) = cli.license_args.date {
cli_date
} else if let Some(idx) = index {
cfg.licenses
.as_ref()
.expect("If an index is passed, assume there is a license")
.get(idx)
.and_then(|lic| lic.date)
.unwrap_or_else(|| jiff::Zoned::now().date())
} else {
jiff::Zoned::now().date()
};
let all = cli.file_args.all.or(cfg.all).unwrap_or(false);
let exclude = utils::build_exclude_regex(&cli.file_args.exclude, Some(cfg), all, index)?;
let multiple = cli.license_args.multiple.or(cfg.multiple).unwrap_or(false);
let prefer_block = cli.prefer_block.or(cfg.prefer_block).unwrap_or(false);
let dry_run = cli.dry_run.unwrap_or(false);
Ok(ApplySettings {
exclude,
license,
dry_run,
targets,
prefer_block,
authors,
date,
multiple,
})
}
}
pub async fn handle_apply(settings: &ApplySettings) -> Result<(), LichenError> {
debug!("Starting handle_apply with args: {:?}", settings);
let license = settings.license;
let exclude_pattern = &settings.exclude;
let targets = &settings.targets;
let multiple = settings.multiple;
let authors = &settings.authors;
let year = &settings.date;
let dry_run = settings.dry_run;
let preference = settings.prefer_block;
debug!(
"Applying license header for: {} to targets: {:?}",
license.spdx_id(),
targets
);
debug!("Exclusion pattern: {:?}", exclude_pattern);
debug!("Block comments preferred?: {}", preference);
let template_content = settings.license.template_content();
debug!(
"Using embedded template content for {}",
settings.license.spdx_id()
);
debug!("Embedded template content:\n{}", template_content);
let rendered_license =
utils::render_license(template_content, year, authors).map_err(LichenError::RenderError)?; trace!("License content rendered successfully.");
debug!("Rendered content:\n{}", rendered_license);
let files_to_process = utils::get_valid_files(targets, exclude_pattern)?;
if files_to_process.is_empty() {
return Err(LichenError::Msg(
"No files require processing based on targets and exclusions. Exiting 'apply' command."
.to_string(),
)); }
if dry_run {
info!("Changes will impact {:?}", files_to_process);
return Ok(());
}
let max_concurrency = std::thread::available_parallelism()
.expect("There should always be some available parellism on the computer"); utils::apply_headers_to_files(
&rendered_license,
&files_to_process,
max_concurrency,
preference,
multiple,
)
.await?;
info!("Finished applying license headers.");
Ok(())
}