1use super::{
2 parse_optional, parse_token, DocComment, Error, FuncType, Ident, InlineInterface, Lookahead,
3 Parse, ParseResult, Peek,
4};
5use crate::lexer::{Lexer, Token};
6use miette::SourceSpan;
7use semver::Version;
8use serde::Serialize;
9
10#[derive(Debug, Clone, Serialize)]
12#[serde(rename_all = "camelCase")]
13pub enum ExternName<'a> {
14 Ident(Ident<'a>),
16 String(super::String<'a>),
18}
19
20impl ExternName<'_> {
21 pub fn span(&self) -> SourceSpan {
23 match self {
24 Self::Ident(ident) => ident.span,
25 Self::String(string) => string.span,
26 }
27 }
28
29 pub fn as_str(&self) -> &str {
31 match self {
32 Self::Ident(ident) => ident.string,
33 Self::String(string) => string.value,
34 }
35 }
36}
37
38impl Peek for ExternName<'_> {
39 fn peek(lookahead: &mut Lookahead) -> bool {
40 Ident::peek(lookahead) || super::String::peek(lookahead)
41 }
42}
43
44impl<'a> Parse<'a> for ExternName<'a> {
45 fn parse(lexer: &mut Lexer<'a>) -> ParseResult<Self> {
46 let mut lookahead = Lookahead::new(lexer);
47 if Ident::peek(&mut lookahead) {
48 Ok(Self::Ident(Parse::parse(lexer)?))
49 } else if super::String::peek(&mut lookahead) {
50 Ok(Self::String(Parse::parse(lexer)?))
51 } else {
52 Err(lookahead.error())
53 }
54 }
55}
56
57#[derive(Debug, Clone, Serialize)]
59#[serde(rename_all = "camelCase")]
60pub struct ImportStatement<'a> {
61 pub docs: Vec<DocComment<'a>>,
63 pub id: Ident<'a>,
65 pub name: Option<ExternName<'a>>,
67 pub ty: ImportType<'a>,
69}
70
71impl<'a> Parse<'a> for ImportStatement<'a> {
72 fn parse(lexer: &mut Lexer<'a>) -> ParseResult<Self> {
73 let docs = Parse::parse(lexer)?;
74 parse_token(lexer, Token::ImportKeyword)?;
75 let id = Parse::parse(lexer)?;
76 let name = parse_optional(lexer, Token::AsKeyword, Parse::parse)?;
77 parse_token(lexer, Token::Colon)?;
78 let ty = Parse::parse(lexer)?;
79 parse_token(lexer, Token::Semicolon)?;
80 Ok(Self { docs, id, name, ty })
81 }
82}
83
84impl Peek for ImportStatement<'_> {
85 fn peek(lookahead: &mut Lookahead) -> bool {
86 lookahead.peek(Token::ImportKeyword)
87 }
88}
89
90#[derive(Debug, Clone, Serialize)]
92#[serde(rename_all = "camelCase")]
93pub enum ImportType<'a> {
94 Package(PackagePath<'a>),
96 Func(FuncType<'a>),
98 Interface(InlineInterface<'a>),
100 Ident(Ident<'a>),
102}
103
104impl<'a> Parse<'a> for ImportType<'a> {
105 fn parse(lexer: &mut Lexer<'a>) -> ParseResult<Self> {
106 let mut lookahead = Lookahead::new(lexer);
107 if FuncType::peek(&mut lookahead) {
108 Ok(Self::Func(Parse::parse(lexer)?))
109 } else if InlineInterface::peek(&mut lookahead) {
110 Ok(Self::Interface(Parse::parse(lexer)?))
111 } else if PackagePath::peek(&mut lookahead) {
112 Ok(Self::Package(Parse::parse(lexer)?))
113 } else if Ident::peek(&mut lookahead) {
114 Ok(Self::Ident(Parse::parse(lexer)?))
115 } else {
116 Err(lookahead.error())
117 }
118 }
119}
120
121#[derive(Debug, Clone, Serialize)]
123#[serde(rename_all = "camelCase")]
124pub struct PackagePath<'a> {
125 pub span: SourceSpan,
127 pub string: &'a str,
129 pub name: &'a str,
131 pub segments: &'a str,
133 pub version: Option<Version>,
135}
136
137impl<'a> PackagePath<'a> {
138 pub fn package_name_span(&self) -> SourceSpan {
140 SourceSpan::new(self.span.offset().into(), self.name.len())
141 }
142
143 pub fn segment_spans<'b>(&'b self) -> impl Iterator<Item = (&'a str, SourceSpan)> + 'b {
145 self.segments.split('/').map(|s| {
146 let start = self.span.offset() + s.as_ptr() as usize - self.name.as_ptr() as usize;
147 (s, SourceSpan::new(start.into(), s.len()))
148 })
149 }
150}
151
152impl<'a> Parse<'a> for PackagePath<'a> {
153 fn parse(lexer: &mut Lexer<'a>) -> ParseResult<Self> {
154 let span = parse_token(lexer, Token::PackagePath)?;
155 let s = lexer.source(span);
156 let slash = s.find('/').unwrap();
157 let at = s.find('@');
158 let name = &s[..slash];
159 let segments = &s[slash + 1..at.unwrap_or(slash + s.len() - name.len())];
160 let version = at
161 .map(|at| {
162 let version = &s[at + 1..];
163 let start = span.offset() + at + 1;
164 version.parse().map_err(|_| Error::InvalidVersion {
165 version: version.to_owned(),
166 span: SourceSpan::new(start.into(), (span.offset() + span.len()) - start),
167 })
168 })
169 .transpose()?;
170
171 Ok(Self {
172 span,
173 string: lexer.source(span),
174 name,
175 segments,
176 version,
177 })
178 }
179}
180
181impl Peek for PackagePath<'_> {
182 fn peek(lookahead: &mut Lookahead) -> bool {
183 lookahead.peek(Token::PackagePath)
184 }
185}
186
187#[derive(Debug, Clone, Serialize)]
189#[serde(rename_all = "camelCase")]
190pub struct PackageName<'a> {
191 pub string: &'a str,
193 pub name: &'a str,
195 pub version: Option<Version>,
197 pub span: SourceSpan,
199}
200
201impl<'a> Parse<'a> for PackageName<'a> {
202 fn parse(lexer: &mut Lexer<'a>) -> ParseResult<Self> {
203 let span = parse_token(lexer, Token::PackageName)?;
204 let s = lexer.source(span);
205 let at = s.find('@');
206 let name = at.map(|at| &s[..at]).unwrap_or(s);
207 let version = at
208 .map(|at| {
209 let version = &s[at + 1..];
210 let start = span.offset() + at + 1;
211 version.parse().map_err(|_| Error::InvalidVersion {
212 version: version.to_string(),
213 span: SourceSpan::new(start.into(), (span.offset() + span.len()) - start),
214 })
215 })
216 .transpose()?;
217 Ok(Self {
218 string: lexer.source(span),
219 name,
220 version,
221 span,
222 })
223 }
224}
225
226#[cfg(test)]
227mod test {
228 use crate::ast::test::roundtrip;
229
230 #[test]
231 fn import_via_package_roundtrip() {
232 roundtrip(
233 "package foo:bar; import x: foo:bar:baz/qux/jam@1.2.3-preview+abc;",
234 "package foo:bar;\n\nimport x: foo:bar:baz/qux/jam@1.2.3-preview+abc;\n",
235 )
236 .unwrap();
237
238 roundtrip(
239 "package foo:bar; import x as \"y\": foo:bar:baz/qux/jam@1.2.3-preview+abc;",
240 "package foo:bar;\n\nimport x as \"y\": foo:bar:baz/qux/jam@1.2.3-preview+abc;\n",
241 )
242 .unwrap();
243 }
244
245 #[test]
246 fn import_function_roundtrip() {
247 roundtrip(
248 "package foo:bar; import x: func(x: string) -> string;",
249 "package foo:bar;\n\nimport x: func(x: string) -> string;\n",
250 )
251 .unwrap();
252
253 roundtrip(
254 "package foo:bar; import x as \"foo\": func(x: string) -> string;",
255 "package foo:bar;\n\nimport x as \"foo\": func(x: string) -> string;\n",
256 )
257 .unwrap();
258 }
259
260 #[test]
261 fn import_interface_roundtrip() {
262 roundtrip(
263 "package foo:bar; import x: interface { x: func(x: string) -> string; };",
264 "package foo:bar;\n\nimport x: interface {\n x: func(x: string) -> string;\n};\n",
265 )
266 .unwrap();
267
268 roundtrip(
269 "package foo:bar; import x as \"foo\": interface { x: func(x: string) -> string; };",
270 "package foo:bar;\n\nimport x as \"foo\": interface {\n x: func(x: string) -> string;\n};\n",
271 )
272 .unwrap();
273 }
274
275 #[test]
276 fn import_via_ident_roundtrip() {
277 roundtrip(
278 "package foo:bar; import x: y;",
279 "package foo:bar;\n\nimport x: y;\n",
280 )
281 .unwrap();
282
283 roundtrip(
284 "package foo:bar; import x /*foo */ as \"foo\": y;",
285 "package foo:bar;\n\nimport x as \"foo\": y;\n",
286 )
287 .unwrap();
288 }
289}