# borrowize
`borrowize` provides `#[derive(View)]`, a small procedural macro for generating
borrowed view structs from owned structs.
The macro is useful when an API wants to expose a cheap borrowed projection of
an owned value without writing a second struct and conversion method by hand.
## Quick Start
```rust
use borrowize::View;
#[derive(Debug, PartialEq, Eq)]
struct Tag(&'static str);
#[derive(View)]
struct User {
id: u64,
name: String,
tags: Vec<Tag>,
}
let user = User {
id: 7,
name: "Ada".to_owned(),
tags: vec![Tag("admin"), Tag("editor")],
};
let view = user.view();
assert_eq!(*view.id, 7);
assert_eq!(view.name, "Ada");
assert_eq!(view.tags, [Tag("admin"), Tag("editor")]);
```
This generates a sibling view type similar to:
```rust
struct UserView<'borrowize> {
id: &'borrowize u64,
name: &'borrowize str,
tags: &'borrowize [Tag],
}
impl User {
fn view(&self) -> UserView<'_> {
UserView {
id: &self.id,
name: self.name.as_str(),
tags: self.tags.as_slice(),
}
}
}
```
The generated lifetime is always named `'borrowize` inside generated view type
definitions. Source structs may not define a lifetime with that name.
## Default Mapping
`borrowize` maps common owned field types to borrowed forms:
| `String` | `&'borrowize str` | `self.field.as_str()` |
| `Vec<T>` | `&'borrowize [T]` | `self.field.as_slice()` |
| `[T; N]` | `&'borrowize [T]` | `&self.field[..]` |
| `Box<T>` | `&'borrowize T` | `self.field.as_ref()` |
| `PathBuf` | `&'borrowize std::path::Path` | `self.field.as_path()` |
| `Option<T>` | `Option<Borrowed<T>>` | `self.field.as_ref().map(...)` |
| `&'a T` | `&'a T` | `self.field` |
| other `T` | `&'borrowize T` | `&self.field` |
`Option<T>` is mapped recursively, so `Option<String>` becomes
`Option<&'borrowize str>` and `Option<u64>` becomes `Option<&'borrowize u64>`.
Type matching is syntactic. The macro recognises unqualified names and common
standard-library qualified names such as `std::string::String`,
`alloc::string::String`, `std::vec::Vec`, `alloc::vec::Vec`,
`std::option::Option`, `core::option::Option`, `std::boxed::Box`,
`alloc::boxed::Box`, and `std::path::PathBuf`. Type aliases are treated as
unknown types and are borrowed as `&T`.
## Struct Options
Struct-level options are written as `#[borrowize(...)]` on the source struct.
Names are explicit; there are no shorthand options.
```rust
use borrowize::View;
#[derive(View)]
#[borrowize(
view_name = "UserParts",
view_visibility = "pub",
field_visibility = "pub",
method_name = "as_parts",
method_visibility = "pub"
)]
pub struct User {
id: u64,
name: String,
}
```
Supported struct options:
| `view_name = "Name"` | Override the generated view type name. |
| `view_visibility = "pub"` | Override the generated view type visibility. |
| `field_visibility = "pub"` | Set the default visibility for generated view fields. |
| `method_name = "name"` | Override the generated method name. |
| `method_visibility = "pub"` | Override the generated method visibility. |
| `no_method` | Do not generate the inherent view method. |
When a visibility option is omitted, visibility is inherited from the source
item or field. Explicit visibility values must be legal Rust visibility syntax,
such as `pub`, `pub(crate)`, or `pub(super)`.
`no_method` cannot be combined with `method_name` or `method_visibility`.
## Field Options
Field-level options are written as `#[borrowize(...)]` on a source field.
```rust
use borrowize::View;
#[derive(View)]
struct Encoded {
#[borrowize(
borrowed_type = "&'borrowize str",
generation_expression = "::std::str::from_utf8(&self.bytes).expect(\"valid utf8\")"
)]
bytes: Vec<u8>,
}
```
Supported field options:
| `borrowed_type = "Type"` | Override the generated view field type. |
| `generation_expression = "expr"` | Override the generated field initializer in the view method. |
| `visibility = "pub"` | Override this generated view field's visibility. |
`borrowed_type` overrides may refer to the generated borrow lifetime as
`'borrowize`.
`generation_expression` is parsed as a Rust expression in the generated method
body. It can refer to `self` because the generated method borrows `&self`.
## Manual View Construction
Generated view fields inherit source field visibility by default. Use
`field_visibility` or field-level `visibility` when views need to be assembled
outside the source module.
```rust
use borrowize::View;
#[derive(View)]
#[borrowize(view_visibility = "pub", field_visibility = "pub", no_method)]
pub struct PublicSource {
id: u64,
name: String,
}
let id = 21;
let view = PublicSourceView {
id: &id,
name: "manual",
};
```
This is useful when you have borrowed pieces available before you have an owned
source value to call `view()` on.
## Generics
Source generics, const generics, lifetimes, and where clauses are preserved on
the generated view type and inherent method.
```rust
use borrowize::View;
#[derive(View)]
struct Generic<'a, T, const N: usize>
where
T: 'a,
{
name: String,
items: Vec<T>,
borrowed: &'a T,
fixed: [u8; N],
}
```
## Current Limitations
`View` currently supports named-field structs only. Enums, unions, tuple
structs, and unit structs produce compile errors.
The generated inherent method can conflict with a user-defined method of the
same name. Use `method_name` to choose a different method name or `no_method`
when only the view type is needed.
Nested custom view conversion is not inferred. Unknown types are borrowed as
`&T`, even if they also derive `View`.
Type matching is syntactic, so aliases and re-exports of recognised container
types are not expanded.
The generated lifetime name `'borrowize` is reserved.
## Licence
`borrowize` is distributed under the terms of both the MIT licence and the
Apache License, Version 2.0. You may use it under either licence, at your
option.
See `LICENSE-MIT` and `LICENSE-APACHE` for the full licence texts.
## Contributions
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in `borrowize` is licensed as MIT OR Apache-2.0, without any
additional terms or conditions.