syn_solidity/item/
import.rs

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