errorlit 0.0.2

Macro for creating error literals
Documentation
//! The Error Literal creation macro.
//! 
//! This is for those circumstances when concat just doesn't work.

use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::ToTokens;
use syn::{
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
    parse_macro_input,
    LitStr,
    Result,
    Error,
    Token,
};

type Literals = Punctuated<LitStr, Token![,]>;

struct LitStrList {
    lits: Literals,
    span: Span,
}

impl Parse for LitStrList {
    fn parse(input: ParseStream) -> Result<Self> {
        let span = input.span();
        let lits = Literals::parse_terminated(input)?;

        if lits.len() < 1 {
            return Err(Error::new(
                span, "Expected at least one literal"
            ));
        }

        Ok(Self { lits, span })
    }
}

/// Produces a string literal at compile time in an error format.
/// 
/// Example:
/// 
/// ```rust
/// # macro_rules! errorlit { ($input:literal) => (); }
/// #[cfg(some_broken_flag)]
/// core::compile_error!(errorlit!(
///     "The `some_broken_flag` is broken!",
///     "Please use turn it off"
/// ));
/// ```
/// 
/// It is required that at least one string literal is passed into it.
/// It will server as the quick error message.
/// The rest of the passed literals will be additional information,
/// for example a suggestion on how to fix the error.
#[proc_macro]
pub fn errorlit(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as LitStrList);
    let name = match get_name() {
        Err(e) => return e.to_compile_error().into(),
        Ok(v) => v,
    };
    let mut iter = input.lits.iter();
    // Safety: we ensure that there is at least one element
    let first = iter.next().expect("At least one string is present");
    let mut error = format!(
        "\n\n\n\n[{name}] Fatal Error: {}\n",
        first.value()
    );
    for msg in iter {
        error.push_str(" -> ");
        error.push_str(&msg.value());
        error.push('\n');
    }
    error.push_str("\n\n\n\n");
    let literal = LitStr::new(&error, input.span);
    literal.to_token_stream().into()
}

fn get_name() -> syn::Result<String> {
    let crate_name = match std::env::var("CARGO_PKG_NAME") {
        Ok(variable) => variable,
        Err(_) => return Err(Error::new(
            Span::mixed_site(),
            "Unable to identify crate's name :("
        ))
    };
    let mut characters = crate_name.chars();
    let uppercased = match characters.next() {
        None => String::new(),
        Some(c) => c.to_uppercase().collect::<String>() + characters.as_str(),
    };
    Ok(uppercased)
}