use super::*;
#[derive(Debug, Clone)]
pub struct PackageName {
name: crate::package::PackageName,
}
impl PackageName {
pub fn new(name: crate::package::PackageName) -> Self {
Self { name }
}
}
impl Rule for PackageName {
fn rule_info(&self) -> &'static str {
"Make sure that the protobuf package name matches the buffer package name."
}
fn check_package(&mut self, package: &Package) -> Violations {
let transposed = self.name.to_string().replace('-', "_");
if !is_prefix(&transposed, &package.name) {
let message = violation::Message {
message: format!("package name is {} but should have {} prefix", package.name, transposed),
help: "Make sure the file name matches the package. For example, a package with the name `package.subpackage` should be stored in `proto/package/subpackage.proto`.".into(),
};
return vec![self.to_violation(message)];
}
Violations::default()
}
}
fn is_prefix(prefix: &str, package: &str) -> bool {
prefix
.replace('-', "_")
.split('.')
.zip(package.split('.'))
.all(|(a, b)| a == b)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_check_prefix() {
assert!(is_prefix("abc", "abc"));
assert!(is_prefix("abc", "abc.def"));
assert!(is_prefix("abc", "abc.def.ghi"));
}
#[test]
fn can_fail_wrong_prefix() {
assert!(!is_prefix("abc", "def"));
assert!(!is_prefix("abc", "abcdef"));
assert!(!is_prefix("abc", ""));
assert!(!is_prefix("abc", "ab"));
}
#[test]
fn correct_package_name() {
let package = Package {
name: "my_package".into(),
files: vec!["ignored.proto".into()],
entities: Default::default(),
};
let mut rule = PackageName::new("my-package".parse().unwrap());
assert!(rule.check_package(&package).is_empty());
}
#[test]
fn correct_package_name_submodule() {
let package = Package {
name: "my_package.submodule".into(),
files: vec!["ignored.proto".into()],
entities: Default::default(),
};
let mut rule = PackageName::new("my-package".parse().unwrap());
assert!(rule.check_package(&package).is_empty());
}
#[test]
fn correct_case_transformation() {
let package = Package {
name: "my_package.submodule".into(),
files: vec!["ignored.proto".into()],
entities: Default::default(),
};
let mut rule = PackageName::new("my-package".parse().unwrap());
assert!(rule.check_package(&package).is_empty());
}
#[test]
fn incorrect_package_name() {
let package = Package {
name: "my_package_other".into(),
files: vec!["ignored.proto".into()],
entities: Default::default(),
};
let mut rule = PackageName::new("my-package".parse().unwrap());
assert_eq!(
rule.check_package(&package),
vec![Violation {
rule: "PackageName".into(),
level: Level::Error,
location: Default::default(),
info: rule.rule_info().into(),
message: violation::Message {
message: "package name is my_package_other but should have my_package prefix".into(),
help: "Make sure the file name matches the package. For example, a package with the name `package.subpackage` should be stored in `proto/package/subpackage.proto`.".into(),
}
}]
);
}
}