1use std::fmt;
2use std::str::FromStr;
3
4use proc_macro2::Span;
5use syn;
6use syn::parse;
7use syn::parse::{Parse, ParseStream};
8use syn::punctuated::Punctuated;
9use syn::{Ident, Lit};
10
11#[derive(Debug, Copy, Clone)]
12pub enum Mode {
13 Raw,
14 Extract,
15}
16
17#[derive(Debug)]
18pub struct Config {
19 pub includes: Vec<String>,
20 pub mode: Option<Mode>,
21 pub use_external_doc: bool,
22}
23
24impl FromStr for Config {
25 type Err = parse::Error;
26
27 fn from_str(s: &str) -> Result<Self, Self::Err> {
28 syn::parse_str(s)
29 }
30}
31
32impl Parse for Config {
33 fn parse(input: ParseStream) -> parse::Result<Self> {
34 let fields = Punctuated::<Field, Token![,]>::parse_terminated(input)?;
35
36 let mut includes = vec![];
37 let mut mode = None;
38 let mut use_external_doc = false;
39
40 for field in fields {
41 match &*field.ident.to_string() {
42 "include" => match field.value {
43 Lit::Str(s) => includes.push(s.value()),
44 _ => return Err(parse_error("unsupported literal type in 'include' field.")),
45 },
46 "mode" => match field.value {
47 Lit::Str(s) => match s.value().trim() {
48 "raw" => mode = Some(Mode::Raw),
49 "extract" => mode = Some(Mode::Extract),
50 s => return Err(parse_error(format!("invalid mode: {:?}", s))),
51 },
52 _ => return Err(parse_error("unsupported literal type in 'mode' field.")),
53 },
54 "use_external_doc" => match field.value {
55 Lit::Str(s) => match s.value().trim() {
56 "on" | "true" | "yes" => use_external_doc = true,
57 "off" | "false" | "no" => use_external_doc = false,
58 s => {
59 return Err(parse_error(format!(
60 "invalid value in `use_external_doc`: {}",
61 s
62 )))
63 }
64 },
65 Lit::Bool(b) => use_external_doc = b.value,
66 _ => {
67 return Err(parse_error(
68 "unsupported literal type in 'use_external_doc' field.",
69 ))
70 }
71 },
72 s => return Err(parse_error(format!("invalid key: {:?}", s))),
73 }
74 }
75
76 Ok(Config {
77 includes,
78 mode,
79 use_external_doc,
80 })
81 }
82}
83
84fn parse_error<D>(message: D) -> parse::Error
85where
86 D: fmt::Display,
87{
88 parse::Error::new(Span::call_site(), message)
89}
90
91#[derive(Debug)]
92struct Field {
93 ident: Ident,
94 eq: Token![=],
95 value: Lit,
96}
97
98impl Parse for Field {
99 fn parse(input: ParseStream) -> parse::Result<Self> {
100 Ok(Field {
101 ident: input.parse()?,
102 eq: input.parse()?,
103 value: input.parse()?,
104 })
105 }
106}