Skip to content

Commit

Permalink
Add paramless inline expression
Browse files Browse the repository at this point in the history
  • Loading branch information
Artem-Romanenia committed May 28, 2023
1 parent b41b07f commit 76acc8e
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 31 deletions.
106 changes: 91 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,38 @@
[<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)

## Content

- [Object to Object mapper for Rust](#object-to-object-mapper-for-rust)
- [Content](#content)
- [Brief description](#brief-description)
- [Examples](#examples)
- [Simplest Case](#simplest-case)
- [Different field name](#different-field-name)
- [Different field type](#different-field-type)
- [Nested structs](#nested-structs)
- [Nested collection](#nested-collection)
- [Assymetric fields (skipping and providing default values)](#assymetric-fields-skipping-and-providing-default-values)
- [Expressions](#expressions)
- [Expressions for struct level instructions](#expressions-for-struct-level-instructions)
- [Expressions for member level instructions](#expressions-for-member-level-instructions)
- [More rexamples](#more-rexamples)
- [Slightly complex example](#slightly-complex-example)
- [Flatened children](#flatened-children)
- [Tuple structs](#tuple-structs)
- [Struct kind hints](#struct-kind-hints)
- [Generics](#generics)
- [Where clauses](#where-clauses)
- [Mapping to multiple structs](#mapping-to-multiple-structs)
- [Avoiding proc macro attribute name collisions (alternative instruction syntax)](#avoiding-proc-macro-attribute-name-collisions-alternative-instruction-syntax)
- [Additional o2o instruction available via `#[o2o(...)]` syntax](#additional-o2o-instruction-available-via-o2o-syntax)
- [Primitive type conversions](#primitive-type-conversions)
- [Repeat instructions](#repeat-instructions)
- [Contributions](#contributions)
- [License](#license)

## Brief description

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

``` rust
Expand Down Expand Up @@ -40,7 +72,7 @@ With that, let's look at some examples.

## Examples

#### Simplest Case
### Simplest Case

``` rust
use o2o::o2o;
Expand Down Expand Up @@ -91,7 +123,7 @@ let dto = EntityDto { some_int: 123, another_int: 321 }
let entity: Entity = dto.into();
```

#### Different field name
### Different field name

``` rust
struct Entity {
Expand Down Expand Up @@ -129,7 +161,7 @@ struct EntityDto {
```
</details>

#### Different field type
### Different field type

``` rust
struct Entity {
Expand Down Expand Up @@ -194,7 +226,7 @@ struct EntityDto {
```
</details>

#### Nested structs
### Nested structs

``` rust
struct Entity {
Expand Down Expand Up @@ -242,7 +274,7 @@ struct ChildDto {
```
</details>

#### Nested collection
### Nested collection

``` rust
struct Entity {
Expand Down Expand Up @@ -307,7 +339,7 @@ struct ChildDto {
```
</details>

#### Assymetric fields (skipping and providing default values)
### Assymetric fields (skipping and providing default values)

**o2o** is able to handle scenarios when either of the structs has a field that the other struct doesn't have.

Expand Down Expand Up @@ -403,7 +435,51 @@ enum ZodiacSign {}
```
</details>

#### Slightly complex example
## Expressions

### Expressions for struct level instructions

```rust
#[ghost(field: { None })]

// field: None,

#[ghost(field: || { None })]

// field: (|| None)(),

#[ghost(field: { @.get_value() })]

// field: self.get_value(),

#[ghost(field: |x| { x.get_value() })]

// field: (|x: &Type| x.get_value())(&self),
```

### Expressions for member level instructions

```rust
#[map({ None })]

// field: None,

#[map(~.clone())]

// field: value.field.clone(),

#[map(@.get_value())]

// field: value.get_value(),

#[map(|x| x.get_value())]

// field: (|x: &Type| x.get_value())(&value),
```

## More rexamples

### Slightly complex example

``` rust
struct Employee {
Expand Down Expand Up @@ -482,7 +558,7 @@ impl EmployeeDto {
subordinate_of: (|x: &EmployeeDto| Box::new(x.reports_to.as_ref().into()))(&self),
subordinates: self.subordinates.iter().map(|p| Box::new(p.as_ref().into())).collect(),
first_name: (|x: &EmployeeDto| x.get_first_name())(&self),
last_name: (|x: &EmployeeDto| x.get_last_name())(&self),
last_name: self.get_last_name(),
}
}
}
Expand All @@ -493,14 +569,14 @@ impl EmployeeDto {
subordinate_of: (|x: &EmployeeDto| Box::new(x.reports_to.as_ref().into()))(self),
subordinates: self.subordinates.iter().map(|p| Box::new(p.as_ref().into())).collect(),
first_name: (|x: &EmployeeDto| x.get_first_name())(self),
last_name: (|x: &EmployeeDto| x.get_last_name())(self),
last_name: self.get_last_name(),
}
}
}
```
</details>

#### Flatened children
### Flatened children

When the instructions are put on the side that contains flatened properties, conversion `From<T>` and `IntoExisting<T>` only requires usage of a member level `#[child(...)]` instruction, which accepts a path to the unflatened field (*without* the field name itself).
``` rust
Expand Down Expand Up @@ -663,7 +739,7 @@ struct CarDto {
```
</details>

#### Tuple structs
### Tuple structs

``` rust
struct TupleEntity(i32, String);
Expand Down Expand Up @@ -726,7 +802,7 @@ struct EntityDto {
```
</details>

#### Struct kind hints
### Struct kind hints

By default, **o2o** will suppose that the struct on the other side is the same kind of struct that the original one is. In order to convert between named and tuple structs when you need to place instructions on a tuple side, you`ll need to use Struct Kind Hint:

Expand Down Expand Up @@ -760,7 +836,7 @@ struct EntityDto{
```
</details>

#### Generics
### Generics

``` rust
struct Entity<T> {
Expand Down Expand Up @@ -798,7 +874,7 @@ struct EntityDto {
```
</details>

#### Where clauses
### Where clauses

``` rust
struct Child<T> {
Expand Down Expand Up @@ -944,7 +1020,7 @@ struct EntityDto {

This syntax applies to all supported struct and member level instructions.

### Additional o2o instruction available via #[o2o(...)] syntax
### Additional o2o instruction available via `#[o2o(...)]` syntax

#### Primitive type conversions

Expand Down
22 changes: 16 additions & 6 deletions o2o-impl/src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ impl Parse for AsAttr {
pub(crate) enum Action {
InlineAtExpr(TokenStream),
InlineTildeExpr(TokenStream),
InlineExpr(TokenStream),
Closure(TokenStream),
ParamlessClosure(TokenStream),
}
Expand Down Expand Up @@ -760,7 +761,9 @@ fn try_parse_children(input: ParseStream) -> Result<Punctuated<ChildData, Token!

// Superficialy parses o2o Actions, when they are guaranteed to be the last thing in the stream.
fn try_parse_action(input: ParseStream) -> Result<Option<Action>> {
if input.peek(Token![@]) {
if input.is_empty() {
Ok(None)
} else if input.peek(Token![@]) {
input.parse::<Token![@]>()?;
return Ok(Some(Action::InlineAtExpr(input.parse()?)))
} else if input.peek(Token![~]) {
Expand All @@ -773,8 +776,11 @@ fn try_parse_action(input: ParseStream) -> Result<Option<Action>> {
validate_closure(input)?;
return Ok(Some(Action::Closure(input.parse()?)))
}
} else {
let content;
braced!(content in input);
return Ok(Some(Action::InlineExpr(content.parse()?)))
}
Ok(None)
}

// Rudimentarily parses |x| { x.something } flavor of closure. To be used when closure is not in the end of the stream.
Expand All @@ -799,19 +805,23 @@ fn parse_braced_action(input: ParseStream) -> Result<Action> {
braced!(content in input);

if inline {
content.parse::<Token![@]>()?;
if content.peek(Token![@]) {
content.parse::<Token![@]>()?;
} else {
paramless = true;
}

tokens.push(content.parse::<TokenStream>()?);
} else {
let content = content.parse::<TokenStream>()?;
tokens.push(quote!({#content}));
}



let token_stream = TokenStream::from_iter(tokens);

let cl = match (inline, paramless) {
(true, _) => Action::InlineAtExpr(token_stream),
(true, true) => Action::InlineExpr(token_stream),
(true, false) => Action::InlineAtExpr(token_stream),
(false, true) => Action::ParamlessClosure(token_stream),
(false, false) => Action::Closure(token_stream)
};
Expand Down
1 change: 1 addition & 0 deletions o2o-impl/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,7 @@ fn quote_action(action: &Action, field_path: Option<TokenStream>, ctx: &ImplCont
};
quote!(#path #args)
},
Action::InlineExpr(args) => args.clone(),
Action::Closure(args) => {
let ident = match ctx.kind {
Kind::FromOwned | Kind::FromRef => quote!(value),
Expand Down
8 changes: 4 additions & 4 deletions tests/12_ghost_attr_props.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ struct TupleEntityModel(i32, i16, i32, i16, f32);
#[derive(o2o)]
#[map(EntityModel)]
#[into_existing(EntityModel)]
#[ghost(ghost_int: |x| { x.some_int }, ghost_int_2: |x| { x.another_int as i16 }, ghost_float: || { 456.0 })]
#[ghost(ghost_int: |x| { x.some_int }, ghost_int_2: |x| { x.another_int as i16 }, ghost_float: { 456.0 })]
struct Entity {
some_int: i32,
another_int: i32,
Expand All @@ -38,7 +38,7 @@ struct Entity2 {
#[map(TupleEntityModel)]
#[into_existing(TupleEntityModel)]
#[o2o(ghost_owned(2: |x| { x.0 }, 3: |x| { x.1 as i16 }, 4: |_| { 456.0 }))]
#[o2o(ghost_ref(2: |x| { x.0 }, 3: |x| { x.1 as i16 }, 4: |_| { 4567.0 }))]
#[o2o(ghost_ref(2: |x| { x.0 }, 3: |x| { x.1 as i16 }, 4: { 4567.0 }))]
struct TupleEntity (i32, i16);

#[derive(o2o)]
Expand All @@ -52,7 +52,7 @@ struct EntityDto {
#[o2o(ghost(@.another_int as i16))]
ghost_int_2: i16,
#[o2o(ghost_owned(|| 456.0))]
#[o2o(ghost_ref(|| 4567.0))]
#[o2o(ghost_ref({4567.0}))]
ghost_float: f32,
}

Expand All @@ -66,7 +66,7 @@ struct TupleEntityDto(
i32,
#[ghost(@.1 as i16)]
i16,
#[o2o(ghost_owned(|_| 456.0))]
#[o2o(ghost_owned({456.0}))]
#[o2o(ghost_ref(|_| 4567.0))]
f32
);
Expand Down
8 changes: 4 additions & 4 deletions tests/13_multiple_ghost_attr_props.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct TupleEntityModel(i32, i16, i32, i16, f32);
ghost_owned(EntityModel|
ghost_int: |x| { x.some_int },
ghost_int_2: |x| { x.another_int as i16 },
ghost_float: || { 456.0 }
ghost_float: { 456.0 }
),
ghost_ref(EntityModel|
ghost_int: |x| { x.some_int },
Expand Down Expand Up @@ -50,7 +50,7 @@ struct Entity {
#[ghost(TupleEntityModel|
2: |x| { x.0 },
3: |x| { x.1 as i16 },
4: |_| { 456.0 }
4: { 456.0 }
)]
#[map(EntityModel as {})]
#[into_existing(EntityModel as {})]
Expand Down Expand Up @@ -84,7 +84,7 @@ struct EntityDto {
#[ghost(Entity| @.another_int as i16)]
#[ghost(TupleEntity| @.1)]
ghost_int_2: i16,
#[ghost(|_| 456.0)]
#[ghost({456.0})]
ghost_float: f32,
}

Expand All @@ -108,7 +108,7 @@ struct TupleEntityDto(
#[ghost(Entity| @.another_int as i16)]
i16,
#[o2o(ghost_owned(|| 456.0))]
#[o2o(ghost_ref(|| 4567.0))]
#[o2o(ghost_ref({4567.0}))]
f32
);

Expand Down
4 changes: 2 additions & 2 deletions tests/21_repeat_attr_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct Machine {
#[map(Car)]
#[into_existing(Car)]
#[children(vehicle: Vehicle, vehicle.machine: Machine)]
#[ghost(vehicle.machine@id: || { 321 })]
#[ghost(vehicle.machine@id: { 321 })]
struct CarDto {
number_of_doors: i8,

Expand All @@ -52,7 +52,7 @@ struct CarDto {
height: f32,
#[o2o(stop_repeat)]

#[o2o(repeat(ghost))] #[ghost(|| {123})]
#[o2o(repeat(ghost))] #[ghost({123})]
useless_param: i32,
useless_param_2: i32,
useless_param_3: i32,
Expand Down

0 comments on commit 76acc8e

Please sign in to comment.