Skip to main content

geam_core/plan/
source.rs

1use camino::Utf8PathBuf;
2use ecow::EcoString;
3use gleam_compiler_core::ast::SrcSpan;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct SourceContext {
7    path: Utf8PathBuf,
8    source: String,
9}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct SourceSpan {
13    start: usize,
14    end: usize,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct PanicSite {
19    module: EcoString,
20    function: EcoString,
21    span: SourceSpan,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct EchoSite {
26    module: EcoString,
27    function: EcoString,
28    span: SourceSpan,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct HostCallSite {
33    module: EcoString,
34    function: EcoString,
35    span: SourceSpan,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub(crate) struct FunctionCallTarget<Function> {
40    function: Function,
41    site: HostCallSite,
42}
43
44impl SourceContext {
45    pub fn new(path: impl Into<Utf8PathBuf>, source: impl Into<String>) -> Self {
46        Self {
47            path: path.into(),
48            source: source.into(),
49        }
50    }
51
52    pub fn path(&self) -> &Utf8PathBuf {
53        &self.path
54    }
55
56    pub fn source(&self) -> &str {
57        &self.source
58    }
59
60    pub(crate) fn named_source(&self) -> miette::NamedSource<String> {
61        miette::NamedSource::new(self.path.as_str(), self.source.clone()).with_language("gleam")
62    }
63}
64
65impl SourceSpan {
66    pub fn new(start: usize, end: usize) -> Self {
67        Self { start, end }
68    }
69
70    pub fn start(&self) -> usize {
71        self.start
72    }
73
74    pub fn end(&self) -> usize {
75        self.end
76    }
77
78    pub fn len(&self) -> usize {
79        self.end.saturating_sub(self.start)
80    }
81
82    pub fn is_empty(&self) -> bool {
83        self.len() == 0
84    }
85
86    pub(crate) fn to_miette(self) -> miette::SourceSpan {
87        (self.start, self.len()).into()
88    }
89}
90
91impl From<SrcSpan> for SourceSpan {
92    fn from(span: SrcSpan) -> Self {
93        Self {
94            start: span.start as usize,
95            end: span.end as usize,
96        }
97    }
98}
99
100impl PanicSite {
101    pub fn new(module: EcoString, function: EcoString, span: SourceSpan) -> Self {
102        Self {
103            module,
104            function,
105            span,
106        }
107    }
108
109    #[cfg(test)]
110    pub(crate) fn unknown() -> Self {
111        Self {
112            module: "<unknown>".into(),
113            function: "<unknown>".into(),
114            span: SourceSpan::new(0, 0),
115        }
116    }
117
118    pub fn module(&self) -> &EcoString {
119        &self.module
120    }
121
122    pub fn function(&self) -> &EcoString {
123        &self.function
124    }
125
126    pub fn span(&self) -> SourceSpan {
127        self.span
128    }
129}
130
131impl EchoSite {
132    pub fn new(module: EcoString, function: EcoString, span: SourceSpan) -> Self {
133        Self {
134            module,
135            function,
136            span,
137        }
138    }
139
140    pub fn module(&self) -> &EcoString {
141        &self.module
142    }
143
144    pub fn function(&self) -> &EcoString {
145        &self.function
146    }
147
148    pub fn span(&self) -> SourceSpan {
149        self.span
150    }
151}
152
153impl HostCallSite {
154    pub fn new(module: EcoString, function: EcoString, span: SourceSpan) -> Self {
155        Self {
156            module,
157            function,
158            span,
159        }
160    }
161
162    pub fn module(&self) -> &EcoString {
163        &self.module
164    }
165
166    pub fn function(&self) -> &EcoString {
167        &self.function
168    }
169
170    pub fn span(&self) -> SourceSpan {
171        self.span
172    }
173
174    #[cfg(test)]
175    pub(crate) fn unknown() -> Self {
176        Self::new(
177            "<unknown>".into(),
178            "<unknown>".into(),
179            SourceSpan::new(0, 0),
180        )
181    }
182}
183
184impl<Function> FunctionCallTarget<Function> {
185    pub(crate) fn new(function: Function, site: HostCallSite) -> Self {
186        Self { function, site }
187    }
188
189    pub(crate) fn function(&self) -> &Function {
190        &self.function
191    }
192
193    pub(crate) fn site(&self) -> &HostCallSite {
194        &self.site
195    }
196}
197
198#[cfg(test)]
199impl From<crate::plan::FunctionInstantiation>
200    for FunctionCallTarget<crate::plan::FunctionInstantiation>
201{
202    fn from(function: crate::plan::FunctionInstantiation) -> Self {
203        Self::new(function, HostCallSite::unknown())
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::{EchoSite, HostCallSite, PanicSite, SourceContext, SourceSpan};
210    use gleam_compiler_core::ast::SrcSpan;
211
212    #[test]
213    fn source_context_preserves_path_and_source() {
214        let context = SourceContext::new("main.gleam", "pub fn main() { 1 }");
215
216        assert_eq!(context.path().as_str(), "main.gleam");
217        assert_eq!(context.source(), "pub fn main() { 1 }");
218        assert_eq!(context.named_source().name(), "main.gleam");
219    }
220
221    #[test]
222    fn source_span_converts_from_gleam_span() {
223        let span = SourceSpan::from(SrcSpan::new(3, 9));
224
225        assert_eq!(span.start(), 3);
226        assert_eq!(span.end(), 9);
227        assert_eq!(span.len(), 6);
228        assert!(!span.is_empty());
229        assert_eq!(span.to_miette(), (3, 6).into());
230
231        let empty = SourceSpan::new(4, 4);
232        assert_eq!(empty.len(), 0);
233        assert!(empty.is_empty());
234    }
235
236    #[test]
237    fn panic_site_preserves_module_function_and_span() {
238        let site = PanicSite::new("main".into(), "run".into(), SourceSpan::new(4, 8));
239
240        assert_eq!(site.module(), "main");
241        assert_eq!(site.function(), "run");
242        assert_eq!(site.span(), SourceSpan::new(4, 8));
243    }
244
245    #[test]
246    fn echo_site_preserves_module_function_and_span() {
247        let site = EchoSite::new("main".into(), "run".into(), SourceSpan::new(4, 8));
248
249        assert_eq!(site.module(), "main");
250        assert_eq!(site.function(), "run");
251        assert_eq!(site.span(), SourceSpan::new(4, 8));
252    }
253
254    #[test]
255    fn host_call_site_preserves_module_function_and_span() {
256        let site = HostCallSite::new("main".into(), "run".into(), SourceSpan::new(4, 8));
257
258        assert_eq!(site.module(), "main");
259        assert_eq!(site.function(), "run");
260        assert_eq!(site.span(), SourceSpan::new(4, 8));
261    }
262}