use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Request {
pub id: u64,
pub crate_name: String,
pub query: Query,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Query {
ListItems,
GetType { path: String },
GetTraitImpls { type_path: String },
GetInherentImpls { type_path: String },
GetFields { type_path: String },
GetLayout { type_path: String },
GetTraits,
GetTrait { path: String },
FindTypes { pattern: String },
ResolveAlias { path: String },
CheckImpl {
type_path: String,
trait_path: String,
},
GetImplementors { trait_path: String },
Ping,
Shutdown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Response {
pub id: u64,
pub result: QueryResult,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "status", rename_all = "snake_case")]
pub enum QueryResult {
Success { data: QueryData },
Error { message: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum QueryData {
Items { items: Vec<ItemInfo> },
TypeInfo(TypeDetails),
TraitImpls { impls: Vec<TraitImplDetails> },
InherentImpls { impls: Vec<InherentImplDetails> },
Fields { fields: Vec<FieldInfo> },
Layout(LayoutInfo),
Traits { traits: Vec<TraitInfo> },
TraitDetails(TraitDetails),
Types { types: Vec<TypeSummary> },
ResolvedType {
original: String,
resolved: String,
chain: Vec<String>,
},
ImplCheck {
implements: bool,
impl_info: Option<TraitImplDetails>,
},
Implementors { types: Vec<TypeSummary> },
Pong,
ShuttingDown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ItemInfo {
pub name: String,
pub path: String,
pub kind: ItemKind,
pub visibility: Visibility,
pub span: Option<SpanInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum ItemKind {
Struct,
Enum,
Union,
Trait,
Function,
Const,
Static,
TypeAlias,
Impl,
Mod,
Use,
ExternCrate,
Macro,
TraitAlias,
Other(String),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Visibility {
Public,
Crate,
Restricted { path: String },
Private,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpanInfo {
pub file: String,
pub start_line: u32,
pub start_col: u32,
pub end_line: u32,
pub end_col: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypeSummary {
pub name: String,
pub path: String,
pub kind: TypeKind,
pub generics: Vec<GenericParam>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypeDetails {
pub name: String,
pub path: String,
pub kind: TypeKind,
pub visibility: Visibility,
pub generics: Vec<GenericParam>,
pub where_clause: Option<String>,
pub docs: Option<String>,
pub attributes: Vec<String>,
pub fields: Option<Vec<FieldInfo>>,
pub variants: Option<Vec<EnumVariantInfo>>,
pub trait_impls: Vec<String>,
pub inherent_methods: Vec<MethodSummary>,
pub layout: Option<LayoutInfo>,
pub source: Option<String>,
pub span: Option<SpanInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum TypeKind {
Struct,
Enum,
Union,
Trait,
TypeAlias,
Primitive,
Tuple,
Array,
Slice,
Reference,
Pointer,
Function,
Closure,
Opaque,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenericParam {
pub name: String,
pub kind: GenericParamKind,
pub bounds: Vec<String>,
pub default: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum GenericParamKind {
Lifetime,
Type,
Const { ty: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldInfo {
pub name: Option<String>,
pub index: usize,
pub ty: String,
pub resolved_ty: Option<String>,
pub visibility: Visibility,
pub docs: Option<String>,
pub attributes: Vec<String>,
pub offset: Option<usize>,
pub size: Option<usize>,
pub span: Option<SpanInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnumVariantInfo {
pub name: String,
pub index: usize,
pub fields: Vec<FieldInfo>,
pub discriminant: Option<String>,
pub docs: Option<String>,
pub attributes: Vec<String>,
pub span: Option<SpanInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LayoutInfo {
pub size: usize,
pub align: usize,
pub field_offsets: Option<Vec<FieldLayoutInfo>>,
pub variants: Option<Vec<VariantLayoutInfo>>,
pub is_sized: bool,
pub is_copy: bool,
pub is_send: bool,
pub is_sync: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldLayoutInfo {
pub name: Option<String>,
pub index: usize,
pub offset: usize,
pub size: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VariantLayoutInfo {
pub name: String,
pub discriminant: Option<i128>,
pub fields: Vec<FieldLayoutInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraitImplDetails {
pub self_ty: String,
pub trait_path: String,
pub generics: Vec<GenericParam>,
pub where_clause: Option<String>,
pub is_negative: bool,
pub is_unsafe: bool,
pub methods: Vec<MethodDetails>,
pub assoc_types: Vec<AssocTypeInfo>,
pub assoc_consts: Vec<AssocConstInfo>,
pub source: Option<String>,
pub span: Option<SpanInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InherentImplDetails {
pub self_ty: String,
pub generics: Vec<GenericParam>,
pub where_clause: Option<String>,
pub is_unsafe: bool,
pub methods: Vec<MethodDetails>,
pub assoc_consts: Vec<AssocConstInfo>,
pub assoc_types: Vec<AssocTypeInfo>,
pub source: Option<String>,
pub span: Option<SpanInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MethodSummary {
pub name: String,
pub path: String,
pub signature: String,
pub is_unsafe: bool,
pub is_const: bool,
pub is_async: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MethodDetails {
pub name: String,
pub path: String,
pub signature: String,
pub parsed_signature: FunctionSignature,
pub has_body: bool,
pub body_source: Option<String>,
pub body_tokens: Option<Vec<Token>>,
pub is_unsafe: bool,
pub is_const: bool,
pub is_async: bool,
pub docs: Option<String>,
pub attributes: Vec<String>,
pub span: Option<SpanInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionSignature {
pub receiver: Option<ReceiverInfo>,
pub params: Vec<ParamInfo>,
pub return_ty: Option<String>,
pub generics: Vec<GenericParam>,
pub where_clause: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReceiverInfo {
pub kind: String,
pub is_mut: bool,
pub is_ref: bool,
pub lifetime: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParamInfo {
pub name: String,
pub ty: String,
pub is_mut: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssocTypeInfo {
pub name: String,
pub ty: Option<String>,
pub bounds: Vec<String>,
pub default: Option<String>,
pub docs: Option<String>,
pub span: Option<SpanInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssocConstInfo {
pub name: String,
pub ty: String,
pub value: Option<String>,
pub docs: Option<String>,
pub span: Option<SpanInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraitInfo {
pub name: String,
pub path: String,
pub generics: Vec<GenericParam>,
pub required_methods: usize,
pub provided_methods: usize,
pub supertraits: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraitDetails {
pub name: String,
pub path: String,
pub visibility: Visibility,
pub generics: Vec<GenericParam>,
pub where_clause: Option<String>,
pub is_auto: bool,
pub is_unsafe: bool,
pub supertraits: Vec<String>,
pub methods: Vec<TraitMethodInfo>,
pub assoc_types: Vec<AssocTypeInfo>,
pub assoc_consts: Vec<AssocConstInfo>,
pub docs: Option<String>,
pub attributes: Vec<String>,
pub source: Option<String>,
pub implementors: Vec<String>,
pub span: Option<SpanInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraitMethodInfo {
pub name: String,
pub signature: String,
pub parsed_signature: FunctionSignature,
pub has_default: bool,
pub default_body: Option<String>,
pub is_unsafe: bool,
pub docs: Option<String>,
pub attributes: Vec<String>,
pub span: Option<SpanInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "token_type", rename_all = "snake_case")]
pub enum Token {
Ident { name: String },
Literal { kind: LiteralKind, value: String },
Punct { ch: char },
Keyword { name: String },
Group {
delimiter: Delimiter,
tokens: Vec<Token>,
},
Path { segments: Vec<String> },
MethodCall {
receiver: Box<Token>,
method: String,
args: Vec<Token>,
},
FnCall { path: Vec<String>, args: Vec<Token> },
FieldAccess { base: Box<Token>, field: String },
BinOp {
lhs: Box<Token>,
op: String,
rhs: Box<Token>,
},
UnaryOp { op: String, expr: Box<Token> },
If {
cond: Box<Token>,
then_branch: Vec<Token>,
else_branch: Option<Vec<Token>>,
},
Match {
expr: Box<Token>,
arms: Vec<MatchArm>,
},
Let {
pattern: String,
ty: Option<String>,
init: Option<Box<Token>>,
},
Return { expr: Option<Box<Token>> },
Block { stmts: Vec<Token> },
Closure {
params: Vec<String>,
body: Box<Token>,
},
Raw { source: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MatchArm {
pub pattern: String,
pub guard: Option<String>,
pub body: Vec<Token>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum LiteralKind {
String,
ByteString,
Char,
Byte,
Int,
Float,
Bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Delimiter {
Paren,
Bracket,
Brace,
None,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CrateTypeInfo {
pub crate_name: String,
pub crate_version: Option<String>,
pub items: Vec<ItemInfo>,
pub types: HashMap<String, TypeDetails>,
pub traits: HashMap<String, TraitDetails>,
pub trait_impls: HashMap<String, Vec<TraitImplDetails>>,
pub inherent_impls: HashMap<String, Vec<InherentImplDetails>>,
pub type_aliases: HashMap<String, TypeAliasInfo>,
pub layouts: HashMap<String, LayoutInfo>,
pub modules: HashMap<String, ModuleInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypeAliasInfo {
pub name: String,
pub path: String,
pub generics: Vec<GenericParam>,
pub ty: String,
pub resolved_ty: String,
pub visibility: Visibility,
pub docs: Option<String>,
pub span: Option<SpanInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleInfo {
pub name: String,
pub path: String,
pub visibility: Visibility,
pub items: Vec<String>,
pub reexports: Vec<ReexportInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReexportInfo {
pub name: String,
pub original_path: String,
pub visibility: Visibility,
}
pub fn default_socket_path() -> std::path::PathBuf {
std::env::temp_dir().join("bronzite.sock")
}
pub fn socket_path_for_workspace(workspace_root: &std::path::Path) -> std::path::PathBuf {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
workspace_root.hash(&mut hasher);
let hash = hasher.finish();
std::env::temp_dir().join(format!("bronzite-{:x}.sock", hash))
}
pub fn path_matches_pattern(path: &str, pattern: &str) -> bool {
let pattern = pattern.trim();
let path = path.trim();
if pattern.ends_with("::**") {
let prefix = &pattern[..pattern.len() - 4];
return path == prefix || path.starts_with(&format!("{}::", prefix));
}
if pattern.ends_with("::*") {
let prefix = &pattern[..pattern.len() - 3];
if !path.starts_with(&format!("{}::", prefix)) {
return false;
}
let suffix = &path[prefix.len() + 2..];
return !suffix.contains("::");
}
if pattern.contains('*') {
let parts: Vec<&str> = pattern.split('*').collect();
if parts.len() != 2 {
return false; }
return path.starts_with(parts[0]) && path.ends_with(parts[1]);
}
path == pattern
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_path_matching() {
assert!(path_matches_pattern("foo::Bar", "foo::Bar"));
assert!(!path_matches_pattern("foo::Baz", "foo::Bar"));
assert!(path_matches_pattern("foo::Bar", "foo::*"));
assert!(path_matches_pattern("foo::Baz", "foo::*"));
assert!(!path_matches_pattern("foo::bar::Baz", "foo::*"));
assert!(path_matches_pattern("foo::Bar", "foo::**"));
assert!(path_matches_pattern("foo::bar::Baz", "foo::**"));
assert!(path_matches_pattern("foo::bar::baz::Qux", "foo::**"));
assert!(!path_matches_pattern("bar::Baz", "foo::**"));
assert!(path_matches_pattern("foo::BarBaz", "foo::Bar*"));
assert!(path_matches_pattern("foo::Bar", "foo::Bar*"));
assert!(!path_matches_pattern("foo::Baz", "foo::Bar*"));
}
#[test]
fn test_query_serialization() {
let query = Query::CheckImpl {
type_path: "Foo".to_string(),
trait_path: "MyTrait".to_string(),
};
let json = serde_json::to_string(&query).unwrap();
let parsed: Query = serde_json::from_str(&json).unwrap();
match parsed {
Query::CheckImpl {
type_path,
trait_path,
} => {
assert_eq!(type_path, "Foo");
assert_eq!(trait_path, "MyTrait");
}
_ => panic!("Wrong query type"),
}
}
#[test]
fn test_response_serialization() {
let response = Response {
id: 42,
result: QueryResult::Success {
data: QueryData::ImplCheck {
implements: true,
impl_info: None,
},
},
};
let json = serde_json::to_string(&response).unwrap();
let parsed: Response = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.id, 42);
}
}