#![doc(html_root_url = "https://docs.rs/fast_new_type/0.1.7/")]
#![no_std]

/*!
This crate serves the purporse of implement [`Deref`],[`DerefMut`],[`AsRef`],[`AsMut`] and [`From`] in new
types,either for the first value of a struct,the "value" or "slot" field or the specified in the attribute.

With `default-features = false` you can opt-out of the [`From`] impl which in some cases is not desirable. 

This crate is **no_std**.

# Examples

```
use fast_new_type::new_type;

#[new_type]
struct FirstField(u32, u16);

#[new_type]
struct ValueField {
    value: u16,
    other_meta: u16,
}

#[new_type]
struct SlotField {
    slot: u16,
    other_meta: u16,
}

#[new_type(other)]
struct SpecField {
    other: u16,
    other_meta: u16,
}

```

[`Deref`]: https://doc.rust-lang.org/std/ops/trait.Deref.html
[`DerefMut`]: https://doc.rust-lang.org/std/ops/trait.DerefMut.html
[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
[`From`]: https://doc.rust-lang.org/std/convert/trait.From.html
*/

extern crate alloc;
extern crate proc_macro;

use alloc::format;
use alloc::string::{String, ToString};
use core::fmt::Write;
use proc_macro::TokenStream;
use syn_str::remove_item_attrs;
use syn_str::custom_types::{DisplayGeneric, Struct, StructType, ORDER_FIELDS};

/// Implement common traits in a wrapper for a type,with the specifie field as arg where needed.
#[proc_macro_attribute]
pub fn new_type(attr: TokenStream, item: TokenStream) -> TokenStream {
    let mut expansion = String::new();
    let item = item.to_string();
    let mut attr = attr.to_string();

    if attr.is_empty() {
        attr.push_str("slot, value");
    } else {
        attr.push_str(",slot, value");
    }

    let mut iter = attr.split(',');

    unsafe {
        ORDER_FIELDS = false;
    }

    let item2 = match remove_item_attrs(&item) {
        Ok(itm) => itm,
        Err(s) => format!("compile_error!(\"{}\")", s).parse().unwrap()
    };

    let syn_struct = match Struct::new(item2.as_ref()) {
        Ok(s) => s,
        Err(s) => {
            return format!("compile_error!(\"Error parsing the struct: {}\")", s)
                .parse()
                .unwrap()
        }
    };

    let (field_name, type_field) = if let Some(f) = syn_struct.fields.get(0) {
        if f.name == "" {
            ("0", f.f_type)
        } else if syn_struct.fields.len() == 1 {
            (f.name, f.f_type)
        } else {
            let mut field = None;

            'a: for mut s in iter.by_ref() {
                s = s.trim();

                for f2 in syn_struct.fields.iter() {
                    if f2.name == s {
                        field = Some(f2);
                        break 'a;
                    }
                }
            }

            match field {
                Some(f2) => (f2.name, f2.f_type),
                None => (f.name, f.f_type),
            }
        }
    } else {
        return String::from("compile_error!(\"Expected struct with fields\")")
            .parse()
            .unwrap();
    };

    writeln!(expansion).unwrap();

    writeln!(expansion, "{}", item).unwrap();

    writeln!(expansion).unwrap();

    writeln!(
        expansion,
        "impl{} core::ops::Deref for {} {{",
        syn_struct.interface.format_generics(),
        syn_struct.interface.name_boundless()
    )
    .unwrap();

    writeln!(expansion, "\ttype Target = {};", type_field).unwrap();

    writeln!(expansion).unwrap();

    writeln!(expansion, "\tfn deref(&self) -> &Self::Target {{").unwrap();

    if syn_struct.interface.item_type != StructType::Union {
        writeln!(expansion, "\t\t&self.{}", field_name).unwrap();
    } else {
        writeln!(expansion, "\t\tunsafe {{ &self.{} }}", field_name).unwrap();
    }

    writeln!(expansion, "\t}}").unwrap();
    writeln!(expansion, "}}").unwrap();
    writeln!(expansion).unwrap();

    writeln!(
        expansion,
        "impl{} core::ops::DerefMut for {} {{",
        syn_struct.interface.format_generics(),
        syn_struct.interface.name_boundless()
    )
    .unwrap();

    writeln!(
        expansion,
        "\tfn deref_mut(&mut self) -> &mut Self::Target {{"
    )
    .unwrap();

    if syn_struct.interface.item_type != StructType::Union {
        writeln!(expansion, "\t\t&mut self.{}", field_name).unwrap();
    } else {
        writeln!(expansion, "\t\tunsafe {{ &mut self.{} }}", field_name).unwrap();
    }

    writeln!(expansion, "\t}}").unwrap();
    writeln!(expansion, "}}").unwrap();
    writeln!(expansion).unwrap();

    if cfg!(feature = "from_struct_for_field") {
    writeln!(
        expansion,
        "impl{} From<{}> for {} {{",
        syn_struct.interface.format_generics(),
        syn_struct.interface.name_boundless(),
        type_field
    )
    .unwrap();

    writeln!(
        expansion,
        "\tfn from(a: {}) -> Self {{",
        syn_struct.interface.name_boundless()
    )
    .unwrap();

    if syn_struct.interface.item_type != StructType::Union {
        writeln!(expansion, "\t\ta.{}", field_name).unwrap();
    } else {
        writeln!(expansion, "\t\tunsafe {{ a.{} }}", field_name).unwrap();
    }

    writeln!(expansion, "\t}}").unwrap();
    writeln!(expansion, "}}").unwrap();
    writeln!(expansion).unwrap();
    }

    writeln!(
        expansion,
        "impl{} AsRef<{}> for {} {{",
        syn_struct.interface.format_generics(),
        type_field,
        syn_struct.interface.name_boundless()
    )
    .unwrap();

    writeln!(expansion, "\tfn as_ref(&self) -> &{} {{", type_field).unwrap();

    if syn_struct.interface.item_type != StructType::Union {
        writeln!(expansion, "\t\t&self.{}", field_name).unwrap();
    } else {
        writeln!(expansion, "\t\tunsafe {{ &self.{} }}", field_name).unwrap();
    }

    writeln!(expansion, "\t}}").unwrap();
    writeln!(expansion, "}}").unwrap();
    writeln!(expansion).unwrap();

    writeln!(
        expansion,
        "impl{} AsMut<{}> for {} {{",
        syn_struct.interface.format_generics(),
        type_field,
        syn_struct.interface.name_boundless(),
    )
    .unwrap();

    writeln!(
        expansion,
        "\tfn as_mut(&mut self) -> &mut {} {{",
        type_field
    )
    .unwrap();

    if syn_struct.interface.item_type != StructType::Union {
        writeln!(expansion, "\t\t&mut self.{}", field_name).unwrap();
    } else {
        writeln!(expansion, "\t\tunsafe {{ &mut self.{} }}", field_name).unwrap();
    }

    writeln!(expansion, "\t}}").unwrap();
    writeln!(expansion, "}}").unwrap();

    expansion.parse().unwrap()
}
