4 use proc_macro
::{Delimiter, TokenStream, TokenTree}
;
6 use proc_macro2
::{Delimiter, TokenStream, TokenTree}
;
10 type Result
<T
, E
= Error
> = std
::result
::Result
<T
, E
>;
13 pub(crate) struct Error
{
17 impl std
::error
::Error
for Error {}
19 impl std
::fmt
::Display
for Error
{
20 fn fmt(&self, f
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
21 write
!(f
, "{}", self.msg
)
25 pub(crate) fn parse(ts
: TokenStream
) -> Result
<ast
::XFlags
> {
26 let mut p
= Parser
::new(ts
);
30 macro_rules
! format_err
{
32 Error { msg: format!($($tt)*) }
39 return Err(format_err
!($
($tt
)*))
43 fn xflags(p
: &mut Parser
) -> Result
<ast
::XFlags
> {
44 let src
= if p
.eat_keyword("src") { Some(p.expect_string()?) }
else { None }
;
45 let doc
= opt_doc(p
)?
;
46 let mut cmd
= cmd(p
)?
;
48 let res
= ast
::XFlags { src, cmd }
;
52 fn cmd(p
: &mut Parser
) -> Result
<ast
::Cmd
> {
53 p
.expect_keyword("cmd")?
;
55 let name
= cmd_name(p
)?
;
56 let mut res
= ast
::Cmd
{
61 subcommands
: Vec
::new(),
65 while !p
.at_delim(Delimiter
::Brace
) {
66 let doc
= opt_doc(p
)?
;
67 let arity
= arity(p
)?
;
70 let arg
= ast
::Arg { arity, doc, val }
;
73 None
=> bail
!("expected ident"),
77 p
.enter_delim(Delimiter
::Brace
)?
;
79 let doc
= opt_doc(p
)?
;
80 let default = p
.eat_keyword("default");
81 if default || p
.at_keyword("cmd") {
82 let mut cmd
= cmd(p
)?
;
84 res
.subcommands
.push(cmd
);
87 bail
!("only one subcommand can be default")
90 res
.subcommands
.rotate_right(1);
93 let mut flag
= flag(p
)?
;
102 fn flag(p
: &mut Parser
) -> Result
<ast
::Flag
> {
103 let arity
= arity(p
)?
;
105 let mut short
= None
;
106 let mut name
= flag_name(p
)?
;
107 if !name
.starts_with("--") {
109 if !p
.eat_punct('
,'
) {
110 bail
!("long option is required for `{}`", short
.unwrap());
112 name
= flag_name(p
)?
;
113 if !name
.starts_with("--") {
114 bail
!("long name must begin with `--`: `{}`", name
);
118 let val
= opt_val(p
)?
;
121 name
: name
[2..].to_string(),
122 short
: short
.map(|it
| it
[1..].to_string()),
128 fn opt_val(p
: &mut Parser
) -> Result
<Option
<ast
::Val
>, Error
> {
129 if !p
.lookahead_punct('
:'
, 1) {
133 let name
= p
.expect_name()?
;
134 p
.expect_punct('
:'
)?
;
136 let res
= ast
::Val { name, ty }
;
140 fn arity(p
: &mut Parser
) -> Result
<ast
::Arity
> {
141 if p
.eat_keyword("optional") {
142 return Ok(ast
::Arity
::Optional
);
144 if p
.eat_keyword("required") {
145 return Ok(ast
::Arity
::Required
);
147 if p
.eat_keyword("repeated") {
148 return Ok(ast
::Arity
::Repeated
);
150 if let Some(name
) = p
.eat_name() {
151 bail
!("expected one of `optional`, `required`, `repeated`, got `{}`", name
)
153 bail
!("expected one of `optional`, `required`, `repeated`, got {:?}", p
.ts
.pop())
156 fn ty(p
: &mut Parser
) -> Result
<ast
::Ty
> {
157 let name
= p
.expect_name()?
;
158 let res
= match name
.as_str() {
159 "PathBuf" => ast
::Ty
::PathBuf
,
160 "OsString" => ast
::Ty
::OsString
,
161 _
=> ast
::Ty
::FromStr(name
),
166 fn opt_single_doc(p
: &mut Parser
) -> Result
<Option
<String
>> {
167 if !p
.eat_punct('
#') {
170 p
.enter_delim(Delimiter
::Bracket
)?
;
171 p
.expect_keyword("doc")?
;
172 p
.expect_punct('
='
)?
;
173 let mut res
= p
.expect_string()?
;
174 if let Some(suf
) = res
.strip_prefix(' '
) {
175 res
= suf
.to_string();
181 fn opt_doc(p
: &mut Parser
) -> Result
<Option
<String
>> {
183 core
::iter
::from_fn(|| opt_single_doc(p
).transpose()).collect
::<Result
<Vec
<String
>>>()?
;
184 let lines
= lines
.join("\n");
186 if lines
.is_empty() {
193 fn cmd_name(p
: &mut Parser
) -> Result
<String
> {
194 let name
= p
.expect_name()?
;
195 if name
.starts_with('
-'
) {
196 bail
!("command name can't begin with `-`: `{}`", name
);
201 fn flag_name(p
: &mut Parser
) -> Result
<String
> {
202 let name
= p
.expect_name()?
;
203 if !name
.starts_with('
-'
) {
204 bail
!("flag name should begin with `-`: `{}`", name
);
210 stack
: Vec
<Vec
<TokenTree
>>,
215 fn new(ts
: TokenStream
) -> Self {
216 let mut ts
= ts
.into_iter().collect
::<Vec
<_
>>();
218 Self { stack: Vec::new(), ts }
221 fn at_delim(&mut self, delimiter
: Delimiter
) -> bool
{
222 match self.ts
.last() {
223 Some(TokenTree
::Group(g
)) => g
.delimiter() == delimiter
,
227 fn enter_delim(&mut self, delimiter
: Delimiter
) -> Result
<()> {
228 match self.ts
.pop() {
229 Some(TokenTree
::Group(g
)) if g
.delimiter() == delimiter
=> {
230 let mut ts
= g
.stream().into_iter().collect
::<Vec
<_
>>();
232 let ts
= mem
::replace(&mut self.ts
, ts
);
235 _
=> bail
!("expected `{{`"),
239 fn exit_delim(&mut self) -> Result
<()> {
241 bail
!("expected `}}`")
243 self.ts
= self.stack
.pop().unwrap();
246 fn end(&mut self) -> bool
{
247 self.ts
.last().is_none()
250 fn expect_keyword(&mut self, kw
: &str) -> Result
<()> {
251 if !self.eat_keyword(kw
) {
252 bail
!("expected `{}`", kw
)
256 fn eat_keyword(&mut self, kw
: &str) -> bool
{
257 if self.at_keyword(kw
) {
258 self.ts
.pop().unwrap();
264 fn at_keyword(&mut self, kw
: &str) -> bool
{
265 match self.ts
.last() {
266 Some(TokenTree
::Ident(ident
)) => &ident
.to_string() == kw
,
271 fn expect_name(&mut self) -> Result
<String
> {
272 self.eat_name().ok_or_else(|| {
273 let next
= self.ts
.pop().map(|it
| it
.to_string()).unwrap_or_default();
274 format_err
!("expected a name, got: `{}`", next
)
277 fn eat_name(&mut self) -> Option
<String
> {
278 let mut buf
= String
::new();
279 let mut prev_ident
= false;
281 match self.ts
.last() {
282 Some(TokenTree
::Punct(p
)) if p
.as_char() == '
-'
=> {
286 Some(TokenTree
::Ident(ident
)) if !prev_ident
=> {
288 buf
.push_str(&ident
.to_string());
301 fn _expect_ident(&mut self) -> Result
<String
> {
302 match self.ts
.pop() {
303 Some(TokenTree
::Ident(ident
)) => Ok(ident
.to_string()),
304 _
=> bail
!("expected ident"),
308 fn expect_punct(&mut self, punct
: char) -> Result
<()> {
309 if !self.eat_punct(punct
) {
310 bail
!("expected `{}`", punct
)
314 fn eat_punct(&mut self, punct
: char) -> bool
{
315 match self.ts
.last() {
316 Some(TokenTree
::Punct(p
)) if p
.as_char() == punct
=> {
323 fn lookahead_punct(&mut self, punct
: char, n
: usize) -> bool
{
324 match self.ts
.iter().rev().nth(n
) {
325 Some(TokenTree
::Punct(p
)) => p
.as_char() == punct
,
330 fn expect_string(&mut self) -> Result
<String
> {
331 match self.ts
.pop() {
332 Some(TokenTree
::Literal(lit
)) if lit
.to_string().starts_with('
"') => {
333 let text = lit.to_string();
334 let res = text.trim_matches('"'
).to_string();
337 _
=> bail
!("expected a string"),