fixed_len_str 0.3.3

A procedural macro for create a smart pointer to str backed by a fixed size array,with the size given by the tokens.
Documentation
#![doc(html_root_url = "https://docs.rs/fixed_len_str/0.3.3/")]
#![no_std]

//! This crate provides a procedural macro for declare a wrapper struct for an array with the size
//! given by the tokens which derefs to [`str`].
//! 
//! For a proper API documentation of one of length 12 see [fixed_len_str_example].
//!
//! If you want to use [serde] to serialize and deserialize the fixed_len_str use
//! `features = ["serde_support"]`,if you want to match a pattern of type
//! `FnMut(FixedStr$len) -> bool` on a [`str`] use `features = ["pattern_pred_support"]`
//! and if you want the documentation visible at the expansion use `default-features = false`.
//! 
//! [`str`]: https://doc.rust-lang.org/std/primitive.str.html
//! [fixed_len_str_example]: https://docs.rs/fixed_len_str_example/
//! [serde]: https://crates.io/crates/serde

extern crate proc_macro;
extern crate alloc;

use proc_macro::TokenStream;

use alloc::string::ToString;
use alloc::format;

/// A macro for declare an FixedStr**input** struct.
/// 
/// # Examples
/// 
/// ```
/// #![feature(proc_macro_hygiene)] 
/// // this is only needed here as I expanding as statements the respective items
/// #![feature(pattern)] 
/// // needed for use the unstable API pattern with
/// // the `pattern_pred_support` feature enabled
/// 
/// use fixed_len_str::fixed_len_str;
/// 
/// fixed_len_str!(3);
/// 
/// let string = FixedStr3::from("abc");
/// 
/// assert_eq!(string, "abc");
/// 
/// let string = FixedStr3::new(*b"abc").unwrap();
/// 
/// assert_eq!(string, "abc");
/// 
/// let mut string = FixedStr3::default(); // equivalent to mem::zeroed but safe
/// string.fill_str("abc");
/// 
/// assert_eq!(string, "abc");
/// 
/// let mut string = FixedStr3::new([b'a', b'b', 0]).unwrap();
/// assert_eq!(string, "ab");
/// string.fill_char('c');
/// 
/// assert_eq!(string, "abc");
/// assert_eq!(string.as_bytes(), (&string[..]).as_bytes()); // this is only certain with non-zero bytes
/// assert_eq!(string.into_string(), String::from(&string[..])); // clone or consume at your option
/// assert_eq!(FixedStr3::from(&[][..]).as_ref(), "");
/// 
/// if cfg!(feature = "pattern_pred_support") {
///     use fixed_str3::Closure; // needed due to the orphan rule
/// 
///     assert_eq!("aaabb".matches(Closure::from(|s: FixedStr3| s == "aaa" || s == "bb"))
///                       .collect::<Vec<&str>>(), ["aaa", "bb"]);
/// }
/// ```
#[proc_macro]
pub fn fixed_len_str(input: TokenStream) -> TokenStream {
    let len = match input.to_string().trim().parse::<usize>() {
        Ok(n) => n,
        Err(e) => return format!("compile_error!(\"Error parsing the argument to an usize: {}\")", e).parse().unwrap(),
    };

    let half_len = {
        let mut temp = len / 2;
        
        if (len - (temp + temp)) != 0 {
            temp += 1;
        }
        
        temp.to_string()
    };

    let len = len.to_string();
    
    let mut string = include_str!("code.in").to_string();

    if cfg!(feature = "serde_support") {
        string.push_str(include_str!("serde_code.in"))
    }

    if cfg!(feature = "pattern_pred_support") {
        string.push_str(include_str!("pattern_pred_code.in"))
    }

    string.push_str("
}

$doc_hide
pub use fixed_str$len::FixedStr$len;");

    if cfg!(feature = "docs_hidden") {
        string = string.replace("$doc_hide", "#[doc(hidden)]");
    } else {
        string = string.replace("$doc_hide", "");
    }

    string.replace("$len", &len[..]).replace("$half_len_rounded_up", &half_len[..]).parse().unwrap()
}

/// A macro for declare an FixedStr**input** struct with NonZeroU8 as utf8 encoded bytes.
/// 
/// # Examples
/// 
/// ```
/// #![feature(proc_macro_hygiene)] 
/// // this is only needed here as I expanding as statements the respective items
/// #![feature(pattern)] 
/// // needed for use the unstable API pattern with
/// // the `pattern_pred_support` feature enabled
/// 
/// use fixed_len_str::fixed_len_str_nz;
/// use core::mem::transmute;
/// 
/// fixed_len_str_nz!(3);
/// 
/// let string = FixedStrNZ3::from("abc");
/// 
/// assert_eq!(string, "abc");
/// 
/// let string = FixedStrNZ3::new(unsafe { transmute(*b"abc") }).unwrap();
/// 
/// assert_eq!(string, "abc");
/// assert_eq!(string.as_bytes(), (&string[..]).as_bytes()); 
/// assert_eq!(string.into_string(), String::from(&string[..])); // clone or consume at your option
/// 
/// assert_eq!(FixedStrNZ3::from(unsafe { transmute(&[b'a', b'b', b'c', b'd']) }).as_ref(), "abc");
/// 
/// if cfg!(feature = "pattern_pred_support") {
///     use fixed_str_nz3::Closure; // needed due to the orphan rule
/// 
///     assert_eq!("aaabb".matches(Closure::from(|s: FixedStrNZ3| s == "aaa" || s == "bb"))
///                       .collect::<Vec<&str>>(), ["aaa"]);
/// }
/// ```
#[proc_macro]
pub fn fixed_len_str_nz(input: TokenStream) -> TokenStream {
    let mut string = include_str!("code_nz.in").to_string();

    if cfg!(feature = "serde_support") {
        string.push_str(include_str!("serde_code_nz.in"))
    }

    if cfg!(feature = "pattern_pred_support") {
        string.push_str(include_str!("pattern_pred_code_nz.in"))
    }

    string.push_str("
}

$doc_hide
pub use fixed_str_nz$len::FixedStrNZ$len;");

    if cfg!(feature = "docs_hidden") {
        string = string.replace("$doc_hide", "#[doc(hidden)]");
    } else {
        string = string.replace("$doc_hide", "");
    }

    string.replace("$len", input.to_string().trim()).parse().unwrap()
}

/// Searchs for ocurrences of FixedStr**len** in the item and expands to calls to [`fixed_len_str!`](./macro.fixed_len_str.html)
/// with **len** as arg at the end of the item.
/// 
/// # Examples
/// 
/// ```
/// use fixed_len_str::use_fixed_str;
/// 
/// #[use_fixed_str] // this declares all the FixedStr structs so this compile well.
/// fn main() {
///     let _: FixedStr2;
///     let _: FixedStr4;
/// }
/// ```
#[proc_macro_attribute]
pub fn use_fixed_str(_: TokenStream, input: TokenStream) -> TokenStream {
    let mut input = input.to_string();
    let input2 = input.clone();
    let mut searched = &input2[..];

    while let Some(i) = searched.find("FixedStr") {
        searched = &searched[i..];

        if searched.starts_with("FixedStrNZ") {
            searched = &searched[7..];
            continue;
        }

        if let Some(i2) = searched.find(|c: char| c.is_numeric()) {
            searched = &searched[i2..];
            let s = format!("fixed_len_str::fixed_len_str!({});", &searched[..searched.find(|c: char| !c.is_numeric())
                                                                      .expect("Number has no end")]);

            if !input.contains(&s[..]) {
                input.push_str(&s[..]);
            } 
        }
    }

    input.parse().unwrap()
}

/// Searchs for ocurrences of FixedStrNZ**len** in the item and expands to calls to [`fixed_len_str_nz!`](./macro.fixed_len_str_nz.html)
/// with **len** as arg at the end of the item.
/// 
/// # Examples
/// 
/// ```
/// use fixed_len_str::use_fixed_str_nz;
/// 
/// #[use_fixed_str_nz] // this declares all the FixedStrNZ structs so this compile well.
/// fn main() {
///     let _: FixedStrNZ2;
///     let _: FixedStrNZ4;
/// }
/// ```
#[proc_macro_attribute]
pub fn use_fixed_str_nz(_: TokenStream, input: TokenStream) -> TokenStream {
    let mut input = input.to_string();
    let input2 = input.clone();
    let mut searched = &input2[..];

    while let Some(i) = searched.find("FixedStrNZ") {
        searched = &searched[i..];

        if let Some(i2) = searched.find(|c: char| c.is_numeric()) {
            searched = &searched[i2..];
            let s = format!("fixed_len_str::fixed_len_str_nz!({});", &searched[..searched.find(|c: char| !c.is_numeric())
                                                                      .expect("Number has no end")]);

            if !input.contains(&s[..]) {
                input.push_str(&s[..]);
            } 
        }
    }

    input.parse().unwrap()
}