use cpd_core::models::Location;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
pub use cpd_core::deadcode::SymbolKind;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct ModuleId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct SymbolId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct SymbolFlags(u16);
impl SymbolFlags {
pub const NONE: Self = Self(0);
pub const DEFAULT_EXPORT: Self = Self(1 << 0);
pub const TYPE_ONLY: Self = Self(1 << 1);
pub const DECORATED: Self = Self(1 << 2);
pub const MEMBER: Self = Self(1 << 3);
pub const IN_DUNDER_ALL: Self = Self(1 << 4);
pub const MAGIC: Self = Self(1 << 5);
pub const PRIVATE_NAME: Self = Self(1 << 6);
pub const TOP_LEVEL: Self = Self(1 << 7);
pub const OVERRIDE: Self = Self(1 << 8);
pub const ABSTRACT: Self = Self(1 << 9);
pub const RE_EXPORT: Self = Self(1 << 10);
#[inline]
pub const fn contains(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
#[inline]
pub const fn intersects(self, other: Self) -> bool {
self.0 & other.0 != 0
}
#[inline]
pub fn insert(&mut self, other: Self) {
self.0 |= other.0;
}
#[inline]
pub fn set(&mut self, other: Self, on: bool) {
if on {
self.0 |= other.0;
} else {
self.0 &= !other.0;
}
}
#[inline]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
}
impl std::ops::BitOr for SymbolFlags {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl std::ops::BitOrAssign for SymbolFlags {
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
#[derive(Debug, Clone)]
pub struct Module {
pub id: ModuleId,
pub path: String,
pub real_path: PathBuf,
pub format: String,
pub language: &'static str,
pub traits: ModuleTraits,
pub lines: u32,
pub is_entry: bool,
pub is_test: bool,
pub has_dynamic_access: bool,
pub parse_failed: bool,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ModuleTraits {
pub entry_point: bool,
pub package_surface: bool,
pub ambient_declarations: bool,
pub names_are_attributes: bool,
}
#[derive(Debug, Clone)]
pub struct Symbol {
pub id: SymbolId,
pub module: ModuleId,
pub name: String,
pub kind: SymbolKind,
pub flags: SymbolFlags,
pub start: Location,
pub end: Location,
pub exported_as: Option<String>,
pub parent: Option<SymbolId>,
pub local_refs: u32,
pub lines: u32,
}
impl Symbol {
pub fn is_exported(&self) -> bool {
self.exported_as.is_some()
}
pub fn export_name(&self) -> Option<&str> {
self.exported_as.as_deref()
}
}
#[derive(Debug, Clone)]
pub struct Import {
pub module: ModuleId,
pub specifier: String,
pub kind: ImportKind,
pub local: Option<SymbolId>,
pub start: Location,
pub type_only: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ImportKind {
Named(String),
Default,
Namespace,
StarReExport,
SideEffect,
Dynamic,
Glob,
}
#[derive(Debug, Clone)]
pub struct Reference {
pub module: ModuleId,
pub name: String,
pub kind: ReferenceKind,
pub from: Option<SymbolId>,
pub at: Location,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReferenceKind {
Binding,
Member,
String,
}
#[derive(Debug, Default)]
pub struct FileFacts {
pub symbols: Vec<Symbol>,
pub imports: Vec<Import>,
pub references: Vec<Reference>,
pub has_dynamic_access: bool,
pub parse_failed: bool,
}
impl FileFacts {
pub fn unparsed() -> Self {
Self {
parse_failed: true,
..Self::default()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flags_contain_and_combine() {
let mut f = SymbolFlags::TOP_LEVEL;
assert!(f.contains(SymbolFlags::TOP_LEVEL));
assert!(!f.contains(SymbolFlags::MAGIC));
f.insert(SymbolFlags::MAGIC);
assert!(f.contains(SymbolFlags::TOP_LEVEL | SymbolFlags::MAGIC));
assert!(f.intersects(SymbolFlags::MAGIC | SymbolFlags::ABSTRACT));
}
#[test]
fn flags_set_clears_a_bit() {
let mut f = SymbolFlags::TOP_LEVEL | SymbolFlags::PRIVATE_NAME;
f.set(SymbolFlags::PRIVATE_NAME, false);
assert!(!f.contains(SymbolFlags::PRIVATE_NAME));
assert!(f.contains(SymbolFlags::TOP_LEVEL));
}
#[test]
fn empty_flags_are_empty() {
assert!(SymbolFlags::NONE.is_empty());
assert!(!SymbolFlags::MAGIC.is_empty());
}
#[test]
fn member_and_type_kinds() {
assert!(SymbolKind::Method.is_member());
assert!(SymbolKind::EnumMember.is_member());
assert!(!SymbolKind::Function.is_member());
assert!(SymbolKind::TypeAlias.is_type_only());
assert!(!SymbolKind::Class.is_type_only());
}
}