use crate::syntax::Span;
use std::sync::Arc;
pub type QualifiedName = Arc<str>;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ViewData {
ViewDefinition(ViewDefinition),
ViewUsage(ViewUsage),
ViewpointDefinition(ViewpointDefinition),
ViewpointUsage(ViewpointUsage),
RenderingDefinition(RenderingDefinition),
RenderingUsage(RenderingUsage),
ExposeRelationship(ExposeRelationship),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ViewDefinition {
pub expose: Vec<ExposeRelationship>,
pub filter: Vec<FilterCondition>,
pub rendering: Option<RenderingSpec>,
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ViewUsage {
pub view_def: Option<QualifiedName>,
pub expose: Vec<ExposeRelationship>,
pub filter: Vec<FilterCondition>,
pub rendering: Option<RenderingSpec>,
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ViewpointDefinition {
pub stakeholders: Vec<QualifiedName>,
pub concerns: Vec<QualifiedName>,
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ViewpointUsage {
pub viewpoint_def: Option<QualifiedName>,
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RenderingDefinition {
pub layout: Option<String>,
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RenderingUsage {
pub rendering_def: Option<QualifiedName>,
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ExposeRelationship {
pub import_path: ImportPath,
pub is_recursive: bool,
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ImportPath {
pub target: QualifiedName,
pub wildcard: WildcardKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WildcardKind {
None,
Direct,
Recursive,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum FilterCondition {
Metadata(MetadataFilter),
Expression(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MetadataFilter {
pub annotation: QualifiedName,
pub span: Option<Span>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RenderingSpec {
pub rendering: QualifiedName,
pub span: Option<Span>,
}
impl ViewDefinition {
pub fn new() -> Self {
Self {
expose: Vec::new(),
filter: Vec::new(),
rendering: None,
span: None,
}
}
pub fn add_expose(&mut self, expose: ExposeRelationship) {
self.expose.push(expose);
}
pub fn add_filter(&mut self, filter: FilterCondition) {
self.filter.push(filter);
}
pub fn set_rendering(&mut self, rendering: RenderingSpec) {
self.rendering = Some(rendering);
}
pub fn passes_filters(&self, element_metadata: &[QualifiedName]) -> bool {
if self.filter.is_empty() {
return true;
}
self.filter
.iter()
.all(|filter| filter.matches(element_metadata))
}
pub fn apply<'a, I>(&self, symbols: I) -> Vec<QualifiedName>
where
I: Iterator<Item = (&'a str, &'a [QualifiedName])> + Clone,
{
if self.expose.is_empty() {
return Vec::new();
}
let mut candidates = std::collections::HashSet::new();
let symbol_names: Vec<&str> = symbols.clone().map(|(qn, _)| qn).collect();
for expose_rel in &self.expose {
let resolved = expose_rel.resolve(symbol_names.iter().copied());
candidates.extend(resolved);
}
if self.filter.is_empty() {
candidates.into_iter().collect()
} else {
let symbol_map: std::collections::HashMap<&str, &[QualifiedName]> = symbols.collect();
candidates
.into_iter()
.filter(|qname| {
if let Some(&metadata) = symbol_map.get(qname.as_ref()) {
self.passes_filters(metadata)
} else {
self.passes_filters(&[])
}
})
.collect()
}
}
}
impl Default for ViewDefinition {
fn default() -> Self {
Self::new()
}
}
impl ViewUsage {
pub fn new(view_def: Option<QualifiedName>) -> Self {
Self {
view_def,
expose: Vec::new(),
filter: Vec::new(),
rendering: None,
span: None,
}
}
pub fn add_expose(&mut self, expose: ExposeRelationship) {
self.expose.push(expose);
}
pub fn add_filter(&mut self, filter: FilterCondition) {
self.filter.push(filter);
}
pub fn passes_filters(&self, element_metadata: &[QualifiedName]) -> bool {
if self.filter.is_empty() {
return true;
}
self.filter
.iter()
.all(|filter| filter.matches(element_metadata))
}
pub fn apply<'a, I>(&self, symbols: I) -> Vec<QualifiedName>
where
I: Iterator<Item = (&'a str, &'a [QualifiedName])> + Clone,
{
if self.expose.is_empty() {
return Vec::new();
}
let mut candidates = std::collections::HashSet::new();
let symbol_names: Vec<&str> = symbols.clone().map(|(qn, _)| qn).collect();
for expose_rel in &self.expose {
let resolved = expose_rel.resolve(symbol_names.iter().copied());
candidates.extend(resolved);
}
if self.filter.is_empty() {
candidates.into_iter().collect()
} else {
let symbol_map: std::collections::HashMap<&str, &[QualifiedName]> = symbols.collect();
candidates
.into_iter()
.filter(|qname| {
if let Some(&metadata) = symbol_map.get(qname.as_ref()) {
self.passes_filters(metadata)
} else {
self.passes_filters(&[])
}
})
.collect()
}
}
}
impl ExposeRelationship {
pub fn new(target: QualifiedName, wildcard: WildcardKind) -> Self {
Self {
import_path: ImportPath { target, wildcard },
is_recursive: wildcard == WildcardKind::Recursive,
span: None,
}
}
pub fn from_path(import_path: ImportPath) -> Self {
Self {
is_recursive: import_path.wildcard == WildcardKind::Recursive,
import_path,
span: None,
}
}
pub fn target(&self) -> &QualifiedName {
&self.import_path.target
}
pub fn is_recursive(&self) -> bool {
self.is_recursive
}
pub fn is_namespace(&self) -> bool {
self.import_path.wildcard == WildcardKind::Direct
}
pub fn is_member(&self) -> bool {
self.import_path.wildcard == WildcardKind::None
}
pub fn resolve<'a, I>(&self, all_symbols: I) -> Vec<QualifiedName>
where
I: Iterator<Item = &'a str>,
{
let target_str = self.import_path.target.as_ref();
match self.import_path.wildcard {
WildcardKind::None => {
all_symbols
.filter(|qname| *qname == target_str)
.map(Arc::from)
.collect()
}
WildcardKind::Direct => {
let prefix = format!("{}::", target_str);
all_symbols
.filter(|qname| {
if let Some(rest) = qname.strip_prefix(&prefix) {
!rest.contains("::")
} else {
false
}
})
.map(Arc::from)
.collect()
}
WildcardKind::Recursive => {
let prefix = format!("{}::", target_str);
all_symbols
.filter(|qname| qname.starts_with(&prefix))
.map(Arc::from)
.collect()
}
}
}
}
impl FilterCondition {
pub fn metadata(annotation: QualifiedName) -> Self {
Self::Metadata(MetadataFilter {
annotation,
span: None,
})
}
pub fn expression(expr: String) -> Self {
Self::Expression(expr)
}
pub fn matches(&self, element_metadata: &[QualifiedName]) -> bool {
match self {
FilterCondition::Metadata(meta_filter) => {
element_metadata.iter().any(|annotation| {
**annotation == *meta_filter.annotation
|| annotation.ends_with(&format!("::{}", meta_filter.annotation))
})
}
FilterCondition::Expression(_) => {
true
}
}
}
}