dotinstall-macro 0.1.1

proc macro for dotinstall crate
Documentation
use std::collections::HashSet;

use syn::{braced, parse::Parse, punctuated::Punctuated, Error, LitStr, Token};

use super::kw;

pub struct Symlinks {
    pub symlinks_kw: kw::symlinks,
    pub links: Vec<Symlink>,
}

impl Parse for Symlinks {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let symlinks_kw = input.parse()?;
        let content;
        let _ = braced!(content in input);

        let links = Punctuated::<Symlink, Token![,]>::parse_terminated(&content)?;

        let mut link_set = HashSet::new();

        for link in &links {
            if !link_set.insert(link.link.value()) {
                return Err(Error::new(
                    link.link.span(),
                    format!("Duplicate symlink for `{}`", link.link.value()),
                ));
            }
        }

        let links = links.into_iter().collect();

        Ok(Self { symlinks_kw, links })
    }
}

pub struct Symlink {
    pub original: LitStr,
    pub arrow: Token![=>],
    pub link: LitStr,
}

impl Parse for Symlink {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let link = input.parse()?;
        let arrow = input.parse()?;
        let original = input.parse()?;

        Ok(Self {
            original,
            arrow,
            link,
        })
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    use syn::parse_str;

    use super::*;

    #[test]
    fn correctly_parses_symlinks() {
        let s: Symlinks = parse_str(
            r#"
            symlinks {
              "foo" => "bar",
              "~/.bashrc" => "~/config/bashrc",
            }
            "#,
        )
        .unwrap();

        let links: HashMap<_, _> = s
            .links
            .iter()
            .map(|l| (l.link.value(), l.original.value()))
            .collect();
        assert_eq!(links.get("foo"), Some(&"bar".to_string()));
        assert_eq!(links.get("~/.bashrc"), Some(&"~/config/bashrc".to_string()));
    }
}