Skip to content

Commit

Permalink
support the Into trait
Browse files Browse the repository at this point in the history
  • Loading branch information
magiclen committed Dec 8, 2023
1 parent 70857aa commit 6026ef3
Show file tree
Hide file tree
Showing 29 changed files with 1,617 additions and 96 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
- --no-default-features --features Deref
- --no-default-features --features DerefMut
- --no-default-features --features Deref --features DerefMut
- --no-default-features --features Into
name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }})
runs-on: ${{ matrix.os }}
steps:
Expand Down Expand Up @@ -74,6 +75,7 @@ jobs:
- --no-default-features --features Deref
- --no-default-features --features DerefMut
- --no-default-features --features Deref --features DerefMut
- --no-default-features --features Into
name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }})
runs-on: ${{ matrix.os }}
steps:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
- --no-default-features --features Deref
- --no-default-features --features DerefMut
- --no-default-features --features Deref --features DerefMut
- --no-default-features --features Into
name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }})
runs-on: ${{ matrix.os }}
steps:
Expand Down Expand Up @@ -90,6 +91,7 @@ jobs:
- --no-default-features --features Deref
- --no-default-features --features DerefMut
- --no-default-features --features Deref --features DerefMut
- --no-default-features --features Into
name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }})
runs-on: ${{ matrix.os }}
steps:
Expand Down
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "educe"
version = "0.5.0-beta.1"
version = "0.5.0"
authors = ["Magic Len <[email protected]>"]
edition = "2021"
rust-version = "1.56"
Expand Down Expand Up @@ -28,7 +28,7 @@ assert-eq-float = "0.1"
rustversion = "1"

[features]
default = ["Debug", "Clone", "Copy", "PartialEq", "Eq", "PartialOrd", "Ord", "Hash", "Default", "Deref", "DerefMut"]
default = ["Debug", "Clone", "Copy", "PartialEq", "Eq", "PartialOrd", "Ord", "Hash", "Default", "Deref", "DerefMut", "Into"]

full = ["syn/full"]

Expand All @@ -43,6 +43,7 @@ Hash = []
Default = []
Deref = []
DerefMut = []
Into = []

[package.metadata.docs.rs]
all-features = true
Expand Down
101 changes: 99 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1421,7 +1421,7 @@ Use `#[derive(Educe)]` and `#[educe(Deref)]` to implement the `Deref` trait for

###### Basic Usage

You must designate a field as a default by inmutably dereferencing it unless the number of fields is exactly one.
You must designate a field as the default for obtaining an immutable reference unless the number of fields is exactly one.

```rust
use educe::Educe;
Expand Down Expand Up @@ -1460,7 +1460,7 @@ Use `#[derive(Educe)]` and `#[educe(DerefMut)]` to implement the `DerefMut` trai

###### Basic Usage

You must designate a field as a default by mutably dereferencing it unless the number of fields is exactly one.
You must designate a field as the default for obtaining an mutable reference unless the number of fields is exactly one.

```rust
use educe::Educe;
Expand Down Expand Up @@ -1496,6 +1496,103 @@ enum Enum {

The mutable dereferencing fields do not need to be the same as the immutable dereferencing fields, but their types must be consistent.

#### Into

Use `#[derive(Educe)]` and `#[educe(Into(type))]` to implement the `Into<type>` trait for a struct or enum.

###### Basic Usage

You need to designate a field as the default for `Into<type>` conversion unless the number of fields is exactly one. If you don't, educe will automatically try to find a proper one.

```rust
use educe::Educe;

#[derive(Educe)]
#[educe(Into(u8), Into(u16))]
struct Struct {
f1: u8,
f2: u16,
}

#[derive(Educe)]
#[educe(Into(u8))]
enum Enum {
V1 {
f1: u8,
#[educe(Into(u8))]
f2: u8,
},
V2 (
u8
),
}
```

###### Use Another Method to Perform Into Conversion

The `method` parameter can be utilized to replace the implementation of the `Into` trait for a field, eliminating the need to implement the `Into` trait for the type of that field.

```rust
use educe::Educe;

fn into(v: u16) -> u8 {
v as u8
}

#[derive(Educe)]
#[educe(Into(u8))]
enum Enum {
V1 {
#[educe(Into(u8, method(into)))]
f1: u16,
},
V2 (
u8
),
}
```

###### Generic Parameters Bound to the `Into` Trait or Others

Generic parameters will be automatically bound to the `Into<type>` trait if necessary.

```rust
use educe::Educe;

#[derive(Educe)]
#[educe(Into(u8))]
enum Enum<T, K> {
V1 {
f1: K,
},
V2 (
T
),
}
```

Or you can set the where predicates by yourself.

```rust
use educe::Educe;

fn into<T>(_v: T) -> u8 {
0
}

#[derive(Educe)]
#[educe(Into(u8, bound(K: Into<u8>)))]
enum Enum<T, K> {
V1 {
f1: K,
},
V2 (
#[educe(Into(u8, method(into)))]
T
),
}
```

## Crates.io

https://crates.io/crates/educe
Expand Down
5 changes: 3 additions & 2 deletions src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ pub(crate) mod ident_bool;
feature = "Ord",
feature = "Hash",
feature = "Deref",
feature = "DerefMut"
feature = "DerefMut",
feature = "Into"
))]
#[allow(dead_code)]
pub(crate) mod ident_index;
Expand All @@ -38,5 +39,5 @@ pub(crate) mod int;
#[allow(dead_code)]
pub(crate) mod unsafe_punctuated_meta;

#[cfg(any(feature = "PartialOrd", feature = "Ord"))]
#[cfg(any(feature = "PartialOrd", feature = "Ord", feature = "Into"))]
pub(crate) mod tools;
100 changes: 100 additions & 0 deletions src/common/tools/hash_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use std::{
cmp::Ordering,
fmt::{self, Display, Formatter},
hash::{Hash, Hasher},
str::FromStr,
};

use proc_macro2::Span;
use quote::ToTokens;
use syn::{spanned::Spanned, Path, Type};

#[derive(Debug, Clone)]
pub(crate) struct HashType(String, Span);

impl PartialEq for HashType {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}

impl Eq for HashType {}

impl PartialOrd for HashType {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl Ord for HashType {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}

impl Hash for HashType {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
Hash::hash(&self.0, state);
}
}

impl Display for HashType {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0.replace("& '", "&'"), f)
}
}

impl From<Type> for HashType {
#[inline]
fn from(value: Type) -> Self {
Self::from(&value)
}
}

impl From<&Type> for HashType {
#[inline]
fn from(value: &Type) -> Self {
Self(value.into_token_stream().to_string(), value.span())
}
}

impl From<Path> for HashType {
#[inline]
fn from(value: Path) -> Self {
Self::from(&value)
}
}

impl From<&Path> for HashType {
#[inline]
fn from(value: &Path) -> Self {
Self(value.into_token_stream().to_string(), value.span())
}
}

#[allow(dead_code)]
impl HashType {
#[inline]
pub(crate) fn to_type(&self) -> Type {
syn::parse_str(self.0.as_str()).unwrap()
}

#[inline]
pub(crate) fn span(&self) -> Span {
self.1
}
}

impl ToTokens for HashType {
#[inline]
fn to_tokens(&self, token_stream: &mut proc_macro2::TokenStream) {
let ty = proc_macro2::TokenStream::from_str(self.0.as_str()).unwrap();

token_stream.extend(ty);
}
}
6 changes: 6 additions & 0 deletions src/common/tools/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ mod discriminant_type;

#[cfg(any(feature = "PartialOrd", feature = "Ord"))]
pub(crate) use discriminant_type::*;

#[cfg(feature = "Into")]
mod hash_type;

#[cfg(feature = "Into")]
pub(crate) use hash_type::*;
34 changes: 33 additions & 1 deletion src/common/type.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,38 @@
use std::collections::HashSet;

use syn::{Ident, Type, TypeParamBound};
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
Ident, Meta, Token, Type, TypeParamBound,
};

pub(crate) struct TypeWithPunctuatedMeta {
pub(crate) ty: Type,
pub(crate) list: Punctuated<Meta, Token![,]>,
}

impl Parse for TypeWithPunctuatedMeta {
#[inline]
fn parse(input: ParseStream) -> syn::Result<Self> {
let ty = input.parse::<Type>()?;

if input.is_empty() {
return Ok(Self {
ty,
list: Punctuated::new(),
});
}

input.parse::<Token![,]>()?;

let list = input.parse_terminated(Meta::parse, Token![,])?;

Ok(Self {
ty,
list,
})
}
}

#[inline]
pub(crate) fn find_idents_in_type<'a>(set: &mut HashSet<&'a Ident>, ty: &'a Type) {
Expand Down
Loading

0 comments on commit 6026ef3

Please sign in to comment.