gamedig_macros 0.1.1

Macro implementations used by gamedig
Documentation
use {
    proc_macro::TokenStream,
    quote::quote,
    syn::{
        Error,
        LitInt,
        LitStr,
        Result,
        Token,
        parse::{Parse, ParseStream},
        parse_macro_input,
    },
};

struct Input {
    text: LitStr,
    width: Option<LitInt>,
}

impl Parse for Input {
    fn parse(input: ParseStream) -> Result<Self> {
        let text: LitStr = input.parse()?;

        let width = if input.is_empty() {
            None
        } else {
            input.parse::<Token![,]>()?;
            Some(input.parse()?)
        };

        Ok(Self { text, width })
    }
}

pub fn expand(input: TokenStream) -> TokenStream {
    let Input { text, width } = parse_macro_input!(input as Input);

    let max_width = match width {
        Some(w) => {
            match w.base10_parse::<usize>() {
                Ok(0) => {
                    return Error::new_spanned(w, "width must be greater than 0")
                        .to_compile_error()
                        .into();
                }

                Ok(v) => v,

                Err(e) => {
                    return Error::new_spanned(w, format!("invalid width: {e}"))
                        .to_compile_error()
                        .into();
                }
            }
        }
        
        None => 80,
    };

    let source = text.value();
    let mut out = String::with_capacity(source.len());

    for (i, paragraph) in source.split('\n').enumerate() {
        if i > 0 {
            out.push('\n');
        }

        let mut line_len = 0usize;

        for word in paragraph.split_whitespace() {
            let word_len = word.len();

            if line_len == 0 {
                out.push_str(word);
                line_len = word_len;
            } else if line_len + 1 + word_len > max_width {
                out.push('\n');
                out.push_str(word);
                line_len = word_len;
            } else {
                out.push(' ');
                out.push_str(word);
                line_len += 1 + word_len;
            }
        }
    }

    let lit = LitStr::new(&out, text.span());
    quote!(#lit).into()
}