# annotation-rs

Compile-time annotation parser for rust

## Features

### Annotation
annotation-rs provides a derive macro `Annotation` to create annotation structure by struct, `StructStruct`, `TupleStruct` and `NoFieldStruct` are all supported.
```rust
use annotation_rs::Annotation;

#[derive(Annotation)]
struct NoField;

#[derive(Annotation)]
struct Tuple(i32, String);

#[derive(Annotation)]
struct Struct {
    int: i32,
    float: f64,
    bool: bool,
}
```

#### Types
* String: `String` in Rust.
* Bool: `bool` in Rust.
* Integer: any integer types in Rust.
* Float: any float types in Rust.
* Object: other annotation structure.
* Enum: defined enum, remember to use `enum_value=true`option.
* Vec<T>: Vec of T(T can`t be Object, Vec or HashMap).
* HashMap<String, T>: HashMap of T mapping by `String` Skey.
If you want to make a field optional, use `Option<T>` on the field type.

```rust
use annotation_rs::{AnnotationEnumValue, Annotation};

#[derive(Annotation)]
struct Bar;

#[derive(AnnotationEnumValue)]
enum SomeEnum {
    A,
    B
}

#[derive(Annotation)]
struct Foo {
    pub string: String,
    pub bool: bool,
    pub int: i32, // or other integer types like u32 ...
    pub float: f32, // or other float types like f64
    pub object: Bar, // any defined object
    #[field(enum_value=true)]
    pub enum_field: SomeEnum, // have to add enum_value option
    pub list: Vec<i32>, // nested type of vec can`t be Object, Vec or HashMap
    pub map: std::collections::HashMap<String, SomeEnum>,
    pub optional: Option<i32> // optional field
}
```
#### Options
* `alias`\
    Generated reader will parse the field with the given name instead of its field name in Rust.
    ```rust
    #[derive(Annotation)]
    struct Foo {
        #[field(alias = "i32")]
        pub int32: i32,
    }
    ```
* `default`\
    Set the default value for this field. If the value is not present when parsing, the default value will be set to the field, even the field is optional.`Object`, `Vec` or `HashMap` fields can`t have default value.
    ```rust
    #[derive(Annotation)]
    struct Foo {
        #[field(default = 1024)]
        pub int32: i32
    }
    ```
* `enum_value`\
    use `enum_value=true` on Enum type field.
        
#### Enum
Use derive `AnnotationEnumValue` on Enum to create a Enum value type.
```rust
use annotation_rs::AnnotationEnumValue;

#[derive(AnnotationEnumValue)]
enum SomeEnum {
    A,
    B
}
```
And then, the enum can be used as a field type.
* `variant_value` attribute\
    Customize a string corresponding value to variant(default is the snake case of variant name in Rust).
```rust
use annotation_rs::AnnotationEnumValue;

#[derive(AnnotationEnumValue)]
enum SomeEnum {
    #[variant_value("aaa")] // default is 'a'
    A,
    B
}
```   
### Parse annotations with `syn`and`quote`
`annotation_rs::AnnotationStructures<T>` can be used in `parse_macro_input!`
```rust
let annotations = syn::parse_macro_inpit!(input as annotation_rs::AnnotationStructures<Foo>);
```
If you want to parse annotation from `syn::Meta`, use `annotation_rs::AnnotationStructure::from_meta()`.\
And annotation structure with value can be convert to token automatically. But the visibility of each field must be public.
```rust
use proc_macro::TokenStream;

#[derive(Annotation)]
struct Foo {
    #[field(default = 1024)]
    pub int32: i32
}

fn derive_fn(input: TokenStream) -> TokenStream {
    let annotations = syn::parse_macro_input!(input as annotation_rs::AnnotationStructures<Foo>);
    let attrs = annotations.attrs;

    TokenStream::from(quote::quote! {
        fn get_attrs() -> Vec<Foo> {
            vec![#(#attrs),*]
        }
    })
}
```

### Generate derive macro
If you want to use builtin reader generator, enable `annotation_reader` feature.
Macro `generate_reader` is used to generate a derive macro.
```rust
use annotation_rs::generate_reader;

generated_reader!(
    MyDerive,
    [StructAttribute1, StructAttribute2],
    [FieldAttribute1, FieldAttribute2]
);

```
The macro will generate a public derive, it can be use to read annotations of `struct` ,`enum` or `union`, and record the metadata by generate `impl` block.

### Read annotations
Use the generated derive macro on a struct, and you can use the macro `has_annotation` and `get_annotation`to process annotations of the struct.
The feature require nightly rustc because `proc_macro_hygiene` is required.
```rust
#![feature(proc_macro_hygiene)]
use annotation_rs::{get_annotation, has_annotation};

#[derive(MyDerive)]
#[StructAttribute1("some parameters")]
struct Foo {
    #[FieldAttribute1("some parameters")]
    field: i32
}

fn some_fn() {
    assert!(has_annotation!(Foo, StructAttribute1));
    assert!(has_annotation!(Foo::field, FieldAttribute1));
    let struct_attr1: Option<StructAttribute1> = get_annotation!(Foo, StructAttribute1);
    let field_attr1: Option<StructAttribute1> = get_annotation!(Foo::field, StructAttribute1);
}
```