#[cfg(any(
feature = "subcommand-dist-build-license",
feature = "subcommand-dist-build-doc"
))]
use cargo_metadata::camino::Utf8PathBuf;
use cargo_metadata::{Package, PackageName, camino::Utf8Path};
use super::{DistTargetConfig, DistTargetConfigBuilder};
use crate::{Result, workspace::PackageExt};
#[derive(Debug)]
pub struct DistPackageConfigBuilder<'a> {
name: PackageName,
metadata: &'a Package,
targets: Option<Vec<DistTargetConfig<'a>>>,
#[cfg(feature = "subcommand-dist-build-bin")]
cargo_build_options: Vec<String>,
#[cfg(feature = "subcommand-dist-build-license")]
license_files: Option<Vec<Utf8PathBuf>>,
#[cfg(feature = "subcommand-dist-build-doc")]
documents: Option<Vec<Utf8PathBuf>>,
}
impl<'a> DistPackageConfigBuilder<'a> {
pub(crate) fn new(package: &'a Package) -> Self {
Self {
name: package.name.clone(),
metadata: package,
targets: None,
#[cfg(feature = "subcommand-dist-build-bin")]
cargo_build_options: vec![],
#[cfg(feature = "subcommand-dist-build-license")]
license_files: None,
#[cfg(feature = "subcommand-dist-build-doc")]
documents: None,
}
}
pub fn all_binaries(&self) -> Vec<DistTargetConfigBuilder<'a>> {
collect_targets(self.metadata, "bin")
}
pub fn all_targets(&self, kind: &str) -> Vec<DistTargetConfigBuilder<'a>> {
collect_targets(self.metadata, kind)
}
pub fn binary_by_name(&self, name: &str) -> Result<DistTargetConfigBuilder<'a>> {
DistTargetConfigBuilder::target_by_name(self.metadata, name, "bin")
}
pub fn target_by_name(&self, name: &str, kind: &str) -> Result<DistTargetConfigBuilder<'a>> {
DistTargetConfigBuilder::target_by_name(self.metadata, name, kind)
}
pub fn target(mut self, target: DistTargetConfig<'a>) -> Self {
self.targets.get_or_insert(vec![]).push(target);
self
}
pub fn targets(mut self, targets: impl IntoIterator<Item = DistTargetConfig<'a>>) -> Self {
self.targets.get_or_insert(vec![]).extend(targets);
self
}
#[cfg(feature = "subcommand-dist-build-bin")]
#[cfg_attr(docsrs, doc(cfg(feature = "subcommand-dist-build-bin")))]
pub fn cargo_build_options(
mut self,
options: impl IntoIterator<Item = impl Into<String>>,
) -> Self {
self.cargo_build_options
.extend(options.into_iter().map(Into::into));
self
}
#[cfg(feature = "subcommand-dist-build-license")]
#[cfg_attr(docsrs, doc(cfg(feature = "subcommand-dist-build-license")))]
pub fn license_files(mut self, files: impl IntoIterator<Item = Utf8PathBuf>) -> Self {
let package_root = self.metadata.root_directory();
let files = files.into_iter().map(|file| {
if file.is_relative() {
package_root.join(file)
} else {
file
}
});
match &mut self.license_files {
Some(fs) => fs.extend(files),
lf @ None => *lf = Some(files.collect()),
}
self
}
#[cfg(feature = "subcommand-dist-build-doc")]
#[cfg_attr(docsrs, doc(cfg(feature = "subcommand-dist-build-doc")))]
pub fn documents(mut self, files: impl IntoIterator<Item = Utf8PathBuf>) -> Self {
let package_root = self.metadata.root_directory();
let files = files.into_iter().map(|file| {
if file.is_relative() {
package_root.join(file)
} else {
file
}
});
match &mut self.documents {
Some(ds) => ds.extend(files),
ds @ None => *ds = Some(files.collect()),
}
self
}
pub fn build(self) -> Result<DistPackageConfig<'a>> {
let targets = match self.targets {
Some(targets) => targets,
None => collect_targets(self.metadata, "bin")
.into_iter()
.map(DistTargetConfigBuilder::build)
.collect::<Result<Vec<_>>>()?,
};
Ok(DistPackageConfig {
name: self.name,
metadata: self.metadata,
targets,
#[cfg(feature = "subcommand-dist-build-bin")]
cargo_build_options: self.cargo_build_options,
#[cfg(feature = "subcommand-dist-build-license")]
license_files: collect_license_files(self.metadata, self.license_files)?,
#[cfg(feature = "subcommand-dist-build-doc")]
documents: self.documents.unwrap_or_default(),
})
}
}
#[derive(Debug)]
pub struct DistPackageConfig<'a> {
name: PackageName,
metadata: &'a Package,
targets: Vec<DistTargetConfig<'a>>,
#[cfg(feature = "subcommand-dist-build-bin")]
cargo_build_options: Vec<String>,
#[cfg(feature = "subcommand-dist-build-license")]
license_files: Vec<Utf8PathBuf>,
#[cfg(feature = "subcommand-dist-build-doc")]
documents: Vec<Utf8PathBuf>,
}
impl<'a> DistPackageConfig<'a> {
pub fn name(&self) -> &PackageName {
&self.name
}
pub fn metadata(&self) -> &'a Package {
self.metadata
}
pub fn targets(&'_ self) -> &'_ [DistTargetConfig<'_>] {
&self.targets
}
pub fn root_directory(&self) -> &Utf8Path {
self.metadata.root_directory()
}
#[cfg(feature = "subcommand-dist-build-bin")]
#[cfg_attr(docsrs, doc(cfg(feature = "subcommand-dist-build-bin")))]
pub fn cargo_build_options(&self) -> &[String] {
&self.cargo_build_options
}
#[cfg(feature = "subcommand-dist-build-license")]
#[cfg_attr(docsrs, doc(cfg(feature = "subcommand-dist-build-license")))]
pub fn license_files(&self) -> &[Utf8PathBuf] {
&self.license_files
}
#[cfg(feature = "subcommand-dist-build-doc")]
#[cfg_attr(docsrs, doc(cfg(feature = "subcommand-dist-build-doc")))]
pub fn documents(&self) -> &[Utf8PathBuf] {
&self.documents
}
}
fn collect_targets<'a>(package: &'a Package, kind: &str) -> Vec<DistTargetConfigBuilder<'a>> {
package
.targets
.iter()
.filter(|target| target.kind.iter().any(|x| x == &kind.into()))
.map(DistTargetConfigBuilder::from_metadata)
.collect()
}
#[cfg(feature = "subcommand-dist-build-license")]
fn collect_license_files(
package: &Package,
files: Option<Vec<Utf8PathBuf>>,
) -> Result<Vec<Utf8PathBuf>> {
use std::sync::LazyLock;
use regex::{Regex, RegexBuilder};
let src_dir = package.root_directory();
if let Some(files) = files {
return Ok(files);
}
if let Some(license_file) = &package.license_file {
return Ok(vec![src_dir.join(license_file)]);
}
let mut files = vec![];
for src_entry in src_dir.read_dir_utf8()? {
let src_entry = src_entry?;
if !src_entry.file_type()?.is_file() {
continue;
}
let src_file = src_entry.path();
static RE: LazyLock<Regex> = LazyLock::new(|| {
RegexBuilder::new(r"^LICENSE(?:-|_|\.|$)")
.case_insensitive(true)
.build()
.unwrap()
});
let src_name = match src_file.file_name() {
Some(name) => name,
None => continue,
};
if !RE.is_match(src_name) {
continue;
}
files.push(src_file.to_owned());
}
Ok(files)
}