use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct ConstantDefinition {
pub name: String,
pub value: String,
pub file: PathBuf,
pub module_path: String,
pub line: u32,
pub visibility: ConstantVisibility,
pub preliminary_score: i32,
}
impl ConstantDefinition {
pub fn new(name: String, value: String, file: PathBuf, line: u32) -> Self {
Self {
name,
value,
file,
module_path: String::new(),
line,
visibility: ConstantVisibility::Private,
preliminary_score: 0,
}
}
pub fn qualified_key(&self) -> String {
if self.module_path.is_empty() {
format!("{}::{}", self.file.display(), self.name)
} else {
format!("{}::{}::{}", self.file.display(), self.module_path, self.name)
}
}
pub fn with_visibility(mut self, visibility: ConstantVisibility) -> Self {
self.visibility = visibility;
self
}
pub fn with_score(mut self, score: i32) -> Self {
self.preliminary_score = score;
self
}
pub fn with_module_path(mut self, module_path: String) -> Self {
self.module_path = module_path;
self
}
pub fn is_public(&self) -> bool {
matches!(
self.visibility,
ConstantVisibility::Public | ConstantVisibility::PubCrate
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConstantVisibility {
Public,
PubCrate,
PubSuper,
Private,
}
impl ConstantVisibility {
pub fn from_syn(vis: &syn::Visibility) -> Self {
match vis {
syn::Visibility::Public(_) => Self::Public,
syn::Visibility::Restricted(r) => {
let path = r
.path
.segments
.first()
.map(|s| s.ident.to_string())
.unwrap_or_default();
match path.as_str() {
"crate" => Self::PubCrate,
"super" => Self::PubSuper,
_ => Self::PubCrate, }
}
syn::Visibility::Inherited => Self::Private,
}
}
}
impl Default for ConstantVisibility {
fn default() -> Self {
Self::Private
}
}
#[derive(Debug, Clone, Default)]
pub struct RegistryStats {
pub total: usize,
pub public: usize,
pub suspicious: usize,
}
impl RegistryStats {
pub fn public_percentage(&self) -> f64 {
if self.total == 0 {
0.0
} else {
(self.public as f64 / self.total as f64) * 100.0
}
}
pub fn suspicious_percentage(&self) -> f64 {
if self.total == 0 {
0.0
} else {
(self.suspicious as f64 / self.total as f64) * 100.0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_constant_definition() {
let def = ConstantDefinition::new(
"API_KEY".into(),
"secret".into(),
PathBuf::from("src/lib.rs"),
10,
)
.with_visibility(ConstantVisibility::Public)
.with_score(50);
assert_eq!(def.name, "API_KEY");
assert!(def.is_public());
assert_eq!(def.preliminary_score, 50);
}
#[test]
fn test_visibility_from_syn() {
use syn::parse_quote;
let vis: syn::Visibility = parse_quote!(pub);
assert_eq!(ConstantVisibility::from_syn(&vis), ConstantVisibility::Public);
let vis: syn::Visibility = parse_quote!(pub(crate));
assert_eq!(
ConstantVisibility::from_syn(&vis),
ConstantVisibility::PubCrate
);
}
}