use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnifyConfig {
#[serde(default = "default_include_paths")]
pub include_paths: bool,
#[serde(default)]
pub include_renamed: bool,
#[serde(default)]
pub pin_transitives: bool,
#[serde(default = "default_transitive_host")]
pub transitive_host: TransitiveFeatureHost,
#[serde(default)]
pub exclude: Vec<String>,
#[serde(default)]
pub include: Vec<String>,
#[serde(default = "default_max_backups")]
pub max_backups: usize,
#[serde(default = "default_true")]
pub msrv: bool,
#[serde(default)]
pub enforce_msrv_inheritance: bool,
#[serde(default)]
pub msrv_source: MsrvSource,
#[serde(default = "default_true")]
pub prune_dead_features: bool,
#[serde(default)]
pub preserve_features: Vec<String>,
#[serde(default = "default_true")]
pub strict_version_compat: bool,
#[serde(default)]
pub exact_pin_handling: ExactPinHandling,
#[serde(default)]
pub major_version_conflict: MajorVersionConflict,
#[serde(default = "default_true")]
pub detect_unused: bool,
#[serde(default = "default_true")]
pub compiler_diag_cache: bool,
#[serde(default = "default_true")]
pub remove_unused: bool,
#[serde(default = "default_true")]
pub detect_undeclared_features: bool,
#[serde(default = "default_true")]
pub fix_undeclared_features: bool,
#[serde(default = "default_skip_undeclared_patterns")]
pub skip_undeclared_patterns: Vec<String>,
#[serde(default = "default_true")]
pub sort_dependencies: bool,
}
impl Default for UnifyConfig {
fn default() -> Self {
Self {
include_paths: default_include_paths(),
include_renamed: false,
pin_transitives: false,
transitive_host: default_transitive_host(),
exclude: Vec::new(),
include: Vec::new(),
max_backups: default_max_backups(),
msrv: true,
enforce_msrv_inheritance: false,
msrv_source: MsrvSource::default(),
prune_dead_features: true,
preserve_features: Vec::new(),
strict_version_compat: true,
exact_pin_handling: ExactPinHandling::default(),
major_version_conflict: MajorVersionConflict::default(),
detect_unused: true,
compiler_diag_cache: true,
remove_unused: true,
detect_undeclared_features: true,
fix_undeclared_features: true,
skip_undeclared_patterns: default_skip_undeclared_patterns(),
sort_dependencies: true,
}
}
}
impl UnifyConfig {
pub fn should_exclude(&self, dep_name: &str) -> bool {
self.exclude.iter().any(|e| e == dep_name)
}
pub fn should_include(&self, dep_name: &str) -> bool {
self.include.iter().any(|i| i == dep_name)
}
pub fn should_preserve_feature(&self, feature_name: &str) -> bool {
self.preserve_features.iter().any(|pattern| {
if pattern.contains('*') || pattern.contains('?') || pattern.contains('[') {
glob::Pattern::new(pattern)
.map(|p| p.matches(feature_name))
.unwrap_or(false)
} else {
pattern == feature_name
}
})
}
pub fn should_skip_undeclared_feature(&self, feature_name: &str) -> bool {
self.skip_undeclared_patterns.iter().any(|pattern| {
if pattern.contains('*') || pattern.contains('?') || pattern.contains('[') {
glob::Pattern::new(pattern)
.map(|p| p.matches(feature_name))
.unwrap_or(false)
} else {
pattern == feature_name
}
})
}
pub fn validate(&self, workspace_root: &std::path::Path) -> Result<(), crate::error::ConfigError> {
if self.pin_transitives
&& let TransitiveFeatureHost::Path(p) = &self.transitive_host
{
if p.contains("..") {
return Err(crate::error::ConfigError::InvalidValue {
field: "unify.transitive_host".to_string(),
message: format!("path '{}' contains '..' traversal, which is not allowed", p),
});
}
if std::path::Path::new(p).is_absolute() {
return Err(crate::error::ConfigError::InvalidValue {
field: "unify.transitive_host".to_string(),
message: format!("path '{}' is absolute, must be relative to workspace root", p),
});
}
let full_path = workspace_root.join(p);
if !full_path.exists() {
return Err(crate::error::ConfigError::InvalidValue {
field: "unify.transitive_host".to_string(),
message: format!("path '{}' does not exist", p),
});
}
let cargo_toml = full_path.join("Cargo.toml");
if !cargo_toml.exists() {
return Err(crate::error::ConfigError::InvalidValue {
field: "unify.transitive_host".to_string(),
message: format!("path '{}' does not contain a Cargo.toml", p),
});
}
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MsrvSource {
Deps,
Workspace,
#[default]
Max,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ExactPinHandling {
Skip,
Preserve,
#[default]
Warn,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MajorVersionConflict {
#[default]
Warn,
Bump,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub enum TransitiveFeatureHost {
#[default]
Root,
Path(String),
}
impl Serialize for TransitiveFeatureHost {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
TransitiveFeatureHost::Root => serializer.serialize_str("root"),
TransitiveFeatureHost::Path(path) => serializer.serialize_str(path),
}
}
}
impl<'de> Deserialize<'de> for TransitiveFeatureHost {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct TransitiveFeatureHostVisitor;
impl serde::de::Visitor<'_> for TransitiveFeatureHostVisitor {
type Value = TransitiveFeatureHost;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("'root' or a path string")
}
fn visit_str<E>(self, value: &str) -> Result<TransitiveFeatureHost, E>
where
E: serde::de::Error,
{
match value {
"root" => Ok(TransitiveFeatureHost::Root),
path => Ok(TransitiveFeatureHost::Path(path.to_string())),
}
}
}
deserializer.deserialize_any(TransitiveFeatureHostVisitor)
}
}
fn default_max_backups() -> usize {
3
}
fn default_include_paths() -> bool {
true
}
fn default_transitive_host() -> TransitiveFeatureHost {
TransitiveFeatureHost::Root
}
pub(crate) fn default_true() -> bool {
true
}
fn default_skip_undeclared_patterns() -> Vec<String> {
const PATTERNS: &[&str] = &["default", "std", "alloc", "*_backend", "*_impl"];
PATTERNS.iter().map(|&s| String::from(s)).collect()
}
#[test]
fn test_transitive_feature_host_path() {
let toml = r#"
include_paths = true
include_renamed = false
pin_transitives = false
transitive_host = "path/to/crate"
exclude = []
include = []
"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert_eq!(
config.transitive_host,
TransitiveFeatureHost::Path("path/to/crate".to_string())
);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unify_config_defaults() {
let config = UnifyConfig::default();
assert!(config.include_paths); assert!(!config.include_renamed); assert!(!config.pin_transitives); assert_eq!(config.transitive_host, TransitiveFeatureHost::Root);
assert!(config.exclude.is_empty());
assert!(config.include.is_empty());
assert!(config.msrv); assert!(config.detect_unused); assert!(config.compiler_diag_cache); assert!(config.remove_unused); }
#[test]
fn test_unify_config_should_exclude() {
let config = UnifyConfig {
exclude: vec!["tokio".to_string(), "serde".to_string()],
..Default::default()
};
assert!(config.should_exclude("tokio"));
assert!(config.should_exclude("serde"));
assert!(!config.should_exclude("regex"));
}
#[test]
fn test_unify_config_should_include() {
let config = UnifyConfig {
include: vec!["special-dep".to_string()],
..Default::default()
};
assert!(config.should_include("special-dep"));
assert!(!config.should_include("normal-dep"));
}
#[test]
fn test_transitive_feature_host_in_full_config() {
let toml = r#"
include_paths = true
include_renamed = false
pin_transitives = true
transitive_host = "root"
exclude = []
include = []
"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert_eq!(config.transitive_host, TransitiveFeatureHost::Root);
assert!(config.include_paths);
assert!(config.pin_transitives);
}
#[test]
fn test_unify_config_default_transitive_host() {
let config = UnifyConfig::default();
assert_eq!(config.transitive_host, TransitiveFeatureHost::Root);
assert!(!config.pin_transitives); }
#[test]
fn test_prune_dead_features_default() {
let config = UnifyConfig::default();
assert!(config.prune_dead_features); }
#[test]
fn test_prune_dead_features_parsing() {
let toml = r#"prune_dead_features = true"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(config.prune_dead_features);
let toml = r#"prune_dead_features = false"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(!config.prune_dead_features);
}
#[test]
fn test_strict_version_compat_default() {
let config = UnifyConfig::default();
assert!(config.strict_version_compat); }
#[test]
fn test_exact_pin_handling_default() {
let config = UnifyConfig::default();
assert_eq!(config.exact_pin_handling, ExactPinHandling::Warn);
}
#[test]
fn test_exact_pin_handling_parsing() {
let toml = r#"exact_pin_handling = "skip""#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert_eq!(config.exact_pin_handling, ExactPinHandling::Skip);
let toml = r#"exact_pin_handling = "preserve""#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert_eq!(config.exact_pin_handling, ExactPinHandling::Preserve);
let toml = r#"exact_pin_handling = "warn""#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert_eq!(config.exact_pin_handling, ExactPinHandling::Warn);
}
#[test]
fn test_detect_unused_default() {
let config = UnifyConfig::default();
assert!(config.detect_unused); assert!(config.compiler_diag_cache); assert!(config.remove_unused); }
#[test]
fn test_new_config_options_parsing() {
let toml = r#"
strict_version_compat = false
exact_pin_handling = "preserve"
detect_unused = true
compiler_diag_cache = false
remove_unused = true
"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(!config.strict_version_compat);
assert_eq!(config.exact_pin_handling, ExactPinHandling::Preserve);
assert!(config.detect_unused);
assert!(!config.compiler_diag_cache);
assert!(config.remove_unused);
}
#[test]
fn test_major_version_conflict_default() {
let config = UnifyConfig::default();
assert_eq!(config.major_version_conflict, MajorVersionConflict::Warn);
}
#[test]
fn test_major_version_conflict_parsing() {
let toml = r#"major_version_conflict = "warn""#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert_eq!(config.major_version_conflict, MajorVersionConflict::Warn);
let toml = r#"major_version_conflict = "bump""#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert_eq!(config.major_version_conflict, MajorVersionConflict::Bump);
}
#[test]
fn test_major_version_conflict_with_other_options() {
let toml = r#"
strict_version_compat = false
exact_pin_handling = "preserve"
major_version_conflict = "bump"
"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(!config.strict_version_compat);
assert_eq!(config.exact_pin_handling, ExactPinHandling::Preserve);
assert_eq!(config.major_version_conflict, MajorVersionConflict::Bump);
}
#[test]
fn test_transitive_host_validate_root() {
let config = UnifyConfig {
pin_transitives: true,
transitive_host: TransitiveFeatureHost::Root,
..Default::default()
};
let workspace = std::env::current_dir().unwrap();
assert!(config.validate(&workspace).is_ok());
}
#[test]
fn test_transitive_host_validate_valid_path() {
let config = UnifyConfig {
pin_transitives: false,
transitive_host: TransitiveFeatureHost::Path("src".to_string()),
..Default::default()
};
let workspace = std::env::current_dir().unwrap();
assert!(config.validate(&workspace).is_ok());
}
#[test]
fn test_transitive_host_validate_nonexistent_path() {
let config = UnifyConfig {
pin_transitives: true,
transitive_host: TransitiveFeatureHost::Path("nonexistent/path".to_string()),
..Default::default()
};
let workspace = std::env::current_dir().unwrap();
let result = config.validate(&workspace);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(err, crate::error::ConfigError::InvalidValue { .. }));
}
#[test]
fn test_transitive_host_validate_path_traversal() {
let config = UnifyConfig {
pin_transitives: true,
transitive_host: TransitiveFeatureHost::Path("../somewhere".to_string()),
..Default::default()
};
let workspace = std::env::current_dir().unwrap();
let result = config.validate(&workspace);
assert!(result.is_err());
let err = result.unwrap_err();
if let crate::error::ConfigError::InvalidValue { message, .. } = err {
assert!(message.contains(".."));
} else {
panic!("Expected InvalidValue error");
}
}
#[test]
fn test_preserve_features_default() {
let config = UnifyConfig::default();
assert!(config.preserve_features.is_empty());
}
#[test]
fn test_preserve_features_parsing() {
let toml = r#"
preserve_features = ["future-api", "unstable-*", "bench*"]
"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert_eq!(config.preserve_features.len(), 3);
assert!(config.preserve_features.contains(&"future-api".to_string()));
assert!(config.preserve_features.contains(&"unstable-*".to_string()));
assert!(config.preserve_features.contains(&"bench*".to_string()));
}
#[test]
fn test_should_preserve_feature_exact_match() {
let config = UnifyConfig {
preserve_features: vec!["future-api".to_string(), "experimental".to_string()],
..Default::default()
};
assert!(config.should_preserve_feature("future-api"));
assert!(config.should_preserve_feature("experimental"));
assert!(!config.should_preserve_feature("other-feature"));
}
#[test]
fn test_should_preserve_feature_glob_wildcard() {
let config = UnifyConfig {
preserve_features: vec!["unstable-*".to_string()],
..Default::default()
};
assert!(config.should_preserve_feature("unstable-api"));
assert!(config.should_preserve_feature("unstable-feature"));
assert!(config.should_preserve_feature("unstable-"));
assert!(!config.should_preserve_feature("unstable")); assert!(!config.should_preserve_feature("stable-api"));
}
#[test]
fn test_should_preserve_feature_glob_suffix() {
let config = UnifyConfig {
preserve_features: vec!["bench*".to_string()],
..Default::default()
};
assert!(config.should_preserve_feature("bench"));
assert!(config.should_preserve_feature("benchmark"));
assert!(config.should_preserve_feature("benchmarks"));
assert!(!config.should_preserve_feature("prebench"));
}
#[test]
fn test_should_preserve_feature_glob_question_mark() {
let config = UnifyConfig {
preserve_features: vec!["test-?".to_string()],
..Default::default()
};
assert!(config.should_preserve_feature("test-a"));
assert!(config.should_preserve_feature("test-1"));
assert!(!config.should_preserve_feature("test-ab")); assert!(!config.should_preserve_feature("test-")); }
#[test]
fn test_should_preserve_feature_multiple_patterns() {
let config = UnifyConfig {
preserve_features: vec!["future-api".to_string(), "unstable-*".to_string(), "bench*".to_string()],
..Default::default()
};
assert!(config.should_preserve_feature("future-api"));
assert!(config.should_preserve_feature("unstable-feature"));
assert!(config.should_preserve_feature("benchmark"));
assert!(!config.should_preserve_feature("stable-api"));
assert!(!config.should_preserve_feature("other"));
}
#[test]
fn test_should_preserve_feature_empty_list() {
let config = UnifyConfig::default();
assert!(!config.should_preserve_feature("any-feature"));
}
#[test]
fn test_msrv_source_default() {
let config = UnifyConfig::default();
assert_eq!(config.msrv_source, MsrvSource::Max);
}
#[test]
fn test_msrv_source_parsing_deps() {
let toml = r#"msrv_source = "deps""#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert_eq!(config.msrv_source, MsrvSource::Deps);
}
#[test]
fn test_msrv_source_parsing_workspace() {
let toml = r#"msrv_source = "workspace""#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert_eq!(config.msrv_source, MsrvSource::Workspace);
}
#[test]
fn test_msrv_source_parsing_max() {
let toml = r#"msrv_source = "max""#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert_eq!(config.msrv_source, MsrvSource::Max);
}
#[test]
fn test_msrv_source_with_msrv_enabled() {
let toml = r#"
msrv = true
msrv_source = "workspace"
"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(config.msrv);
assert_eq!(config.msrv_source, MsrvSource::Workspace);
}
#[test]
fn test_msrv_source_with_msrv_disabled() {
let toml = r#"
msrv = false
msrv_source = "deps"
"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(!config.msrv);
assert_eq!(config.msrv_source, MsrvSource::Deps);
}
#[test]
fn test_detect_undeclared_features_default() {
let config = UnifyConfig::default();
assert!(config.detect_undeclared_features); }
#[test]
fn test_fix_undeclared_features_default() {
let config = UnifyConfig::default();
assert!(config.fix_undeclared_features); }
#[test]
fn test_detect_undeclared_features_parsing_true() {
let toml = r#"detect_undeclared_features = true"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(config.detect_undeclared_features);
}
#[test]
fn test_detect_undeclared_features_parsing_false() {
let toml = r#"detect_undeclared_features = false"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(!config.detect_undeclared_features);
}
#[test]
fn test_fix_undeclared_features_parsing_true() {
let toml = r#"fix_undeclared_features = true"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(config.fix_undeclared_features);
}
#[test]
fn test_fix_undeclared_features_parsing_false() {
let toml = r#"fix_undeclared_features = false"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(!config.fix_undeclared_features);
}
#[test]
fn test_undeclared_features_both_options() {
let toml = r#"
detect_undeclared_features = true
fix_undeclared_features = false
"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(config.detect_undeclared_features);
assert!(!config.fix_undeclared_features);
}
#[test]
fn test_undeclared_features_with_other_options() {
let toml = r#"
detect_unused = true
remove_unused = true
detect_undeclared_features = true
fix_undeclared_features = true
prune_dead_features = false
"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(config.detect_unused);
assert!(config.remove_unused);
assert!(config.detect_undeclared_features);
assert!(config.fix_undeclared_features);
assert!(!config.prune_dead_features);
}
#[test]
fn test_undeclared_features_detect_disabled_fix_enabled() {
let toml = r#"
detect_undeclared_features = false
fix_undeclared_features = true
"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(!config.detect_undeclared_features);
assert!(config.fix_undeclared_features);
}
#[test]
fn test_skip_undeclared_patterns_default() {
let config = UnifyConfig::default();
assert!(!config.skip_undeclared_patterns.is_empty());
assert!(config.skip_undeclared_patterns.contains(&"default".to_string()));
assert!(config.skip_undeclared_patterns.contains(&"std".to_string()));
assert!(config.skip_undeclared_patterns.contains(&"alloc".to_string()));
assert!(config.skip_undeclared_patterns.contains(&"*_backend".to_string()));
assert!(config.skip_undeclared_patterns.contains(&"*_impl".to_string()));
}
#[test]
fn test_skip_undeclared_patterns_parsing() {
let toml = r#"
skip_undeclared_patterns = ["default", "std", "custom-*"]
"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert_eq!(config.skip_undeclared_patterns.len(), 3);
assert!(config.skip_undeclared_patterns.contains(&"default".to_string()));
assert!(config.skip_undeclared_patterns.contains(&"std".to_string()));
assert!(config.skip_undeclared_patterns.contains(&"custom-*".to_string()));
}
#[test]
fn test_skip_undeclared_patterns_empty() {
let toml = r#"
skip_undeclared_patterns = []
"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(config.skip_undeclared_patterns.is_empty());
}
#[test]
fn test_should_skip_undeclared_feature_exact_match() {
let config = UnifyConfig {
skip_undeclared_patterns: vec!["default".to_string(), "std".to_string()],
..Default::default()
};
assert!(config.should_skip_undeclared_feature("default"));
assert!(config.should_skip_undeclared_feature("std"));
assert!(!config.should_skip_undeclared_feature("derive"));
}
#[test]
fn test_should_skip_undeclared_feature_glob_suffix() {
let config = UnifyConfig {
skip_undeclared_patterns: vec!["*_backend".to_string()],
..Default::default()
};
assert!(config.should_skip_undeclared_feature("sqlite_backend"));
assert!(config.should_skip_undeclared_feature("postgres_backend"));
assert!(config.should_skip_undeclared_feature("_backend")); assert!(!config.should_skip_undeclared_feature("backend"));
assert!(!config.should_skip_undeclared_feature("backend_"));
}
#[test]
fn test_should_skip_undeclared_feature_glob_prefix() {
let config = UnifyConfig {
skip_undeclared_patterns: vec!["unstable-*".to_string()],
..Default::default()
};
assert!(config.should_skip_undeclared_feature("unstable-api"));
assert!(config.should_skip_undeclared_feature("unstable-internal"));
assert!(config.should_skip_undeclared_feature("unstable-")); assert!(!config.should_skip_undeclared_feature("unstable"));
}
#[test]
fn test_should_skip_undeclared_feature_glob_question_mark() {
let config = UnifyConfig {
skip_undeclared_patterns: vec!["test-?".to_string()],
..Default::default()
};
assert!(config.should_skip_undeclared_feature("test-1"));
assert!(config.should_skip_undeclared_feature("test-a"));
assert!(!config.should_skip_undeclared_feature("test-12"));
assert!(!config.should_skip_undeclared_feature("test-"));
}
#[test]
fn test_should_skip_undeclared_feature_multiple_patterns() {
let config = UnifyConfig {
skip_undeclared_patterns: vec![
"default".to_string(),
"std".to_string(),
"*_backend".to_string(),
"*_impl".to_string(),
],
..Default::default()
};
assert!(config.should_skip_undeclared_feature("default"));
assert!(config.should_skip_undeclared_feature("std"));
assert!(config.should_skip_undeclared_feature("sqlite_backend"));
assert!(config.should_skip_undeclared_feature("sync_impl"));
assert!(!config.should_skip_undeclared_feature("derive"));
assert!(!config.should_skip_undeclared_feature("serde"));
}
#[test]
fn test_should_skip_undeclared_feature_empty_patterns() {
let config = UnifyConfig {
skip_undeclared_patterns: vec![],
..Default::default()
};
assert!(!config.should_skip_undeclared_feature("default"));
assert!(!config.should_skip_undeclared_feature("std"));
assert!(!config.should_skip_undeclared_feature("anything"));
}
#[test]
fn test_sort_dependencies_default() {
let config = UnifyConfig::default();
assert!(config.sort_dependencies); }
#[test]
fn test_sort_dependencies_parsing_true() {
let toml = r#"sort_dependencies = true"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(config.sort_dependencies);
}
#[test]
fn test_sort_dependencies_parsing_false() {
let toml = r#"sort_dependencies = false"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(!config.sort_dependencies);
}
#[test]
fn test_sort_dependencies_with_other_options() {
let toml = r#"
detect_unused = true
remove_unused = true
sort_dependencies = false
"#;
let config: UnifyConfig = toml_edit::de::from_str(toml).unwrap();
assert!(config.detect_unused);
assert!(config.remove_unused);
assert!(!config.sort_dependencies);
}
}