#![deny(missing_docs)]
//! # enum_repr_conv
//! `enum_repr_conv` is a proc-macro library that provides a convenient attribute-style `repr_conv()` macro
//! to specify a primitive integer type used for representing variants of the annotated fieldless enum as
//! values of this type.
//!
//! ## Description
//! `enum_repr_conv` bridges the gap between C-style enums and fieldless enums in Rust that can be
//! represented using a chosen primitive integer type. It implements conversions from enum variants into
//! integer values (infallible through [`std::convert::From`](https://doc.rust-lang.org/std/convert/trait.From.html))
//! and vice versa (fallible through [`std::convert::TryFrom`](https://doc.rust-lang.org/std/convert/trait.TryFrom.html)).
//! This functionality is provided via `repr_conv(<type>)` macro that mimics the syntax and behavior of
//! [`repr(u*)/repr(i*)`](https://doc.rust-lang.org/nomicon/other-reprs.html#repru-repri) data layout
//! attributes but additionally provides practical and sensible implementations of common conversion traits.

use proc_macro::TokenStream;

use crate::parse::parse;
use crate::produce::produce;
use crate::transform::transform;

mod parse;
mod produce;
mod transform;

/// An attribute macro that mimics the syntax and behavior of `repr(u*)`/`repr(i*)` but additionally
/// implements [`std::convert::From`](https://doc.rust-lang.org/std/convert/trait.From.html) and
/// [`std::convert::TryFrom`](https://doc.rust-lang.org/std/convert/trait.TryFrom.html)
/// traits to provide practical and sensible conversions between annontated enum variants and respective
/// integer values.
///
/// [`repr_conv`](macro@repr_conv) accepts a single parameter which is a primitive integer type.
///
/// ```rust
/// # use enum_repr_conv::repr_conv;
/// #[repr_conv(u16)]
/// #[derive(Debug, PartialEq, Eq)]
/// enum SupportedPorts {
///     FTP = 21,
///     SSH, // = 22
///     HTTP = 80,
///     HTTPS = 443,
/// }
///
/// // we can get a u16 for the FTP port easily...
/// assert_eq!(u16::from(SupportedPorts::FTP), 21);
/// //... and for the SSH port too
/// assert_eq!(u16::from(SupportedPorts::SSH), 22);
/// // HTTP port is supported and so conversion succeeds
/// assert_eq!(SupportedPorts::try_from(80).unwrap(), SupportedPorts::HTTP);
/// // Gopher is not supported; who needs it in 2025?!
/// assert!(SupportedPorts::try_from(70).is_err());
/// ```
#[proc_macro_attribute]
pub fn repr_conv(attr: TokenStream, item: TokenStream) -> TokenStream {
    match parse(attr.into(), item.into()) {
        Ok(parsed) => {
            let transformed = transform(parsed);
            let code = produce(transformed);

            code.into()
        }
        Err(err) => err.to_compile_error().into(),
    }
}
