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)]
21pub enum DefinitionBody {
22    TypeAlias {
23        generics: Vec<Generic>,
24        annotation: Annotation,
25        methods: MethodSignatures,
26    },
27    Enum {
28        generics: Vec<Generic>,
29        variants: Vec<EnumVariant>,
30        methods: MethodSignatures,
31        display: bool,
32    },
33    Struct {
34        generics: Vec<Generic>,
35        fields: Vec<StructFieldDefinition>,
36        kind: StructKind,
37        methods: MethodSignatures,
38        constructor: Option<Type>,
39        display: bool,
40        closed_domain: bool,
41    },
42    Interface {
43        definition: Interface,
44    },
45    Value {
46        allowed_lints: Vec<String>,
47        go_hints: Vec<String>,
48        go_name: Option<String>,
49        /// The known literal value when this definition is a case-eligible
50        /// constant (usable as a Go `case` and as a const-pattern target).
51        /// `None` for variables, functions, and non-literal constants.
52        const_value: Option<Literal>,
53    },
54}
55
56impl Definition {
57    pub fn ty(&self) -> &Type {
58        &self.ty
59    }
60
61    pub fn visibility(&self) -> &Visibility {
62        &self.visibility
63    }
64
65    pub fn name_span(&self) -> Option<Span> {
66        self.name_span
67    }
68
69    pub fn doc(&self) -> Option<&String> {
70        self.doc.as_ref()
71    }
72
73    /// A newtype is a single-field, non-generic tuple struct. Relevant
74    /// because Go compiles newtypes to named scalar types, so `.0` is a cast
75    /// rather than a field access — it cannot be assigned to, and taking
76    /// its address is invalid.
77    pub fn is_newtype(&self) -> bool {
78        matches!(
79            &self.body,
80            DefinitionBody::Struct {
81                kind: StructKind::Tuple,
82                fields,
83                generics,
84                ..
85            } if fields.len() == 1 && generics.is_empty()
86        )
87    }
88
89    pub fn is_pointer_backed_newtype<F>(&self, is_alias: F) -> bool
90    where
91        F: Fn(&str) -> bool,
92    {
93        self.is_newtype()
94            && matches!(
95                &self.body,
96                DefinitionBody::Struct { fields, .. }
97                    if crate::types::peel_alias(&fields[0].ty, is_alias).is_ref()
98            )
99    }
100
101    pub fn allowed_lints(&self) -> &[String] {
102        match &self.body {
103            DefinitionBody::Value { allowed_lints, .. } => allowed_lints,
104            _ => &[],
105        }
106    }
107
108    pub fn go_hints(&self) -> &[String] {
109        match &self.body {
110            DefinitionBody::Value { go_hints, .. } => go_hints,
111            _ => &[],
112        }
113    }
114
115    pub fn go_name(&self) -> Option<&str> {
116        match &self.body {
117            DefinitionBody::Value { go_name, .. } => go_name.as_deref(),
118            _ => None,
119        }
120    }
121
122    pub fn const_value(&self) -> Option<&Literal> {
123        match &self.body {
124            DefinitionBody::Value { const_value, .. } => const_value.as_ref(),
125            _ => None,
126        }
127    }
128
129    pub fn methods_mut(&mut self) -> Option<&mut MethodSignatures> {
130        match &mut self.body {
131            DefinitionBody::Struct { methods, .. } => Some(methods),
132            DefinitionBody::TypeAlias { methods, .. } => Some(methods),
133            DefinitionBody::Enum { methods, .. } => Some(methods),
134            _ => None,
135        }
136    }
137
138    pub fn is_display(&self) -> bool {
139        matches!(
140            &self.body,
141            DefinitionBody::Struct { display: true, .. }
142                | DefinitionBody::Enum { display: true, .. }
143        )
144    }
145
146    pub fn is_closed_domain(&self) -> bool {
147        matches!(
148            &self.body,
149            DefinitionBody::Struct {
150                closed_domain: true,
151                ..
152            }
153        )
154    }
155
156    pub fn is_type_definition(&self) -> bool {
157        matches!(
158            self.body,
159            DefinitionBody::Struct { .. }
160                | DefinitionBody::Enum { .. }
161                | DefinitionBody::TypeAlias { .. }
162        )
163    }
164
165    pub fn is_type_alias(&self) -> bool {
166        matches!(self.body, DefinitionBody::TypeAlias { .. })
167    }
168
169    pub fn is_value(&self, qualified_name: &str) -> bool {
170        matches!(self.body, DefinitionBody::Value { .. })
171            && self.ty.unwrap_forall().get_qualified_id() != Some(qualified_name)
172    }
173}
174
175pub type MethodSignatures = HashMap<EcoString, Type>;
176
177#[derive(Debug, Clone, PartialEq)]
178pub enum Visibility {
179    Public,
180    Private,
181    Local,
182}
183
184impl Visibility {
185    pub fn is_public(&self) -> bool {
186        matches!(self, Visibility::Public)
187    }
188}
189
190#[derive(Debug, Clone, PartialEq)]
191pub struct Interface {
192    pub name: EcoString,
193    pub generics: Vec<Generic>,
194    pub parents: Vec<Type>,
195    pub methods: HashMap<EcoString, Type>,
196}