fixed-type-id 0.1.2

Make your types have a fixed type id&stable type name with version support between different builds.
Documentation
## Fixed Type Id


> Nightly toolchain required.

Make your types have a fixed type id and stable type name between different builds.

The crate provides a trait and a procedural macro. By implementing [`FixedTypeId`],
other crates can use [`FixedTypeId::ty_id()`], [`FixedTypeId::ty_name()`] and [`FixedTypeId::ty_version()`]
to get the type id, name and version about this type

It use [rapidhash](https://github.com/hoxxep/rapidhash) to hash the type name you provided, with/without version hashed into the id.
Then provide the hash as a fixed id for your type. So you can construct exact the same id from the same type name and version.

The purpose of this crate is to provide a fixed type id for simple types, which you may want to persist their metadata, like `u8`, `i16`, `f32`, `str`, `String`, `bool`, `(u8,i16,f32)`, `[u8; 3]`, `[i16; 3]`, `&[u8]`, etc. Or types frequently used in your structs, like `HashMap<K, V>`, `Vec<T>`, `Box<T>` etc.

It also support trait objects, which is used by [trait_cast_rs](https://github.com/c00t/trait_cast_rs) to
cast between different traits.

Note that the type name implemented by default for standard library types may be different from [`core::any::type_name`], you shouldn't compare it with [`FixedTypeId::ty_name()`].

Because of the orphan rule, if you want to implement [`FixedTypeId`] for types in other crates, you can submit a PR to add them.

### Usage


The example usage:

```rust
use fixed_type_id::{FixedTypeId, FixedId, fixed_type_id, name_version_to_hash};
use std::hash::Hasher;

mod m {
    // the macro use `self::xxx` to import required items, I avoid to use `$crate` because it avoid to reexport items from this crate.
    use fixed_type_id::{FixedTypeId, FixedId, fixed_type_id, FixedVersion};
    pub trait Q {}
    pub trait W {}
    pub trait E<T> {}
    fixed_type_id!{
        // default to (0,0,0)
        #[FixedTypeIdVersion((0,1,0))]
        // #[FixedTypeIdFile("types.toml")]
        // no default, but when store into file, version will be dropped, so only use it for debug.
        dyn m::Q; // type name is "dyn m::Q", it only store the type name you provided, without modification.
        dyn W; // type name is "dyn W", though `W` is under `m` module, it still store "dyn W"
        dyn E<u8>; // type name is "dyn E<u8>"
        A; // type name is "A"
        B<u8>; // type name is "B<u8>"
    }
    pub struct A;
    pub struct B<T> {
    pub t: T
    }
    impl Q for A {}
}
use m::*;
assert_eq!(<dyn Q>::TYPE_ID.0, name_version_to_hash("dyn m::Q", &(0,1,0).into()));
assert_eq!(<dyn Q>::TYPE_NAME, "dyn m::Q");
assert_eq!(<A as FixedTypeId>::TYPE_VERSION, (0,1,0).into());
assert_eq!(<A as FixedTypeId>::TYPE_NAME, "A");
```

If you want to define for your generic struct, you can use [`implement_wrapper_fixed_type_id`]:

```rust
use fixed_type_id::{FixedTypeId, FixedId, ConstTypeName, fstr_to_str, FixedVersion, implement_wrapper_fixed_type_id};
use std::ops::Add;

pub struct MyStruct<T: Add> {
    pub x: T,
}

implement_wrapper_fixed_type_id!{
    // You can define a typename with different module path, but it's not recommended.
    // Generally, you should define it in the same module as the struct for better readability.
    MyStruct<T:Add> => "SomeSpecialModule::MyStruct"
}

assert_eq!(<MyStruct<u8> as FixedTypeId>::TYPE_NAME, "SomeSpecialModule::MyStruct<u8>");
```

Also, you can define this trait yoursellf:

```rust
use fixed_type_id::{FixedTypeId, FixedId, FixedVersion};
use rapidhash::rapidhash;

struct MyType;

impl FixedTypeId for MyType {
    const TYPE_NAME: &'static str = "MyType";
    // make this type id hash without version
    const TYPE_ID: FixedId = FixedId::from_type_name(Self::TYPE_NAME, None);
    const TYPE_VERSION: FixedVersion = FixedVersion::new(0, 0, 0);
}

assert_eq!(<MyType as FixedTypeId>::TYPE_NAME, "MyType");
assert_eq!(<MyType as FixedTypeId>::TYPE_ID.0, rapidhash::rapidhash("MyType".as_bytes()));
assert_eq!(<MyType as FixedTypeId>::TYPE_VERSION, (0,0,0).into());
```

### Notes


#### Version


For standard libraries types, the version is always `(0,0,0)`, in the future, it may be changed to rustc version you are using.

Currently, this crate implement [`FixedTypeId`] for these types:

- `()`, `Infallible`
- `T` for all primitive types, like `u8`, `i16`, `f32`, `str`, `String`, `bool` etc.
- `&T`, `&mut T` for all primitive types
- `Box<T>`, `Vec<T>`, `HashMap<K, V>`, `PhantomData<T>`, `NonZero<T>`, `fn(T) -> R`, `fn() -> R` for all generic types that implement [`FixedTypeId`]
- `(T,)`, `(T,U)`, `(T,U,V)`... `(T1,..., T16)` for all generic types that implement [`FixedTypeId`]
- `[T; N]` for all `T` that implement [`FixedTypeId`], but there is a limit for `N`, only `N <= 32` or some special numbers(`64`, `128`, `256`,..., `768`, `1024`, `2048`,...,`65535`) are supported, other numbers will just leave it as `N`. If you know how to generate `&str` for `const N: usize` in const context, you can submit a PR to add it.

#### Type Name Length


When you want to implement [`FixedTypeId`] for your types with generic parameters, you need to provide a dynamic generated `&str` as type name either 1. in const context or 2. store a `&[&str]` in const and then concat them at runtime.

If we choose to generate it in const context, because the only way i know to dynamically generate a `&str` in const context is to **fill a fixed length array `[u8;N]`**, and this array will be persisted into the binary, so the length of the type name is limited by **the binary size**. Currently, the length can be configured by feature flags `len64`, `len128` and `len256`, the default is `len128`, it means the max length of the type name is 128 bytes.

If we choose to store a `&[&str]` in const and then concat them at runtime, the return type of [`FixedTypeId::ty_name()`] will be `String`, it's different from the return type of [`core::any::type_name`], which is `&'static str`. It makes it difficult to just replace [`core::any::type_name`] with [`type_name`] or [`FixedTypeId::ty_name()`].

So currently we choose to generate it in const context.

#### Differences between `fixed_type_id`, `fixed_type_id_without_version_hash` and `random_fixed_type_id`


- `fixed_type_id`: Generate a unique id for the type, with a [`FixedId`] that [`rapidhash::rapidhash`] the name you provided,
  the version is also hashed into the [`FixedId`]. Version defaults to `(0,0,0)`, use `#[FixedTypeIdVersion((0,1,0))]` to change it.
  Use it when you want that different versions of your type have different ids.
- `fixed_type_id_without_version_hash`: Generate a unique id for the type, with a [`FixedId`] that [`rapidhash::rapidhash`] the name you provided,
  without version hashed into the [`FixedId`]. Use it when you want that different versions of your type have the same id.
- `random_fixed_type_id`: Generate a random id for the type, with a [`FixedId`] that random generated for each build.

All these macros can be used with:

- `#[FixedTypeIdVersion((x,y,z))]`: Set the version to `(x,y,z)`.
- `#[FixedTypeIdFile("filename.toml")]`: Store the type id into a file, so you can use it for debug, make sure the file already exists.
- `#[FixedTypeIdEqualTo("other_type")]`: Make the type id [`FixedId`] equal to `other_type`, so the two types have the same id, but different type names, and versions.

#### Erase Type Name


It can be configured by feature flag `erase_name`, default is disabled.

Currently, it only works for implementation of [`FixedTypeId`] by `fixed_type_id` and `fixed_type_id_without_version_hash`.