Skip to content

Commit

Permalink
item instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
Artem-Romanenia committed Jun 27, 2024
1 parent 224923d commit 2d75b6d
Show file tree
Hide file tree
Showing 5 changed files with 501 additions and 61 deletions.
112 changes: 112 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ And here's the code that `o2o` generates (from here on, generated code is produc
- [Define helper variables](#define-helper-variables)
- [Quick return](#quick-return)
- [Repeat trait instruction params](#repeat-trait-instruction-params)
- [Item attributes (attributes for `#[] impl`, `#[] fn`, `fn() { #![] }`)](#item-attributes-attributes-for--impl--fn-fn---)
- [Slightly complex example](#slightly-complex-example)
- [Flatened children](#flatened-children)
- [Tuple structs](#tuple-structs)
Expand All @@ -150,6 +151,7 @@ And here's the code that `o2o` generates (from here on, generated code is produc
- [Additional o2o instruction available via `#[o2o(...)]` syntax](#additional-o2o-instruction-available-via-o2o-syntax)
- [Primitive type conversions](#primitive-type-conversions)
- [Repeat member instructions](#repeat-member-instructions)
- ['Permeating' repeat for enum variant fields](#permeating-repeat-for-enum-variant-fields)
- [Enum Examples](#enum-examples)
- [Different variant name](#different-variant-name)
- [Different enum variant field names and types](#different-enum-variant-field-names-and-types)
Expand Down Expand Up @@ -971,6 +973,39 @@ struct MyError(String);
```
</details>

### Item attributes (attributes for `#[] impl`, `#[] fn`, `fn() { #![] }`)

``` rust
struct TestDto {
x: i32
}

#[derive(o2o::o2o)]
#[from_owned(TestDto|
impl_attribute(cfg(any(foo, bar))),
attribute(inline(always)),
inner_attribute(allow(unused_variables))
)]
struct Test {
x: i32,
}
```

<details>
<summary>View generated code</summary>

``` rust ignore
#[cfg(any(foo, bar))]
impl std::convert::From<TestDto> for Test {
#[inline(always)]
fn from(value: TestDto) -> Test {
#![allow(unused_variables)]
Test { x: value.x }
}
}
```
</details>

### Slightly complex example

``` rust
Expand Down Expand Up @@ -1740,6 +1775,83 @@ struct CarDto {
```
</details>

#### 'Permeating' repeat for enum variant fields

If you want repeat to be carried on on the fields of the following variants, you can use `permeate()` inside repeat instruction:
``` rust
enum Enum {
Var1 { field: i32, field_2: i32 },
Var2 { field_3: i32 },
Var3 { field_4: i32 },
Var4 { field_5: i32 },
Var5 { str: &'static str },
}

#[derive(o2o::o2o)]
#[map_owned(Enum)]
enum EnumDto {
Var1 {
#[o2o(repeat(permeate()))]
#[from(~ * 2)]
#[into(~ / 2)]

field: i32,
field_2: i32
},
Var2 { field_3: i32 },
Var3 { field_4: i32 },
Var4 { field_5: i32 },
Var5 { #[o2o(stop_repeat)] str: &'static str },
}
```

<details>
<summary>View generated code</summary>

``` rust ignore
impl std::convert::From<Enum> for EnumDto {
fn from(value: Enum) -> EnumDto {
match value {
Enum::Var1 { field, field_2 } => EnumDto::Var1 {
field: field * 2,
field_2: field_2 * 2,
},
Enum::Var2 { field_3 } => EnumDto::Var2 {
field_3: field_3 * 2,
},
Enum::Var3 { field_4 } => EnumDto::Var3 {
field_4: field_4 * 2,
},
Enum::Var4 { field_5 } => EnumDto::Var4 {
field_5: field_5 * 2,
},
Enum::Var5 { str } => EnumDto::Var5 { str: str },
}
}
}
impl std::convert::Into<Enum> for EnumDto {
fn into(self) -> Enum {
match self {
EnumDto::Var1 { field, field_2 } => Enum::Var1 {
field: field / 2,
field_2: field_2 / 2,
},
EnumDto::Var2 { field_3 } => Enum::Var2 {
field_3: field_3 / 2,
},
EnumDto::Var3 { field_4 } => Enum::Var3 {
field_4: field_4 / 2,
},
EnumDto::Var4 { field_5 } => Enum::Var4 {
field_5: field_5 / 2,
},
EnumDto::Var5 { str } => Enum::Var5 { str: str },
}
}
}
```
</details>

## Enum Examples

### Different variant name
Expand Down
178 changes: 123 additions & 55 deletions o2o-impl/src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,9 @@ pub(crate) struct TraitAttrCore {
pub repeat: Option<TraitRepeatFor>,
pub skip_repeat: bool,
pub stop_repeat: bool,
pub attribute: Option<TokenStream>,
pub impl_attribute: Option<TokenStream>,
pub inner_attribute: Option<TokenStream>,
}

impl TraitAttrCore {
Expand All @@ -482,25 +485,25 @@ impl TraitAttrCore {
if let Some(attr_to_repeat) = other.repeat {
if attr_to_repeat[&TraitAttrType::Vars] {
if self.init_data.is_some() {
Err(syn::Error::new(self.ty.span, "Vars will be overriden. Did you mean to use 'skip_repeat'?"))?
Err(syn::Error::new(self.ty.span, "Vars will be overriden. Did you forget to use 'skip_repeat'?"))?
}
self.init_data = other.init_data
}
if attr_to_repeat[&TraitAttrType::Update] {
if self.update.is_some() {
Err(syn::Error::new(self.update.span(), "Update statement will be overriden. Did you mean to use 'skip_repeat'?"))?
Err(syn::Error::new(self.update.span(), "Update statement will be overriden. Did you forget to use 'skip_repeat'?"))?
}
self.update = other.update
}
if attr_to_repeat[&TraitAttrType::QuickReturn] {
if self.quick_return.is_some() {
Err(syn::Error::new(self.quick_return.span(), "Quick Return statement will be overriden. Did you mean to use 'skip_repeat'?"))?
Err(syn::Error::new(self.quick_return.span(), "Quick Return statement will be overriden. Did you forget to use 'skip_repeat'?"))?
}
self.quick_return = other.quick_return
}
if attr_to_repeat[&TraitAttrType::DefaultCase] {
if self.default_case.is_some() {
Err(syn::Error::new(self.default_case.span(), "Default Case statement will be overriden. Did you mean to use 'skip_repeat'?"))?
Err(syn::Error::new(self.default_case.span(), "Default Case statement will be overriden. Did you forget to use 'skip_repeat'?"))?
}
self.default_case = other.default_case
}
Expand All @@ -525,65 +528,130 @@ impl Parse for TraitAttrCore {
None
};

let mut attr = TraitAttrCore { ty, err_ty, type_hint, init_data: None, update: None, quick_return: None, default_case: None, repeat: None, skip_repeat: false, stop_repeat: false, attribute: None, impl_attribute: None, inner_attribute: None };

if !input.peek(Token![|]){
return Ok(TraitAttrCore { ty, err_ty, type_hint, init_data: None, update: None, quick_return: None, default_case: None, repeat: None, skip_repeat: false, stop_repeat: false })
return Ok(attr)
}

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

let stop_repeat = if input.peek(kw::stop_repeat) {
input.parse::<kw::stop_repeat>()?;
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
true
while parse_trait_instruction_param(input, &mut attr)? {}

Ok(attr)
}
}

fn parse_trait_instruction_param(input: &syn::parse::ParseBuffer, attr: &mut TraitAttrCore) -> Result<bool> {
if input.peek(kw::stop_repeat) {
let a = input.parse::<kw::stop_repeat>()?;
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
if attr.stop_repeat {
Err(syn::Error::new(a.span, "Instruction parameter 'stop_repeat' was already set."))?
} else {
false
};
let skip_repeat = if input.peek(kw::skip_repeat) {
input.parse::<kw::skip_repeat>()?;
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
true
attr.stop_repeat = true;
return Ok(true)
}
} else if input.peek(kw::skip_repeat) {
let a = input.parse::<kw::skip_repeat>()?;
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
if attr.skip_repeat {
Err(syn::Error::new(a.span, "Instruction parameter 'skip_repeat' was already set."))?
} else {
false
};
let repeat = if input.peek(kw::repeat) {
input.parse::<kw::repeat>()?;
let content;
parenthesized!(content in input);
let repeat = Some(content.parse::<TraitRepeatForWrap>()?);
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
repeat
} else { None };
let init_data: Option<Punctuated<InitData, Token![,]>> = if input.peek(kw::vars) {
input.parse::<kw::vars>()?;
let content;
parenthesized!(content in input);
let vars = Some(Punctuated::parse_separated_nonempty(&content)?);
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
vars
} else { None };
let update = if input.peek(Token![..]) {
input.parse::<Token![..]>()?;
try_parse_action(input, true)?
} else { None };
let quick_return = if input.peek(Token![return]) {
input.parse::<Token![return]>()?;
try_parse_action(input, true)?
} else { None };
let default_case = if input.peek(Token![_]) {
input.parse::<Token![_]>()?;
try_parse_action(input, true)?
} else { None };

Ok(TraitAttrCore { ty, err_ty, type_hint, init_data, update, quick_return, default_case, repeat: repeat.map(|x| x.0), skip_repeat, stop_repeat })
attr.skip_repeat = true;
return Ok(true)
}
} else if input.peek(kw::repeat) {
let a = input.parse::<kw::repeat>()?;
let content;
parenthesized!(content in input);
let repeat = content.parse::<TraitRepeatForWrap>()?;
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
if attr.repeat.is_some() {
Err(syn::Error::new(a.span, "Instruction parameter 'repeat' was already set."))?
} else {
attr.repeat = Some(repeat.0);
return Ok(true)
}
} else if input.peek(kw::vars) {
let a = input.parse::<kw::vars>()?;
let content;
parenthesized!(content in input);
let vars = Some(Punctuated::parse_separated_nonempty(&content)?);
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
if attr.init_data.is_some() {
Err(syn::Error::new(a.span, "Instruction parameter 'vars' was already set."))?
} else {
attr.init_data = vars;
return Ok(true)
}
} else if input.peek(Token![..]) {
input.parse::<Token![..]>()?;
attr.update = try_parse_action(input, true)?;
return Ok(false)
} else if input.peek(Token![return]) {
input.parse::<Token![return]>()?;
attr.quick_return = try_parse_action(input, true)?;
return Ok(false)
} else if input.peek(Token![_]) {
input.parse::<Token![_]>()?;
attr.default_case = try_parse_action(input, true)?;
return Ok(false)
} else if input.peek(kw::attribute) {
let a = input.parse::<kw::attribute>()?;
let content;
parenthesized!(content in input);
let attribute = content.parse::<TokenStream>()?;
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
if attr.attribute.is_some() {
Err(syn::Error::new(a.span, "Instruction parameter 'attribute' was already set."))?
} else {
attr.attribute = Some(quote!(#[ #attribute ]));
return Ok(true)
}
}
else if input.peek(kw::impl_attribute) {
let a = input.parse::<kw::impl_attribute>()?;
let content;
parenthesized!(content in input);
let attribute = content.parse::<TokenStream>()?;
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
if attr.impl_attribute.is_some() {
Err(syn::Error::new(a.span, "Instruction parameter 'impl_attribute' was already set."))?
} else {
attr.impl_attribute = Some(quote!(#[ #attribute ]));
return Ok(true)
}
}
if input.peek(kw::inner_attribute) {
let a = input.parse::<kw::inner_attribute>()?;
let content;
parenthesized!(content in input);
let attribute = content.parse::<TokenStream>()?;
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
if attr.inner_attribute.is_some() {
Err(syn::Error::new(a.span, "Instruction parameter 'inner_attribute' was already set."))?
} else {
attr.inner_attribute = Some(quote!(#![ #attribute ]));
return Ok(true)
}
}

Ok(false)
}

#[derive(Clone)]
Expand Down
Loading

0 comments on commit 2d75b6d

Please sign in to comment.