Skip to content

Commit

Permalink
Updated docs
Browse files Browse the repository at this point in the history
  • Loading branch information
NexRX committed Mar 11, 2024
1 parent fa040a3 commit 7963438
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 76 deletions.
191 changes: 117 additions & 74 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,131 72,174 @@ struct User {
Continue reading for the available models and their breakdown and arguments in general.


## Common Arguments
These are arguments that can be applied to all models.

- `name` - The name of the struct the generate [**Required**, **Must be first** e.g. `MyStruct`]
- `fields` [**Only One**]
- `fields` - A _list_ of field names in the original structure to carry over [e.g. `fields(field1, field2, ...)`]
- `omit` - A _list_ of field names in the original structure to omit [e.g. `omit(field1, field2, ...)`]
- `derive` - A _list_ of derivables to derive on the generated struct just like you would normally [e.g. `derive(Clone, Debug, thiserror::Error)`]
- `preset` - A _string literal_ of the preset to use, presets are a set of defaults to apply to a model. *Below is a list of what arguments are composed in a preset.* [e.g. `preset = "none"`]
- **none** - **Default**, does nothing and is the default behaviour
- **write** *['openapi' Feature Flag]* - Designed to only show properties that can be written to.
- `omit` - Applied as a base, any fields with `#[oai(read_only)]` attribute are removed, your fields/omit is applied after
- `option` **patch only** - Arg defaults to `MaybeUndefined`
# Models
These are arguments that can be applied there respective Attributes (i.e. `#[attr(args...)])`).

- **read** *['openapi' Feature Flag]* - Designed to only show properties that can always be read.
- `omit` - Applied as a base, any fields with `#[oai(write_only)]` attribute are removed, your fields/omit is applied after
- `option` **patch only** - arg defaults to `MaybeUndefined`
## `#[model]`
Used to define a *base* or *default* set of arguments to be apply to all models. Acts as a interface for taking arguments to apply more broad and *doesn't* generate any models on itself.

- `attributes_with` - A _string literal_ of the attributes to inherit at both struct & field level. *Below is a list of values.* [e.g. `attributes_with = "none"`]
- **none** - Does not consider any attributes [**Default**]
- **oai** *['openapi' Feature Flag]* - Composes all Poem's OpenAPI attributes
- **deriveless** - Composes all attributes but omits the derive attributes
- **all** - Composes all attributes
There are two arguments possible
### base
A *list* of non-overridable arguments that are applied to all generated arguments that you cab build on-top off. It doesn't prevent you from using the in individual models later but it also won't allow you to undo the effect individually.

<br/>
e.g. `#[model(base(...)]`

# Models
Derivable models via the struct attributes.
| Argument Name | description | Required? | Type/Enum | Example |
|-----------------|----------------------------------------------------|------------|-------------------------|-----------------------------------|
| **fields** or | Field names in the original structure to include | False | List(Ident) | `fields(field1, field2, ...)` |
| **omit** | Field names in the original structure to exclude | False | List(Ident) | `omit(field1, field2, ...)` |
| derive | Things to derive on the newly generated struct | False | List(Path) | `derive(Debug, thiserror::Error)` |

### defaults
Arguments given in this list are applied to all models where the argument isn't given. Meaning, if writen `#[model(defaults(fields(a, b)))]` and then later `#[view(omit(b))]` is written, the `fields(a, b)` earlier will not be applied because the two args are mutally exclusive unlike with *base* arguments.

## **Patch**
A subset model where every field's type is an option in some way. It's called patch because it reflect a REST / DB patch of the original struct.
e.g. `#[model(defaults(...))]`

#### **Unique Args**
- `option` - A _Identifer_ that allows a different option implementation from supported crates [e.g. `option = MaybeUndefined` (from poem-openapi)]
- **Option** - The standard implementation of Option [**Default**]
- **MaybeUndefined** *['openapi' Feature Flag]* - Use Poem's OpenAPI crate and it's Option implmentation
| Argument Name | description | Required? | Type/Enum | Example |
|-----------------|----------------------------------------------------|------------|-------------------------|-----------------------------------|
| **fields** or | Field names in the original structure to include | False | List(Ident) | `fields(field1, field2, ...)` |
| **omit** | Field names in the original structure to exclude | False | List(Ident) | `omit(field1, field2, ...)` |
| derive | Things to derive on the newly generated struct | False | List(Path) | `derive(Debug, thiserror::Error)` |
| preset | Behaviours and/or defaults to apply | False | none/write/read | `preset = "all"` |
| attributes_with | Attributes to inherit at both struct & field level | False | none/oai/deriveless/all | `attributes_with = "all"` |

#### **Example**

### Example
```rust
#[derive(restructed::Models)]
#[patch(UserUpdate, omit(id), option = Option)]
#[derive(Clone, restructed::Models)]
#[model(base(derive(Debug)))] // All models now *MUST* derive Debug (despite parent)
#[view(UserView)]
#[patch(UserPatch)]
struct User {
id: i32,
display_name: String,
bio: String,
extra: Option<String>,
password: String,
}
```

will expand into something like this:
```rust
struct UserUpdate {
display_name: Option<String>,
bio: Option<String>,
extra: Option<Option<String>>,
password: Option<String>,
fn debug_models() {
let user = User {
id: 1,
display_name: "Dude".to_string(),
bio: "Too long didn't read".to_string(),
extra: None,
password: "ezpz".to_string(),
};

let view: UserView = user.clone().into(); // Automatically gen from model
print!("A view of a user {:?}", view);

let patch: UserPatch = user.clone().into(); // Automatically gen from model
print!("A patch of a user {:?}", patch);
}
```

## **View**
A simple subset of the deriving model/struct.
## `#[view]`
A model with generates a subset of the original/parent deriving `Model`. Useful for creating things like RESTful API or Database views.

#### **Unique Args**
- N/A
| Argument Name | description | Required? | Type/Enum | Example |
|-----------------|----------------------------------------------------|------------|-------------------------|-----------------------------------|
| name | Name of the struct the generate | True First | Identifier | `MyStruct` |
| **fields** or | Field names in the original structure to include | False | List(Ident) | `fields(field1, field2, ...)` |
| **omit** | Field names in the original structure to exclude | False | List(Ident) | `omit(field1, field2, ...)` |
| derive | Things to derive on the newly generated struct | False | List(Path) | `derive(Debug, thiserror::Error)` |
| preset | Behaviours and/or defaults to apply | False | none/write/read | `preset = "all"` |
| attributes_with | Attributes to inherit at both struct & field level | False | none/oai/deriveless/all | `attributes_with = "all"` |

#### **Example**
```rust
#[derive(restructed::Models)]
#[view(UserPublic, fields(display_name, bio))]
#[derive(Clone, restructed::Models)]
#[view(UserProfile, omit(id, password))]
struct User {
id: i32,
id: i32, // Not in `UserProfile`
display_name: String,
bio: String,
extra: Option<String>,
password: String,
password: String, // Not in `UserProfile`
}
```

will expand into something like this:
## `#[patch]`
A model which creates subsets of your data except each field's type is wrapped in a `Option<t>` or a alternative type of Option implementation if specified. Useful for creating RESTful API patch method types or Database Table Patches where you only want to update fields if they were explictly given (even to delete).

| Argument Name | description | Required? | Type/Enum | Example |
|-----------------|----------------------------------------------------|------------|-------------------------|-----------------------------------|
| name | Name of the struct the generate | True First | Identifier | `MyStruct` |
| **fields** or | Field names in the original structure to include | False | List(Ident) | `fields(field1, field2, ...)` |
| **omit** | Field names in the original structure to exclude | False | List(Ident) | `omit(field1, field2, ...)` |
| derive | Things to derive on the newly generated struct | False | List(Path) | `derive(Debug, thiserror::Error)` |
| preset | Behaviours and/or defaults to apply | False | none/write/read | `preset = "all"` |
| attributes_with | Attributes to inherit at both struct & field level | False | none/oai/deriveless/all | `attributes_with = "all"` |
| option | A alternative to `Option<T>` to wrap fields with | False | Option/MaybeUndefined | `option = MaybeUndefined` |

```rust
struct UserPublic {
display_name: String,
bio: String,
#[derive(Clone, restructed::Models)]
#[patch(UserUpdate, fields(display_name, bio, extra, password))]
struct User {
id: i32, // Not in `UserUpdate`
display_name: String, // Option<String> in `UserUpdate`
bio: String, // Option<String> in `UserUpdate`
extra: Option<String>, // Option<Option<String>> in `UserUpdate` (If this isn't desired, see *option* arg and the *openapi* crate feature)
password: String, // Not in `UserProfile`
}
```

<br/>

# Complex Example
Just to demonstrate the versitility of this crate, here is an example using all the possible arguments at once using all features.
# Argument Behaviours

## Poem OpenAPI
Each attribute is copied over so all your validations are kept
## `preset`
A _string literal_ of the preset to use, presets are a set of defaults to apply to a model. *Below is a list of what arguments are composed in a preset.* [e.g. `preset = "none"`]
- **none** - Does nothing and is the default behaviour [**Default**]
- **write** *['openapi' Feature Flag]* - Designed to only show properties that can be written to.
- `omit` - Applied as a base, any fields with `#[oai(read_only)]` attribute are removed, your fields/omit is applied after
- `option` **patch only** - Arg defaults to `MaybeUndefined`

- **read** *['openapi' Feature Flag]* - Designed to only show properties that can always be read.
- `omit` - Applied as a base, any fields with `#[oai(write_only)]` attribute are removed, your fields/omit is applied after
- `option` **patch only** - arg defaults to `MaybeUndefined`

## `attributes_with`
A _string literal_ of the attributes to inherit at both struct & field level. *Below is a list of values.* [e.g. `attributes_with = "none"`]
- **none** - Does not Includes any attributes [**Default**]
- **oai** *['openapi' Feature Flag]* - Includes all Poem's OpenAPI attributes
- **deriveless** - Includes all attributes but omits the derive attributes
- **all** - Includes all attributes

# Known Limitations

- *Generic Structs & Enums* - At the moment, this crate **doesn't support** deriving models on Structs that need to be generic (e.g. deriving on a `Struct<T>`). I just don't need the feature, contributions are welcome however!

<br/>

# Crate Features
Links are to other crates GitHub page that are related to the features.<br/>

## Poem OpenAPI
Enables wrapping `Option<T>` from the source struct with `MaybeUndefined<T>` from the [poem-openapi](https://github.com/poem-web/poem/tree/master/poem-openapi) crate in `patch` models. All `oai(...)` attributes can also be explictly copied over to the generated struct meaning you keep all validators, etc..

```rust
use restructed::Models;

#[cfg(test)] // For rust_doc test purposes
#[derive(poem::Object, Models)]
#[derive(poem_openapi::Object, Models)]
#[oai(skip_serializing_if_is_none, rename_all = "camelCase")]
#[patch(UserUpdate, preset = "write", derive(Object))]
#[view(UserProfile, preset = "view", derive(Object))]
#[view(UserNames, preset = "view", derive(Object))]
#[model(base(derive(poem_openapi::Object, Debug)), defaults(preset = "read"))]
#[patch(UserUpdate, preset = "write")]
#[view(UserProfile)]
#[view(UserNames, fields(username, name, surname))]
pub struct User {
#[oai(read_only)]
pub id: u32,
// profile
#[oai(validator(min_length = 3, max_length = 16, pattern = r"^[a-zA-Z0-9_]*$"))]
#[oai(validator(min_length = 3, max_length = 16, pattern = r"^[a-zA-Z0-9_]*$"))] // oai attributes carry over with `preset = write/write` or attributes_with="oai"
pub username: String,
#[oai(validator(min_length = 5, max_length = 1024), write_only)]
pub password: String,
#[oai(validator(min_length = 2, max_length = 16, pattern = r"^[a-zA-Z\s]*$"))]
pub name: Option<String>,
#[oai(validator(min_length = 2, max_length = 16, pattern = r"^[a-zA-Z\s]*$"))]
pub surname: Option<String>,
pub surname: Option<String>, // in patch modeels, this is `MaybeUndefined` type with default with preset `read` or `write` (or option = MaybeUndefined)
#[oai(read_only)]
pub joined: u64,
}
```

<br/>

# Crate Features
Links are to other crates GitHub page that are related to the features.<br/>

## Poem OpenAPI
Enables wrapping `Option<T>` from the source struct with `MaybeUndefined<T>` from the [poem-openapi](https://github.com/poem-web/poem/tree/master/poem-openapi) crate in `patch` models. All `oai(...)` attributes can also be explictly copied over to the generated struct meaning you keep all validators, etc..
```
4 changes: 2 additions & 2 deletions tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 1,3 @@
//! Entry point for integration tests.
//! Entry point for integration/feature tests.
mod feature_testing;

mod feature_testing;

0 comments on commit 7963438

Please sign in to comment.