Skip to main content

lisette_syntax/program/
definition.rs

1use rustc_hash::FxHashMap as HashMap;
2
3use ecow::EcoString;
4
5use crate::ast::{
6    Annotation, EnumVariant, Generic, Literal, Span, StructFieldDefinition, StructKind,
7};
8use crate::types::Type;
9
10#[derive(Debug, Clone)]
11pub struct Definition {
12    pub visibility: Visibility,
13    pub ty: Type,
14    pub name: Option<EcoString>,
15    pub name_span: Option<Span>,
16    pub doc: Option<String>,
17    pub body: DefinitionBody,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub enum TypeAttribute {
23    Display,
24    ClosedDomain,
25    AnonStruct,
26    /// Go struct that looks flat but hides an embed bindgen could not emit, so it
27    /// cannot be soundly embedded.
28    HiddenEmbed,
29}
30
31pub type Attributes = HashMap<TypeAttribute, ()>;
32
33#[derive(Debug, Clone)]
34pub enum DefinitionBody {
35    TypeAlias {
36        generics: Vec<Generic>,
37        annotation: Annotation,
38        methods: MethodSignatures,
39        attributes: Attributes,
40    },
41    Enum {
42        generics: Vec<Generic>,
43        variants: Vec<EnumVariant>,
44        methods: MethodSignatures,
45        attributes: Attributes,
46    },
47    Struct {
48        generics: Vec<Generic>,
49        fields: Vec<StructFieldDefinition>,
50        kind: StructKind,
51        methods: MethodSignatures,
52        constructor: Option<Type>,
53        attributes: Attributes,
54    },
55    Interface {
56        definition: Interface,
57    },
58    Value {
59        allowed_lints: Vec<String>,
60        go_hints: Vec<String>,
61        go_name: Option<String>,
62        /// The known literal value when this definition is a case-eligible
63        /// constant (usable as a Go `case` and as a const-pattern target).
64        /// `None` for variables, functions, and non-literal constants.
65        const_value: Option<Literal>,
66    },
67}
68
69impl Definition {
70    pub fn ty(&self) -> &Type {
71        &self.ty
72    }
73
74    pub fn visibility(&self) -> &Visibility {
75        &self.visibility
76    }
77
78    pub fn name_span(&self) -> Option<Span> {
79        self.name_span
80    }
81
82    pub fn doc(&self) -> Option<&String> {
83        self.doc.as_ref()
84    }
85
86    /// A newtype is a single-field, non-generic tuple struct. Relevant
87    /// because Go compiles newtypes to named scalar types, so `.0` is a cast
88    /// rather than a field access — it cannot be assigned to, and taking
89    /// its address is invalid.
90    pub fn is_newtype(&self) -> bool {
91        matches!(
92            &self.body,
93            DefinitionBody::Struct {
94                kind: StructKind::Tuple,
95                fields,
96                generics,
97                ..
98            } if fields.len() == 1 && generics.is_empty()
99        )
100    }
101
102    pub fn is_pointer_backed_newtype<F>(&self, is_alias: F) -> bool
103    where
104        F: Fn(&str) -> bool,
105    {
106        self.is_newtype()
107            && matches!(
108                &self.body,
109                DefinitionBody::Struct { fields, .. }
110                    if crate::types::peel_alias(&fields[0].ty, is_alias).is_ref()
111            )
112    }
113
114    pub fn allowed_lints(&self) -> &[String] {
115        match &self.body {
116            DefinitionBody::Value { allowed_lints, .. } => allowed_lints,
117            _ => &[],
118        }
119    }
120
121    pub fn go_hints(&self) -> &[String] {
122        match &self.body {
123            DefinitionBody::Value { go_hints, .. } => go_hints,
124            _ => &[],
125        }
126    }
127
128    pub fn go_name(&self) -> Option<&str> {
129        match &self.body {
130            DefinitionBody::Value { go_name, .. } => go_name.as_deref(),
131            _ => None,
132        }
133    }
134
135    pub fn const_value(&self) -> Option<&Literal> {
136        match &self.body {
137            DefinitionBody::Value { const_value, .. } => const_value.as_ref(),
138            _ => None,
139        }
140    }
141
142    pub fn methods_mut(&mut self) -> Option<&mut MethodSignatures> {
143        match &mut self.body {
144            DefinitionBody::Struct { methods, .. } => Some(methods),
145            DefinitionBody::TypeAlias { methods, .. } => Some(methods),
146            DefinitionBody::Enum { methods, .. } => Some(methods),
147            _ => None,
148        }
149    }
150
151    pub fn attributes(&self) -> Option<&Attributes> {
152        match &self.body {
153            DefinitionBody::Struct { attributes, .. }
154            | DefinitionBody::Enum { attributes, .. }
155            | DefinitionBody::TypeAlias { attributes, .. } => Some(attributes),
156            _ => None,
157        }
158    }
159
160    pub fn is_display(&self) -> bool {
161        self.attributes()
162            .is_some_and(|a| a.contains_key(&TypeAttribute::Display))
163    }
164
165    pub fn is_closed_domain(&self) -> bool {
166        self.attributes()
167            .is_some_and(|a| a.contains_key(&TypeAttribute::ClosedDomain))
168    }
169
170    pub fn is_anon_struct(&self) -> bool {
171        self.attributes()
172            .is_some_and(|a| a.contains_key(&TypeAttribute::AnonStruct))
173    }
174
175    pub fn has_hidden_embed(&self) -> bool {
176        self.attributes()
177            .is_some_and(|a| a.contains_key(&TypeAttribute::HiddenEmbed))
178    }
179
180    pub fn is_type_definition(&self) -> bool {
181        matches!(
182            self.body,
183            DefinitionBody::Struct { .. }
184                | DefinitionBody::Enum { .. }
185                | DefinitionBody::TypeAlias { .. }
186        )
187    }
188
189    pub fn is_type_alias(&self) -> bool {
190        matches!(self.body, DefinitionBody::TypeAlias { .. })
191    }
192
193    pub fn is_value(&self, qualified_name: &str) -> bool {
194        matches!(self.body, DefinitionBody::Value { .. })
195            && self.ty.unwrap_forall().get_qualified_id() != Some(qualified_name)
196    }
197}
198
199pub type MethodSignatures = HashMap<EcoString, Type>;
200
201#[derive(Debug, Clone, PartialEq)]
202pub enum Visibility {
203    Public,
204    Private,
205    Local,
206}
207
208impl Visibility {
209    pub fn is_public(&self) -> bool {
210        matches!(self, Visibility::Public)
211    }
212}
213
214#[derive(Debug, Clone, PartialEq)]
215pub struct Interface {
216    pub name: EcoString,
217    pub generics: Vec<Generic>,
218    pub parents: Vec<Type>,
219    pub methods: HashMap<EcoString, Type>,
220}