doubter-impl 0.1.0

Internal implementation of doubter
Documentation
use std::fmt;
use std::str::FromStr;

use proc_macro2::Span;
use syn;
use syn::parse;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::{Ident, Lit};

#[derive(Debug, Copy, Clone)]
pub enum Mode {
    Raw,
    Extract,
}

#[derive(Debug)]
pub struct Config {
    pub includes: Vec<String>,
    pub mode: Option<Mode>,
    pub use_external_doc: bool,
}

impl FromStr for Config {
    type Err = parse::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        syn::parse_str(s)
    }
}

impl Parse for Config {
    fn parse(input: ParseStream) -> parse::Result<Self> {
        let fields = Punctuated::<Field, Token![,]>::parse_terminated(input)?;

        let mut includes = vec![];
        let mut mode = None;
        let mut use_external_doc = false;

        for field in fields {
            match &*field.ident.to_string() {
                "include" => match field.value {
                    Lit::Str(s) => includes.push(s.value()),
                    _ => return Err(parse_error("unsupported literal type in 'include' field.")),
                },
                "mode" => match field.value {
                    Lit::Str(s) => match s.value().trim() {
                        "raw" => mode = Some(Mode::Raw),
                        "extract" => mode = Some(Mode::Extract),
                        s => return Err(parse_error(format!("invalid mode: {:?}", s))),
                    },
                    _ => return Err(parse_error("unsupported literal type in 'mode' field.")),
                },
                "use_external_doc" => match field.value {
                    Lit::Str(s) => match s.value().trim() {
                        "on" | "true" | "yes" => use_external_doc = true,
                        "off" | "false" | "no" => use_external_doc = false,
                        s => {
                            return Err(parse_error(format!(
                                "invalid value in `use_external_doc`: {}",
                                s
                            )))
                        }
                    },
                    Lit::Bool(b) => use_external_doc = b.value,
                    _ => {
                        return Err(parse_error(
                            "unsupported literal type in 'use_external_doc' field.",
                        ))
                    }
                },
                s => return Err(parse_error(format!("invalid key: {:?}", s))),
            }
        }

        Ok(Config {
            includes,
            mode,
            use_external_doc,
        })
    }
}

fn parse_error<D>(message: D) -> parse::Error
where
    D: fmt::Display,
{
    parse::Error::new(Span::call_site(), message)
}

#[derive(Debug)]
struct Field {
    ident: Ident,
    eq: Token![=],
    value: Lit,
}

impl Parse for Field {
    fn parse(input: ParseStream) -> parse::Result<Self> {
        Ok(Field {
            ident: input.parse()?,
            eq: input.parse()?,
            value: input.parse()?,
        })
    }
}