Experimenting with more structured ways to handle command-line input/output in Rust
use proc_macro::TokenStream;
use proc_macro_error2::{proc_macro_error};
use quote::quote;

mod fluent;
mod macro_impl;

#[proc_macro_attribute]
#[proc_macro_error]
pub fn localize(attribute: TokenStream, item: TokenStream) -> TokenStream {
    let attribute_stream = proc_macro2::TokenStream::from(attribute);
    let derive_input_stream = proc_macro2::TokenStream::from(item);

    match macro_impl::localize(attribute_stream.clone(), derive_input_stream.clone()) {
        Ok(implementation) => {
            // No errors found, emit the generated `fluent_embed::Localize` implementation
            
            let token_stream = quote! {
                #derive_input_stream

                #implementation
            };

            // Optionally expand the macro for debugging purposes if the "expand" feature is enabled
            #[cfg(feature = "expand")]
            {
                expander::Expander::new("fluent_embed_derive")
                .write_to_out_dir(token_stream)
                .unwrap()
            }
            #[cfg(not(feature = "expand"))]
            token_stream
        }
        Err(error) => {
            // Emit the relevant error message
            macro_impl::error::emit(error, attribute_stream, derive_input_stream.clone());

            // Generate a minimal `Localize` implementation so the error is self-contained
            match syn::parse2::<syn::DeriveInput>(derive_input_stream.clone()) {
                Ok(derive_input) => {
                    let ident = derive_input.ident;
                    quote! {
                        #derive_input_stream
        
                        impl<W: ::std::io::Write> ::fluent_embed::Localize<W> for #ident {
                            const CANONICAL_LOCALE: ::fluent_embed::icu_locale::LanguageIdentifier =
                                ::fluent_embed::icu_locale::langid!("en-US");
        
                            fn available_locales(&self) -> Vec<::fluent_embed::icu_locale::LanguageIdentifier> {
                                unimplemented!("Encountered error in derive macro")
                            }
                
                            fn message_for_locale(
                                &self,
                                writer: &mut W,
                                locale: &::fluent_embed::icu_locale::LanguageIdentifier,
                            ) -> Result<(), ::fluent_embed::LocalizationError> {
                                unimplemented!("Encountered error in derive macro")
                            }
                        }
                    }
                },
                // Unable to fail gracefully if the ident is unknown, so just return the original input
                Err(_) => derive_input_stream,
            }
            
        }
    }
    .into()
}