use std::{collections::BTreeSet, path::Path};
use anyhow::{Context, Result, bail};
use tracing::debug;
use crate::{
changeset::LoadedChange,
config::Config,
output::display_path,
workspace::{Member, Workspace},
};
pub(crate) struct SkipSet {
names: BTreeSet<String>,
}
impl SkipSet {
pub(crate) fn load(
workspace: &Workspace,
config: &Config,
cli_ignore: &[String],
) -> Result<SkipSet> {
let ignore = if config.has_ignore() {
if !cli_ignore.is_empty() {
bail!(
"the --ignore option cannot be used while ignore is defined in .changeset/config.json; use only one of them"
);
}
config.resolve_ignore(workspace.members().iter().map(Member::name))
} else {
for name in cli_ignore {
workspace.member(name).context("invalid `--ignore` value")?;
}
cli_ignore.to_vec()
};
let mut names = BTreeSet::new();
for member in workspace.members() {
if ignore.iter().any(|name| name == member.name()) {
debug!("`{}` is skipped: ignored", member.name());
} else if member.private() && !config.private_packages_version {
debug!("`{}` is skipped: private", member.name());
} else {
continue;
}
names.insert(member.name().to_owned());
}
Ok(SkipSet { names })
}
pub(crate) fn contains(&self, name: &str) -> bool {
self.names.contains(name)
}
pub(crate) fn filter_changes(
&self,
workspace: &Workspace,
changeset_dir: &Path,
changes: &[LoadedChange],
) -> Result<Vec<LoadedChange>> {
for change in changes {
for (name, _) in &change.releases {
workspace
.member(name)
.with_context(|| display_path(&changeset_dir.join(change.rel_path())))?;
}
}
let mut consumed = Vec::new();
for change in changes {
let (skipped, not_skipped): (Vec<&str>, Vec<&str>) = change
.releases
.iter()
.map(|(name, _)| name.as_str())
.partition(|name| self.contains(name));
if skipped.is_empty() {
consumed.push(change.clone());
} else if !not_skipped.is_empty() {
bail!(
"{}: cannot mix skipped packages ({}) and not skipped packages ({})",
display_path(&changeset_dir.join(change.rel_path())),
quote_list(&skipped),
quote_list(¬_skipped)
);
}
}
Ok(consumed)
}
}
fn quote_list(names: &[&str]) -> String {
names
.iter()
.map(|name| format!("`{name}`"))
.collect::<Vec<_>>()
.join(", ")
}