use std::collections::HashMap;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TypeKind {
Primitive,
Record,
Enum,
Function,
Generic,
Tuple,
Array,
Optional,
Reference,
Unknown,
}
impl std::fmt::Display for TypeKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TypeKind::Primitive => write!(f, "primitive"),
TypeKind::Record => write!(f, "record"),
TypeKind::Enum => write!(f, "enum"),
TypeKind::Function => write!(f, "function"),
TypeKind::Generic => write!(f, "generic"),
TypeKind::Tuple => write!(f, "tuple"),
TypeKind::Array => write!(f, "array"),
TypeKind::Optional => write!(f, "optional"),
TypeKind::Reference => write!(f, "reference"),
TypeKind::Unknown => write!(f, "unknown"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FieldInfo {
name: String,
type_name: String,
optional: bool,
mutable: bool,
doc: Option<String>,
default: Option<String>,
}
impl FieldInfo {
pub fn new(name: impl Into<String>, type_name: impl Into<String>) -> Self {
Self {
name: name.into(),
type_name: type_name.into(),
optional: false,
mutable: false,
doc: None,
default: None,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn type_name(&self) -> &str {
&self.type_name
}
pub fn is_optional(&self) -> bool {
self.optional
}
pub fn is_mutable(&self) -> bool {
self.mutable
}
pub fn doc(&self) -> Option<&str> {
self.doc.as_deref()
}
pub fn default(&self) -> Option<&str> {
self.default.as_deref()
}
pub fn optional(mut self) -> Self {
self.optional = true;
self
}
pub fn mutable(mut self) -> Self {
self.mutable = true;
self
}
pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
self.doc = Some(doc.into());
self
}
pub fn with_default(mut self, default: impl Into<String>) -> Self {
self.default = Some(default.into());
self
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MethodInfo {
name: String,
params: Vec<(String, String)>,
return_type: String,
is_static: bool,
is_pure: bool,
doc: Option<String>,
}
impl MethodInfo {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
params: Vec::new(),
return_type: "Void".to_string(),
is_static: false,
is_pure: false,
doc: None,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn params(&self) -> &[(String, String)] {
&self.params
}
pub fn return_type(&self) -> &str {
&self.return_type
}
pub fn is_static(&self) -> bool {
self.is_static
}
pub fn is_pure(&self) -> bool {
self.is_pure
}
pub fn doc(&self) -> Option<&str> {
self.doc.as_deref()
}
pub fn with_param(mut self, name: impl Into<String>, type_name: impl Into<String>) -> Self {
self.params.push((name.into(), type_name.into()));
self
}
pub fn returns(mut self, type_name: impl Into<String>) -> Self {
self.return_type = type_name.into();
self
}
pub fn static_method(mut self) -> Self {
self.is_static = true;
self
}
pub fn pure(mut self) -> Self {
self.is_pure = true;
self
}
pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
self.doc = Some(doc.into());
self
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TypeInfo {
name: String,
kind: TypeKind,
fields: Vec<FieldInfo>,
methods: Vec<MethodInfo>,
type_params: Vec<String>,
parent: Option<String>,
traits: Vec<String>,
doc: Option<String>,
is_public: bool,
}
impl TypeInfo {
pub fn new(name: impl Into<String>, kind: TypeKind) -> Self {
Self {
name: name.into(),
kind,
fields: Vec::new(),
methods: Vec::new(),
type_params: Vec::new(),
parent: None,
traits: Vec::new(),
doc: None,
is_public: true,
}
}
pub fn primitive(name: impl Into<String>) -> Self {
Self::new(name, TypeKind::Primitive)
}
pub fn record(name: impl Into<String>) -> Self {
Self::new(name, TypeKind::Record)
}
pub fn function(name: impl Into<String>) -> Self {
Self::new(name, TypeKind::Function)
}
pub fn generic(name: impl Into<String>) -> Self {
Self::new(name, TypeKind::Generic)
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> TypeKind {
self.kind
}
pub fn fields(&self) -> &[FieldInfo] {
&self.fields
}
pub fn methods(&self) -> &[MethodInfo] {
&self.methods
}
pub fn type_params(&self) -> &[String] {
&self.type_params
}
pub fn parent(&self) -> Option<&str> {
self.parent.as_deref()
}
pub fn traits(&self) -> &[String] {
&self.traits
}
pub fn doc(&self) -> Option<&str> {
self.doc.as_deref()
}
pub fn is_public(&self) -> bool {
self.is_public
}
pub fn field(&self, name: &str) -> Option<&FieldInfo> {
self.fields.iter().find(|f| f.name() == name)
}
pub fn method(&self, name: &str) -> Option<&MethodInfo> {
self.methods.iter().find(|m| m.name() == name)
}
pub fn with_field(mut self, field: FieldInfo) -> Self {
self.fields.push(field);
self
}
pub fn with_method(mut self, method: MethodInfo) -> Self {
self.methods.push(method);
self
}
pub fn with_type_param(mut self, param: impl Into<String>) -> Self {
self.type_params.push(param.into());
self
}
pub fn with_parent(mut self, parent: impl Into<String>) -> Self {
self.parent = Some(parent.into());
self
}
pub fn implements(mut self, trait_name: impl Into<String>) -> Self {
self.traits.push(trait_name.into());
self
}
pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
self.doc = Some(doc.into());
self
}
pub fn private(mut self) -> Self {
self.is_public = false;
self
}
}
#[derive(Debug, Clone, Default)]
pub struct TypeRegistry {
types: HashMap<String, TypeInfo>,
}
impl TypeRegistry {
pub fn new() -> Self {
Self {
types: HashMap::new(),
}
}
pub fn with_primitives() -> Self {
let mut registry = Self::new();
registry.register(TypeInfo::primitive("Void"));
registry.register(TypeInfo::primitive("Bool"));
registry.register(TypeInfo::primitive("Int8"));
registry.register(TypeInfo::primitive("Int16"));
registry.register(TypeInfo::primitive("Int32"));
registry.register(TypeInfo::primitive("Int64"));
registry.register(TypeInfo::primitive("UInt8"));
registry.register(TypeInfo::primitive("UInt16"));
registry.register(TypeInfo::primitive("UInt32"));
registry.register(TypeInfo::primitive("UInt64"));
registry.register(TypeInfo::primitive("Float32"));
registry.register(TypeInfo::primitive("Float64"));
registry.register(TypeInfo::primitive("String"));
registry
}
pub fn is_empty(&self) -> bool {
self.types.is_empty()
}
pub fn len(&self) -> usize {
self.types.len()
}
pub fn register(&mut self, info: TypeInfo) {
self.types.insert(info.name().to_string(), info);
}
pub fn lookup(&self, name: &str) -> Option<&TypeInfo> {
self.types.get(name)
}
pub fn type_names(&self) -> impl Iterator<Item = &str> {
self.types.keys().map(|s| s.as_str())
}
pub fn types(&self) -> impl Iterator<Item = &TypeInfo> {
self.types.values()
}
pub fn contains(&self, name: &str) -> bool {
self.types.contains_key(name)
}
pub fn remove(&mut self, name: &str) -> Option<TypeInfo> {
self.types.remove(name)
}
pub fn clear(&mut self) {
self.types.clear();
}
pub fn implementors(&self, trait_name: &str) -> Vec<&TypeInfo> {
self.types
.values()
.filter(|t| t.traits().contains(&trait_name.to_string()))
.collect()
}
pub fn subtypes(&self, parent_name: &str) -> Vec<&TypeInfo> {
self.types
.values()
.filter(|t| t.parent() == Some(parent_name))
.collect()
}
}
pub fn reflect_type<'a>(registry: &'a TypeRegistry, type_name: &str) -> &'a TypeInfo {
static UNKNOWN: std::sync::LazyLock<TypeInfo> =
std::sync::LazyLock::new(|| TypeInfo::new("Unknown", TypeKind::Unknown));
registry.lookup(type_name).unwrap_or(&UNKNOWN)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_field_info_creation() {
let field = FieldInfo::new("name", "String")
.optional()
.mutable()
.with_doc("User's name")
.with_default("\"unnamed\"");
assert_eq!(field.name(), "name");
assert_eq!(field.type_name(), "String");
assert!(field.is_optional());
assert!(field.is_mutable());
assert_eq!(field.doc(), Some("User's name"));
assert_eq!(field.default(), Some("\"unnamed\""));
}
#[test]
fn test_method_info_creation() {
let method = MethodInfo::new("calculate")
.with_param("x", "Int32")
.with_param("y", "Int32")
.returns("Int32")
.pure()
.with_doc("Calculates sum");
assert_eq!(method.name(), "calculate");
assert_eq!(method.params().len(), 2);
assert_eq!(method.return_type(), "Int32");
assert!(method.is_pure());
assert!(!method.is_static());
assert_eq!(method.doc(), Some("Calculates sum"));
}
#[test]
fn test_method_static() {
let method = MethodInfo::new("create").returns("User").static_method();
assert!(method.is_static());
}
#[test]
fn test_type_info_record() {
let info = TypeInfo::record("User")
.with_field(FieldInfo::new("name", "String"))
.with_field(FieldInfo::new("age", "Int32"))
.with_method(MethodInfo::new("greet").returns("String"))
.with_doc("A user entity");
assert_eq!(info.name(), "User");
assert_eq!(info.kind(), TypeKind::Record);
assert_eq!(info.fields().len(), 2);
assert_eq!(info.methods().len(), 1);
assert!(info.is_public());
assert_eq!(info.doc(), Some("A user entity"));
}
#[test]
fn test_type_info_field_lookup() {
let info = TypeInfo::record("Person")
.with_field(FieldInfo::new("name", "String"))
.with_field(FieldInfo::new("age", "Int32"));
assert!(info.field("name").is_some());
assert_eq!(info.field("name").unwrap().type_name(), "String");
assert!(info.field("nonexistent").is_none());
}
#[test]
fn test_type_info_method_lookup() {
let info = TypeInfo::record("Calculator")
.with_method(MethodInfo::new("add").returns("Int32"))
.with_method(MethodInfo::new("subtract").returns("Int32"));
assert!(info.method("add").is_some());
assert!(info.method("multiply").is_none());
}
#[test]
fn test_type_info_generic() {
let info = TypeInfo::generic("List")
.with_type_param("T")
.with_method(MethodInfo::new("push").with_param("item", "T"));
assert_eq!(info.kind(), TypeKind::Generic);
assert_eq!(info.type_params(), &["T"]);
}
#[test]
fn test_type_info_inheritance() {
let info = TypeInfo::record("Admin")
.with_parent("User")
.implements("Authenticatable")
.implements("Authorizable");
assert_eq!(info.parent(), Some("User"));
assert_eq!(info.traits().len(), 2);
assert!(info.traits().contains(&"Authenticatable".to_string()));
}
#[test]
fn test_type_registry_basic() {
let mut registry = TypeRegistry::new();
assert!(registry.is_empty());
registry.register(TypeInfo::record("User"));
assert!(!registry.is_empty());
assert_eq!(registry.len(), 1);
assert!(registry.contains("User"));
assert!(registry.lookup("User").is_some());
}
#[test]
fn test_type_registry_with_primitives() {
let registry = TypeRegistry::with_primitives();
assert!(registry.lookup("Int32").is_some());
assert!(registry.lookup("String").is_some());
assert!(registry.lookup("Bool").is_some());
assert!(registry.lookup("Float64").is_some());
assert_eq!(
registry.lookup("Int32").unwrap().kind(),
TypeKind::Primitive
);
}
#[test]
fn test_type_registry_implementors() {
let mut registry = TypeRegistry::new();
registry.register(TypeInfo::record("User").implements("Serializable"));
registry.register(TypeInfo::record("Product").implements("Serializable"));
registry.register(TypeInfo::record("Order"));
let implementors = registry.implementors("Serializable");
assert_eq!(implementors.len(), 2);
}
#[test]
fn test_type_registry_subtypes() {
let mut registry = TypeRegistry::new();
registry.register(TypeInfo::record("Entity"));
registry.register(TypeInfo::record("User").with_parent("Entity"));
registry.register(TypeInfo::record("Product").with_parent("Entity"));
registry.register(TypeInfo::record("Order"));
let subtypes = registry.subtypes("Entity");
assert_eq!(subtypes.len(), 2);
}
#[test]
fn test_type_registry_remove() {
let mut registry = TypeRegistry::new();
registry.register(TypeInfo::record("User"));
assert!(registry.contains("User"));
let removed = registry.remove("User");
assert!(removed.is_some());
assert!(!registry.contains("User"));
}
#[test]
fn test_type_registry_clear() {
let mut registry = TypeRegistry::new();
registry.register(TypeInfo::record("User"));
registry.register(TypeInfo::record("Product"));
assert_eq!(registry.len(), 2);
registry.clear();
assert!(registry.is_empty());
}
#[test]
fn test_type_kind_display() {
assert_eq!(format!("{}", TypeKind::Primitive), "primitive");
assert_eq!(format!("{}", TypeKind::Record), "record");
assert_eq!(format!("{}", TypeKind::Enum), "enum");
assert_eq!(format!("{}", TypeKind::Function), "function");
}
#[test]
fn test_reflect_type() {
let mut registry = TypeRegistry::with_primitives();
registry.register(TypeInfo::record("User"));
let info = reflect_type(®istry, "User");
assert_eq!(info.name(), "User");
let unknown = reflect_type(®istry, "NonExistent");
assert_eq!(unknown.kind(), TypeKind::Unknown);
}
#[test]
fn test_type_info_private() {
let info = TypeInfo::record("InternalType").private();
assert!(!info.is_public());
}
}