use crate::checker::types::TypeId;
use std::collections::{HashMap, HashSet};
pub fn get_lifecycle_visibility_enum(checker: &crate::checker::Checker) -> Option<TypeId> {
checker.get_std_type("Lifecycle")
}
pub type VisibilityModifiers = HashMap<TypeId, HashSet<TypeId>>;
#[derive(Debug, Default)]
pub struct VisibilityStore {
store: HashMap<TypeId, VisibilityModifiers>,
sealed_properties: HashSet<TypeId>,
sealed_classes: HashMap<TypeId, HashSet<TypeId>>,
default_modifiers: HashMap<TypeId, HashSet<TypeId>>,
program_sealed: bool,
}
impl VisibilityStore {
pub fn new() -> Self {
Self::default()
}
pub fn is_sealed(&self, property_id: TypeId, visibility_class: Option<TypeId>) -> bool {
if self.program_sealed {
return true;
}
if self.sealed_properties.contains(&property_id) {
return true;
}
if let Some(class_id) = visibility_class
&& let Some(sealed) = self.sealed_classes.get(&property_id)
{
return sealed.contains(&class_id);
}
false
}
pub fn seal_visibility(&mut self, property_id: TypeId, visibility_class: Option<TypeId>) {
if let Some(class_id) = visibility_class {
self.sealed_classes
.entry(property_id)
.or_default()
.insert(class_id);
} else {
self.sealed_properties.insert(property_id);
}
}
pub fn seal_program(&mut self) {
self.program_sealed = true;
}
pub fn get_or_initialize(&mut self, property_id: TypeId) -> &mut VisibilityModifiers {
self.store.entry(property_id).or_default()
}
pub fn get_visibility_for_class(
&mut self,
property_id: TypeId,
visibility_class: TypeId,
) -> HashSet<TypeId> {
let modifiers = self.store.entry(property_id).or_default();
modifiers
.get(&visibility_class)
.cloned()
.unwrap_or_else(|| self.get_default_modifier_set(visibility_class))
}
pub fn add_visibility_modifiers(
&mut self,
property_id: TypeId,
class_id: TypeId,
modifiers: &[TypeId],
) -> bool {
if self.is_sealed(property_id, Some(class_id)) {
return false;
}
let vis = self.store.entry(property_id).or_default();
let set = vis.entry(class_id).or_default();
for &m in modifiers {
set.insert(m);
}
true
}
pub fn remove_visibility_modifiers(
&mut self,
property_id: TypeId,
class_id: TypeId,
modifiers: &[TypeId],
) -> bool {
if self.is_sealed(property_id, Some(class_id)) {
return false;
}
let default_set = self.get_default_modifier_set(class_id);
let vis = self.store.entry(property_id).or_default();
let set = vis.entry(class_id).or_insert(default_set);
for &m in modifiers {
set.remove(&m);
}
true
}
fn get_default_modifier_set(&self, visibility_class: TypeId) -> HashSet<TypeId> {
self.default_modifiers
.get(&visibility_class)
.cloned()
.unwrap_or_default()
}
pub fn set_default_modifier_set(
&mut self,
visibility_class: TypeId,
default_set: HashSet<TypeId>,
) {
self.default_modifiers.insert(visibility_class, default_set);
}
pub fn clear_visibility_for_class(&mut self, property_id: TypeId, class_id: TypeId) {
if let Some(modifiers) = self.store.get_mut(&property_id) {
modifiers.remove(&class_id);
}
}
pub fn reset_visibility_for_class(&mut self, property_id: TypeId, class_id: TypeId) {
self.clear_visibility_for_class(property_id, class_id);
if let Some(defaults) = self.default_modifiers.get(&class_id) {
let entry = self.store.entry(property_id).or_default();
entry.insert(class_id, defaults.clone());
}
}
}
#[derive(Debug, Clone, Default)]
pub struct VisibilityFilter {
pub all: Option<HashSet<TypeId>>,
pub any: Option<HashSet<TypeId>>,
pub none: Option<HashSet<TypeId>>,
}
pub fn is_visible(
store: &mut VisibilityStore,
property_id: TypeId,
filter: &VisibilityFilter,
) -> bool {
if let Some(ref all) = filter.all {
for &modifier_id in all {
if let Some(modifiers) = store.store.get(&property_id) {
let found = modifiers.values().any(|set| set.contains(&modifier_id));
if !found {
return false;
}
} else {
return false;
}
}
}
if let Some(ref none) = filter.none {
for &modifier_id in none {
if let Some(modifiers) = store.store.get(&property_id) {
let found = modifiers.values().any(|set| set.contains(&modifier_id));
if found {
return false;
}
}
}
}
if let Some(ref any) = filter.any {
if any.is_empty() {
return false;
}
let mut found_any = false;
for &modifier_id in any {
if let Some(modifiers) = store.store.get(&property_id)
&& modifiers.values().any(|set| set.contains(&modifier_id))
{
found_any = true;
break;
}
}
if !found_any {
return false;
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_visibility_store_seal_property() {
let mut store = VisibilityStore::new();
assert!(!store.is_sealed(1, None));
store.seal_visibility(1, None);
assert!(store.is_sealed(1, None));
}
#[test]
fn test_visibility_store_seal_class() {
let mut store = VisibilityStore::new();
assert!(!store.is_sealed(1, Some(10)));
store.seal_visibility(1, Some(10));
assert!(store.is_sealed(1, Some(10)));
assert!(!store.is_sealed(1, Some(20)));
}
#[test]
fn test_visibility_store_seal_program() {
let mut store = VisibilityStore::new();
store.seal_program();
assert!(store.is_sealed(1, None));
assert!(store.is_sealed(1, Some(10)));
}
#[test]
fn test_add_visibility_modifiers() {
let mut store = VisibilityStore::new();
assert!(store.add_visibility_modifiers(1, 10, &[100, 101]));
let vis = store.get_visibility_for_class(1, 10);
assert!(vis.contains(&100));
assert!(vis.contains(&101));
}
#[test]
fn test_add_visibility_when_sealed() {
let mut store = VisibilityStore::new();
store.seal_visibility(1, Some(10));
assert!(!store.add_visibility_modifiers(1, 10, &[100]));
}
#[test]
fn test_remove_visibility_modifiers() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100, 101]);
assert!(store.remove_visibility_modifiers(1, 10, &[100]));
let vis = store.get_visibility_for_class(1, 10);
assert!(!vis.contains(&100));
assert!(vis.contains(&101));
}
#[test]
fn test_default_modifier_set() {
let mut store = VisibilityStore::new();
store.set_default_modifier_set(10, HashSet::from([100, 101, 102]));
let vis = store.get_visibility_for_class(1, 10);
assert_eq!(vis.len(), 3);
}
#[test]
fn test_is_visible_with_all_filter() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100, 101]);
let filter = VisibilityFilter {
all: Some(HashSet::from([100, 101])),
..Default::default()
};
assert!(is_visible(&mut store, 1, &filter));
}
#[test]
fn test_is_visible_with_all_filter_missing() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100]);
let filter = VisibilityFilter {
all: Some(HashSet::from([100, 101])),
..Default::default()
};
assert!(!is_visible(&mut store, 1, &filter));
}
#[test]
fn test_is_visible_with_any_filter() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100]);
let filter = VisibilityFilter {
any: Some(HashSet::from([100, 101])),
..Default::default()
};
assert!(is_visible(&mut store, 1, &filter));
}
#[test]
fn test_is_visible_with_any_filter_empty() {
let mut store = VisibilityStore::new();
let filter = VisibilityFilter {
any: Some(HashSet::new()),
..Default::default()
};
assert!(!is_visible(&mut store, 1, &filter));
}
#[test]
fn test_is_visible_with_none_filter() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100, 101]);
let filter = VisibilityFilter {
none: Some(HashSet::from([102])),
..Default::default()
};
assert!(is_visible(&mut store, 1, &filter));
}
#[test]
fn test_is_visible_with_none_filter_excluded() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100]);
let filter = VisibilityFilter {
none: Some(HashSet::from([100])),
..Default::default()
};
assert!(!is_visible(&mut store, 1, &filter));
}
#[test]
fn test_default_visibility_no_modifiers() {
let mut store = VisibilityStore::new();
let filter = VisibilityFilter {
any: Some(HashSet::from([100])),
..Default::default()
};
assert!(!is_visible(&mut store, 1, &filter));
}
#[test]
fn test_visibility_multiple_classes() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100]);
store.add_visibility_modifiers(1, 20, &[200]);
let vis_10 = store.get_visibility_for_class(1, 10);
assert!(vis_10.contains(&100));
let vis_20 = store.get_visibility_for_class(1, 20);
assert!(vis_20.contains(&200));
}
#[test]
fn test_visibility_add_same_modifier_twice() {
let mut store = VisibilityStore::new();
assert!(store.add_visibility_modifiers(1, 10, &[100]));
assert!(store.add_visibility_modifiers(1, 10, &[100]));
let vis = store.get_visibility_for_class(1, 10);
assert!(vis.contains(&100));
}
#[test]
fn test_visibility_remove_nonexistent() {
let mut store = VisibilityStore::new();
assert!(store.remove_visibility_modifiers(1, 10, &[100]));
}
#[test]
fn test_visibility_clear_modifiers() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100, 101]);
store.clear_visibility_for_class(1, 10);
let vis = store.get_visibility_for_class(1, 10);
assert!(vis.is_empty());
}
#[test]
fn test_visibility_reset_modifiers() {
let mut store = VisibilityStore::new();
store.set_default_modifier_set(10, HashSet::from([100, 101]));
store.add_visibility_modifiers(1, 10, &[200]);
store.reset_visibility_for_class(1, 10);
let vis = store.get_visibility_for_class(1, 10);
assert!(vis.contains(&100));
assert!(vis.contains(&101));
assert!(!vis.contains(&200));
}
#[test]
fn test_visibility_seal_then_add_fails() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100]);
store.seal_visibility(1, Some(10));
assert!(!store.add_visibility_modifiers(1, 10, &[101]));
let vis = store.get_visibility_for_class(1, 10);
assert!(vis.contains(&100));
assert!(!vis.contains(&101));
}
#[test]
fn test_visibility_seal_program_then_add_fails() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100]);
store.seal_program();
assert!(!store.add_visibility_modifiers(2, 10, &[100]));
}
#[test]
fn test_visibility_remove_when_sealed_fails() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100]);
store.seal_visibility(1, Some(10));
assert!(!store.remove_visibility_modifiers(1, 10, &[100]));
}
#[test]
fn test_is_visible_combined_filters() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100, 101]);
let filter = VisibilityFilter {
all: Some(HashSet::from([100])),
any: Some(HashSet::from([101])),
..Default::default()
};
assert!(is_visible(&mut store, 1, &filter));
}
#[test]
fn test_is_visible_combined_filters_fails() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100]);
let filter = VisibilityFilter {
all: Some(HashSet::from([100, 101])), any: Some(HashSet::from([100])),
..Default::default()
};
assert!(!is_visible(&mut store, 1, &filter));
}
#[test]
fn test_is_visible_no_filters() {
let mut store = VisibilityStore::new();
let filter = VisibilityFilter::default();
assert!(is_visible(&mut store, 1, &filter));
}
#[test]
fn test_visibility_default_modifiers_applied_to_new_property() {
let mut store = VisibilityStore::new();
store.set_default_modifier_set(10, HashSet::from([100, 101]));
let vis = store.get_visibility_for_class(1, 10);
assert!(vis.contains(&100));
assert!(vis.contains(&101));
}
#[test]
fn test_visibility_preserves_other_classes() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100]);
store.add_visibility_modifiers(1, 20, &[200]);
store.clear_visibility_for_class(1, 10);
assert!(store.get_visibility_for_class(1, 10).is_empty());
assert!(store.get_visibility_for_class(1, 20).contains(&200));
}
#[test]
fn test_visibility_multiple_properties() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100]);
store.add_visibility_modifiers(2, 10, &[101]);
let filter = VisibilityFilter {
any: Some(HashSet::from([100])),
..Default::default()
};
assert!(is_visible(&mut store, 1, &filter));
assert!(!is_visible(&mut store, 2, &filter));
}
#[test]
fn test_visibility_none_filter_with_multiple_classes() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100]);
store.add_visibility_modifiers(1, 20, &[200]);
let filter = VisibilityFilter {
none: Some(HashSet::from([100])),
..Default::default()
};
assert!(!is_visible(&mut store, 1, &filter));
}
#[test]
fn test_visibility_all_filter_with_multiple_classes() {
let mut store = VisibilityStore::new();
store.add_visibility_modifiers(1, 10, &[100]);
store.add_visibility_modifiers(1, 20, &[200]);
let filter = VisibilityFilter {
all: Some(HashSet::from([100, 200])),
..Default::default()
};
assert!(is_visible(&mut store, 1, &filter));
}
#[test]
fn test_get_lifecycle_visibility_enum() {
use crate::parser::parse;
let result = parse("");
let mut checker = crate::checker::Checker::new();
checker.set_parse_result(result.root_id, result.builder);
checker.check_program();
let result = get_lifecycle_visibility_enum(&checker);
let _ = result;
}
}