use std::collections::HashMap;
use ryo_analysis::context::AnalysisContext;
use ryo_analysis::symbol::WorkspaceFilePath;
use ryo_analysis::SymbolId;
use ryo_source::pure::PureFile;
use ryo_spec::comment::{CommentSpec, CommentSpecExtractor};
#[derive(Debug, Default)]
pub struct AllowStore {
specs: HashMap<String, CommentSpec>,
}
impl AllowStore {
pub fn new() -> Self {
Self::default()
}
pub fn from_context(ctx: &AnalysisContext) -> Self {
let mut store = Self::new();
let extractor = CommentSpecExtractor::new();
for (path, file) in ctx.files() {
store.extract_from_file(path, file, &extractor);
}
store
}
fn extract_from_file(
&mut self,
_path: &WorkspaceFilePath,
file: &PureFile,
extractor: &CommentSpecExtractor,
) {
let specs = extractor.extract(file);
for spec in specs {
self.specs.insert(spec.target.clone(), spec);
}
}
pub fn is_allowed(&self, symbol_name: &str, rule_id: &str) -> bool {
if let Some(spec) = self.specs.get(symbol_name) {
spec.is_rule_allowed(rule_id)
} else {
false
}
}
pub fn is_allowed_for_symbols(
&self,
ctx: &AnalysisContext,
symbol_ids: &[SymbolId],
rule_id: &str,
) -> bool {
for &symbol_id in symbol_ids {
if let Some(path) = ctx.registry.path(symbol_id) {
let symbol_name = path.name();
if self.is_allowed(symbol_name, rule_id) {
return true;
}
}
}
false
}
pub fn get(&self, symbol_name: &str) -> Option<&CommentSpec> {
self.specs.get(symbol_name)
}
pub fn len(&self) -> usize {
self.specs.len()
}
pub fn is_empty(&self) -> bool {
self.specs.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
use ryo_source::ItemKind;
use ryo_spec::comment::SpecDirective;
#[test]
fn test_allow_store_basic() {
let mut store = AllowStore::new();
let spec = CommentSpec::new("LegacyConfig".into(), ItemKind::Struct)
.with_directive(SpecDirective::Allow(vec!["RL001".into(), "RL002".into()]));
store.specs.insert("LegacyConfig".into(), spec);
assert!(store.is_allowed("LegacyConfig", "RL001"));
assert!(store.is_allowed("LegacyConfig", "RL002"));
assert!(!store.is_allowed("LegacyConfig", "RL003"));
assert!(!store.is_allowed("OtherStruct", "RL001"));
}
#[test]
fn test_allow_store_wildcard() {
let mut store = AllowStore::new();
let spec = CommentSpec::new("LegacyModule".into(), ItemKind::Mod)
.with_directive(SpecDirective::Allow(vec!["RL*".into()]));
store.specs.insert("LegacyModule".into(), spec);
assert!(store.is_allowed("LegacyModule", "RL001"));
assert!(store.is_allowed("LegacyModule", "RL999"));
assert!(!store.is_allowed("LegacyModule", "PT001"));
}
#[test]
fn test_allow_store_empty() {
let store = AllowStore::new();
assert!(!store.is_allowed("AnyStruct", "RL001"));
assert!(store.is_empty());
}
}