syn_solidity/item/
import.rs

1use crate::{LitStr, SolIdent, Spanned, kw};
2use proc_macro2::Span;
3use std::fmt;
4use syn::{
5    Result, Token, braced,
6    parse::{Parse, ParseStream},
7    punctuated::Punctuated,
8    token::Brace,
9};
10
11/// An import directive: `import "foo.sol";`.
12///
13/// Solidity reference:
14/// <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.importDirective>
15#[derive(Clone)]
16pub struct ImportDirective {
17    pub import_token: kw::import,
18    pub path: ImportPath,
19    pub semi_token: Token![;],
20}
21
22impl fmt::Display for ImportDirective {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        write!(f, "import {};", self.path)
25    }
26}
27
28impl fmt::Debug for ImportDirective {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        f.debug_struct("ImportDirective").field("path", &self.path).finish()
31    }
32}
33
34impl Parse for ImportDirective {
35    fn parse(input: ParseStream<'_>) -> Result<Self> {
36        Ok(Self { import_token: input.parse()?, path: input.parse()?, semi_token: input.parse()? })
37    }
38}
39
40impl Spanned for ImportDirective {
41    fn span(&self) -> Span {
42        let span = self.import_token.span;
43        span.join(self.semi_token.span).unwrap_or(span)
44    }
45
46    fn set_span(&mut self, span: Span) {
47        self.import_token.span = span;
48        self.path.set_span(span);
49        self.semi_token.span = span;
50    }
51}
52
53/// The path of an import directive.
54#[derive(Clone, Debug)]
55pub enum ImportPath {
56    /// A plain import directive: `import "foo.sol" as Foo;`.
57    Plain(ImportPlain),
58    /// A list of import aliases: `import { Foo as Bar, Baz } from "foo.sol";`.
59    Aliases(ImportAliases),
60    /// A glob import directive: `import * as Foo from "foo.sol";`.
61    Glob(ImportGlob),
62}
63
64impl fmt::Display for ImportPath {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        match self {
67            Self::Plain(p) => p.fmt(f),
68            Self::Aliases(p) => p.fmt(f),
69            Self::Glob(p) => p.fmt(f),
70        }
71    }
72}
73
74impl Parse for ImportPath {
75    fn parse(input: ParseStream<'_>) -> Result<Self> {
76        let lookahead = input.lookahead1();
77        if lookahead.peek(Token![*]) {
78            input.parse().map(Self::Glob)
79        } else if lookahead.peek(Brace) {
80            input.parse().map(Self::Aliases)
81        } else {
82            input.parse().map(Self::Plain)
83        }
84    }
85}
86
87impl Spanned for ImportPath {
88    fn span(&self) -> Span {
89        match self {
90            Self::Plain(p) => p.span(),
91            Self::Aliases(p) => p.span(),
92            Self::Glob(p) => p.span(),
93        }
94    }
95
96    fn set_span(&mut self, span: Span) {
97        match self {
98            Self::Plain(p) => p.set_span(span),
99            Self::Aliases(p) => p.set_span(span),
100            Self::Glob(p) => p.set_span(span),
101        }
102    }
103}
104
105impl ImportPath {
106    pub fn path(&self) -> &LitStr {
107        match self {
108            Self::Plain(ImportPlain { path, .. })
109            | Self::Aliases(ImportAliases { path, .. })
110            | Self::Glob(ImportGlob { path, .. }) => path,
111        }
112    }
113
114    pub fn path_mut(&mut self) -> &mut LitStr {
115        match self {
116            Self::Plain(ImportPlain { path, .. })
117            | Self::Aliases(ImportAliases { path, .. })
118            | Self::Glob(ImportGlob { path, .. }) => path,
119        }
120    }
121}
122
123/// An import alias.
124#[derive(Clone)]
125pub struct ImportAlias {
126    pub as_token: Token![as],
127    pub alias: SolIdent,
128}
129
130impl fmt::Display for ImportAlias {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        write!(f, "as {}", self.alias)
133    }
134}
135
136impl fmt::Debug for ImportAlias {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        f.debug_tuple("Alias").field(&self.alias).finish()
139    }
140}
141
142impl Parse for ImportAlias {
143    fn parse(input: ParseStream<'_>) -> Result<Self> {
144        Ok(Self { as_token: input.parse()?, alias: input.parse()? })
145    }
146}
147
148impl Spanned for ImportAlias {
149    fn span(&self) -> Span {
150        let span = self.as_token.span;
151        span.join(self.alias.span()).unwrap_or(span)
152    }
153
154    fn set_span(&mut self, span: Span) {
155        self.as_token.span = span;
156        self.alias.set_span(span);
157    }
158}
159
160impl ImportAlias {
161    pub fn parse_opt(input: ParseStream<'_>) -> Result<Option<Self>> {
162        if input.peek(Token![as]) { input.parse().map(Some) } else { Ok(None) }
163    }
164}
165
166/// A plain import directive: `import "foo.sol" as Foo;`.
167#[derive(Clone)]
168pub struct ImportPlain {
169    pub path: LitStr,
170    pub alias: Option<ImportAlias>,
171}
172
173impl fmt::Display for ImportPlain {
174    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175        write!(f, "{}", self.path)?;
176        if let Some(alias) = &self.alias {
177            write!(f, " {alias}")?;
178        }
179        Ok(())
180    }
181}
182
183impl fmt::Debug for ImportPlain {
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        f.debug_struct("Plain").field("path", &self.path).field("alias", &self.alias).finish()
186    }
187}
188
189impl Parse for ImportPlain {
190    fn parse(input: ParseStream<'_>) -> Result<Self> {
191        Ok(Self { path: input.parse()?, alias: input.call(ImportAlias::parse_opt)? })
192    }
193}
194
195impl Spanned for ImportPlain {
196    fn span(&self) -> Span {
197        let span = self.path.span();
198        if let Some(alias) = &self.alias { span.join(alias.span()).unwrap_or(span) } else { span }
199    }
200
201    fn set_span(&mut self, span: Span) {
202        self.path.set_span(span);
203        if let Some(alias) = &mut self.alias {
204            alias.set_span(span);
205        }
206    }
207}
208
209/// A list of import aliases: `{ Foo as Bar, Baz } from "foo.sol"`.
210#[derive(Clone)]
211pub struct ImportAliases {
212    pub brace_token: Brace,
213    pub imports: Punctuated<(SolIdent, Option<ImportAlias>), Token![,]>,
214    pub from_token: kw::from,
215    pub path: LitStr,
216}
217
218impl fmt::Display for ImportAliases {
219    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220        write!(f, "{{")?;
221        for (i, (ident, alias)) in self.imports.iter().enumerate() {
222            if i > 0 {
223                write!(f, ", ")?;
224            }
225            write!(f, "{ident}")?;
226            if let Some(alias) = alias {
227                write!(f, " {alias}")?;
228            }
229        }
230        write!(f, "}} from {}", self.path)
231    }
232}
233
234impl fmt::Debug for ImportAliases {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        f.debug_struct("Aliases").field("imports", &self.imports).field("path", &self.path).finish()
237    }
238}
239
240impl Parse for ImportAliases {
241    fn parse(input: ParseStream<'_>) -> Result<Self> {
242        let content;
243        Ok(Self {
244            brace_token: braced!(content in input),
245            imports: content.parse_terminated(
246                |c| Ok((c.parse()?, c.call(ImportAlias::parse_opt)?)),
247                Token![,],
248            )?,
249            from_token: input.parse()?,
250            path: input.parse()?,
251        })
252    }
253}
254
255impl Spanned for ImportAliases {
256    fn span(&self) -> Span {
257        let span = self.brace_token.span.join();
258        span.join(self.path.span()).unwrap_or(span)
259    }
260
261    fn set_span(&mut self, span: Span) {
262        self.brace_token = Brace(span);
263        self.from_token.span = span;
264        self.path.set_span(span);
265    }
266}
267
268/// A glob import directive: `* as Foo from "foo.sol"`.
269#[derive(Clone)]
270pub struct ImportGlob {
271    pub star_token: Token![*],
272    pub alias: Option<ImportAlias>,
273    pub from_token: kw::from,
274    pub path: LitStr,
275}
276
277impl fmt::Display for ImportGlob {
278    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279        write!(f, "*")?;
280        if let Some(alias) = &self.alias {
281            write!(f, " {alias}")?;
282        }
283        write!(f, " from {}", self.path)
284    }
285}
286
287impl fmt::Debug for ImportGlob {
288    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289        f.debug_struct("Glob").field("alias", &self.alias).field("path", &self.path).finish()
290    }
291}
292
293impl Parse for ImportGlob {
294    fn parse(input: ParseStream<'_>) -> Result<Self> {
295        Ok(Self {
296            star_token: input.parse()?,
297            alias: input.call(ImportAlias::parse_opt)?,
298            from_token: input.parse()?,
299            path: input.parse()?,
300        })
301    }
302}
303
304impl Spanned for ImportGlob {
305    fn span(&self) -> Span {
306        let span = self.star_token.span;
307        span.join(self.path.span()).unwrap_or(span)
308    }
309
310    fn set_span(&mut self, span: Span) {
311        self.star_token.span = span;
312        self.alias.set_span(span);
313        self.from_token.span = span;
314        self.path.set_span(span);
315    }
316}