Skip to content

Commit

Permalink
Features/try (#10)
Browse files Browse the repository at this point in the history
Fallible conversions impl
  • Loading branch information
Artem-Romanenia authored Jun 20, 2024
1 parent b1f37f8 commit a6412df
Show file tree
Hide file tree
Showing 41 changed files with 9,842 additions and 137 deletions.
103 changes: 73 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Object to Object mapper for Rust. Derive `From` and `Into` traits.<!-- omit from toc -->
Object to Object mapper for Rust. Derive `(Try)From`, and `(Try)Into` traits.<!-- omit from toc -->
================================
[<img alt="github.com" src="https://github.com/Artem-Romanenia/o2o/workflows/Build/badge.svg" height="25">](https://github.com/Artem-Romanenia/o2o/)
[<img alt="crates.io" src="https://img.shields.io/crates/v/o2o.svg?style=for-the-badge&color=2f4d28&labelColor=f9f7ec&logo=rust&logoColor=black" height="25">](https://crates.io/crates/o2o)
Expand Down Expand Up @@ -33,7 +33,7 @@ struct Person {

#[derive(o2o)]
#[from_owned(Person)] // This tells o2o to generate 'From<Person> for PersonDto' implementation
#[owned_into(Person)] // This generates 'Into<Person> for PersonDto'
#[owned_try_into(Person, std::io::Error)] // This generates 'TryInto<Person> for PersonDto' with type Error = std::io::Error
struct PersonDto {
id: u32,
name: String,
Expand All @@ -50,11 +50,11 @@ assert_eq!(dto.id, 123); assert_eq!(dto.name, "John"); assert_eq!(dto.age, 42);
// and this:

let dto = PersonDto { id: 321, name: "Jack".into(), age: 23 };
let person: Person = dto.into();
let person: Person = dto.try_into().unwrap();

assert_eq!(person.id, 321); assert_eq!(person.name, "Jack"); assert_eq!(person.age, 23);

// Starting from v0.4.2 o2o also supports enums:
// o2o also supports enums:

enum Creature {
Person(Person),
Expand All @@ -66,7 +66,7 @@ enum Creature {
#[derive(o2o)]
#[from_owned(Creature)]
enum CreatureDto {
Person(#[map(~.into())] PersonDto),
Person(#[from(~.into())] PersonDto),
Cat { nickname: String },
Dog(String),
Other
Expand All @@ -92,13 +92,14 @@ And here's the code that `o2o` generates (from here on, generated code is produc
}
}
}
impl std::convert::Into<Person> for PersonDto {
fn into(self) -> Person {
Person {
impl std::convert::TryInto<Person> for PersonDto {
type Error = std::io::Error;
fn try_into(self) -> Result<Person, std::io::Error> {
Ok(Person {
id: self.id,
name: self.name,
age: self.age,
}
})
}
}

Expand All @@ -115,6 +116,13 @@ And here's the code that `o2o` generates (from here on, generated code is produc
```
</details>

## Some milestones<!-- omit from toc -->

* **v0.4.4** Fallible conversions
* **v0.4.3** Enum-to-primitive type conversions with `#[literal(...)]` and `#[pattern(...)]`
* **v0.4.2** Basic enum conversions
* **...**

## Content<!-- omit from toc -->

- [Traits and `o2o` *trait instructions*](#traits-and-o2o-trait-instructions)
Expand Down Expand Up @@ -162,28 +170,46 @@ struct Entity { }
struct EntityDto { }
```

o2o procedural macro is able to generate implementation of 6 kinds of traits:
o2o procedural macro is able to generate implementation of 12 kinds of traits:

``` rust ignore
// When applied to a struct B:

// #[from_owned(A)]
impl std::convert::From<A> for B { ... }

// #[try_from_owned(A)]
impl std::convert::TryFrom<A> for B { ... }

// #[from_ref(A)]
impl std::convert::From<&A> for B { ... }

// #[try_from_ref(A)]
impl std::convert::TryFrom<&A> for B { ... }

// #[owned_into(A)]
impl std::convert::Into<A> for B { ... }

// #[try_owned_into(A)]
impl std::convert::TryInto<A> for B { ... }

// #[ref_into(A)]
impl std::convert::Into<A> for &B { ... }

// #[try_ref_into(A)]
impl std::convert::TryInto<A> for &B { ... }

// #[owned_into_existing(A)]
impl o2o::traits::IntoExisting<A> for B { ... }

// #[owned_try_into_existing(A)]
impl o2o::traits::TryIntoExisting<A> for B { ... }

// #[ref_into_existing(A)]
impl o2o::traits::IntoExisting<A> for &B { ... }

// #[ref_try_into_existing(A)]
impl o2o::traits::TryIntoExisting<A> for &B { ... }
```

o2o also has shortcuts to configure multiple trait implementations with fewer lines of code:
Expand Down Expand Up @@ -218,6 +244,8 @@ struct Entity { }
struct EntityDto { }
```

**Exactly the same shortcuts apply to *fallible* conversions.**

## The (not so big) Problem

This section may be useful for people which are not very familiar with Rust's procedural macros and it explains why some things are done the way they're done.
Expand Down Expand Up @@ -313,7 +341,7 @@ impl std::convert::Into<Entity> for EntityDto {
}
```

Obviously, you can use `~` for inline expressions that are passed only to member level o2o instructions, while `@` can be used at both member and type level.
You can use `~` for inline expressions that are passed only to member level o2o instructions, while `@` can be used at both member and type level.

So finally, let's look at some examples.

Expand Down Expand Up @@ -403,13 +431,14 @@ struct Entity {
}

#[derive(o2o)]
#[map(Entity)]
#[from(Entity)]
#[try_into(Entity, std::num::ParseIntError)]
struct EntityDto {
some_int: i32,
#[map_ref(@.str.clone())]
str: String,
#[from(~.to_string())]
#[into(~.parse::<i16>().unwrap())]
#[into(~.parse::<i16>()?)]
val: String
}
```
Expand All @@ -435,22 +464,24 @@ struct EntityDto {
}
}
}
impl std::convert::Into<Entity> for EntityDto {
fn into(self) -> Entity {
Entity {
impl std::convert::TryInto<Entity> for EntityDto {
type Error = std::num::ParseIntError;
fn try_into(self) -> Result<Entity, std::num::ParseIntError> {
Ok(Entity {
some_int: self.some_int,
str: self.str,
val: self.val.parse::<i16>().unwrap(),
}
val: self.val.parse::<i16>()?,
})
}
}
impl std::convert::Into<Entity> for &EntityDto {
fn into(self) -> Entity {
Entity {
impl std::convert::TryInto<Entity> for &EntityDto {
type Error = std::num::ParseIntError;
fn try_into(self) -> Result<Entity, std::num::ParseIntError> {
Ok(Entity {
some_int: self.some_int,
str: self.str.clone(),
val: self.val.parse::<i16>().unwrap(),
}
val: self.val.parse::<i16>()?,
})
}
}
```
Expand Down Expand Up @@ -776,12 +807,19 @@ use o2o::o2o;

#[derive(o2o)]
#[owned_into(String| return @.0.to_string())]
struct Wrapper(i32);
#[try_from_owned(String, std::num::ParseIntError)]
struct Wrapper(#[from(@.parse::<i32>()?)]i32);
```
<details>
<summary>View generated code</summary>

``` rust ignore
impl std::convert::TryFrom<String> for Wrapper {
type Error = std::num::ParseIntError;
fn try_from(value: String) -> Result<Wrapper, std::num::ParseIntError> {
Ok(Wrapper(value.parse::<i32>()?))
}
}
impl std::convert::Into<String> for Wrapper {
fn into(self) -> String {
self.0.to_string()
Expand Down Expand Up @@ -1386,34 +1424,39 @@ struct Entity {
}
// =====================================================================
#[derive(o2o)]
#[map(Entity)]
#[from(Entity)]
#[try_into(Entity, std::num::ParseIntError)]
struct EntityDto1 {
some_int: i32,
#[from(~.to_string())]
#[into(~.parse::<i16>().unwrap())]
#[into(~.parse::<i16>()?)]
val: String,
#[map_ref(~.clone())]
str: String
}
// =====================================================================
#[derive(o2o)]
#[o2o(map(Entity))]
#[o2o(from(Entity))]
#[o2o(try_into(Entity, std::num::ParseIntError))]
struct EntityDto2 {
some_int: i32,
#[o2o(from(~.to_string()))]
#[o2o(into(~.parse::<i16>().unwrap()))]
#[o2o(into(~.parse::<i16>()?))]
val: String,
#[o2o(map_ref(~.clone()))]
str: String
}
// =====================================================================
#[derive(o2o)]
#[o2o(map(Entity))]
#[o2o(
from(Entity),
try_into(Entity, std::num::ParseIntError)
)]
struct EntityDto3 {
some_int: i32,
#[o2o(
from(~.to_string()),
into(~.parse::<i16>().unwrap()),
try_into(~.parse::<i16>()?),
)]
val: String,
#[o2o(map_ref(~.clone()))]
Expand Down
Loading

0 comments on commit a6412df

Please sign in to comment.