use borrowize::View;
use std::path::Path;
#[derive(Debug, PartialEq, Eq)]
struct Tag(&'static str);
#[derive(View)]
struct User {
id: u64,
name: String,
tags: Vec<Tag>,
}
#[test]
fn creates_default_borrowed_view() {
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")]);
}
#[test]
fn manual_default_view_matches_automatic_view() {
let user = User {
id: 8,
name: "Grace".to_owned(),
tags: vec![Tag("operator")],
};
let automatic = user.view();
let manual = UserView {
id: &user.id,
name: user.name.as_str(),
tags: user.tags.as_slice(),
};
assert_user_views_eq(&manual, &automatic);
}
fn assert_user_views_eq(left: &UserView<'_>, right: &UserView<'_>) {
assert_eq!(left.id, right.id);
assert_eq!(left.name, right.name);
assert_eq!(left.tags, right.tags);
}
#[derive(View)]
struct CommonTypes<'a> {
label: &'a str,
bytes: [u8; 3],
maybe_name: Option<String>,
maybe_id: Option<u64>,
boxed: Box<u64>,
path: std::path::PathBuf,
}
#[test]
fn maps_common_owned_types() {
let value = CommonTypes {
label: "source",
bytes: [1, 2, 3],
maybe_name: Some("Grace".to_owned()),
maybe_id: Some(11),
boxed: Box::new(13),
path: Path::new("/tmp/borrowize").to_path_buf(),
};
let view = value.view();
assert_eq!(view.label, "source");
assert_eq!(view.bytes, &[1, 2, 3]);
assert_eq!(view.maybe_name, Some("Grace"));
assert_eq!(view.maybe_id, Some(&11));
assert_eq!(*view.boxed, 13);
assert_eq!(view.path, Path::new("/tmp/borrowize"));
}
#[derive(View)]
struct TupleSource(String, Vec<Tag>, Option<u64>, &'static str);
#[test]
fn creates_borrowed_view_for_tuple_structs() {
let value = TupleSource(
"tuple".to_owned(),
vec![Tag("first"), Tag("second")],
Some(17),
"borrowed",
);
let view = value.view();
assert_eq!(view.0, "tuple");
assert_eq!(view.1, [Tag("first"), Tag("second")]);
assert_eq!(view.2, Some(&17));
assert_eq!(view.3, "borrowed");
}
#[test]
fn supports_manual_view_construction_with_visibility_overrides() {
let (id, name) = visibility_boundaries::sibling::public_field_access();
let restricted_data = visibility_boundaries::sibling::restricted_field_access();
assert_eq!(*id, 21);
assert_eq!(name, "manual");
assert_eq!(restricted_data, &[3, 5, 8]);
}
mod visibility_boundaries {
pub mod source {
use borrowize::View;
#[derive(View)]
#[borrowize(view_visibility = "pub", field_visibility = "pub", no_method)]
#[allow(dead_code)]
pub struct PublicSource {
id: u64,
name: String,
}
#[derive(View)]
#[borrowize(
view_name = "RestrictedView",
view_visibility = "pub",
field_visibility = "pub",
no_method
)]
#[allow(dead_code)]
pub struct RestrictedSource {
label: String,
#[borrowize(borrowed_type = "&'borrowize [u8]", visibility = "pub(super)")]
data: Vec<u8>,
}
}
pub mod sibling {
const ID: u64 = 21;
const DATA: &[u8] = &[3, 5, 8];
pub fn public_field_access() -> (&'static u64, &'static str) {
let view = super::source::PublicSourceView {
id: &ID,
name: "manual",
};
(view.id, view.name)
}
pub fn restricted_field_access() -> &'static [u8] {
let view = super::source::RestrictedView {
label: "restricted",
data: DATA,
};
view.data
}
}
}
#[test]
fn supports_manual_tuple_view_construction_with_visibility_overrides() {
let (id, name) = tuple_visibility_boundaries::sibling::public_field_access();
let restricted_data = tuple_visibility_boundaries::sibling::restricted_field_access();
assert_eq!(*id, 55);
assert_eq!(name, "tuple-manual");
assert_eq!(restricted_data, &[13, 21, 34]);
}
mod tuple_visibility_boundaries {
pub mod source {
use borrowize::View;
#[derive(View)]
#[borrowize(view_visibility = "pub", field_visibility = "pub", no_method)]
#[allow(dead_code)]
pub struct PublicSource(u64, String);
#[derive(View)]
#[borrowize(
view_name = "RestrictedTupleView",
view_visibility = "pub",
field_visibility = "pub",
no_method
)]
#[allow(dead_code)]
pub struct RestrictedSource(
String,
#[borrowize(borrowed_type = "&'borrowize [u8]", visibility = "pub(super)")] Vec<u8>,
);
}
pub mod sibling {
const ID: u64 = 55;
const DATA: &[u8] = &[13, 21, 34];
pub fn public_field_access() -> (&'static u64, &'static str) {
let view = super::source::PublicSourceView(&ID, "tuple-manual");
(view.0, view.1)
}
pub fn restricted_field_access() -> &'static [u8] {
let view = super::source::RestrictedTupleView("restricted", DATA);
view.1
}
}
}
#[derive(View)]
#[borrowize(method_name = "as_encoded_view", method_visibility = "pub")]
struct Encoded {
#[borrowize(
borrowed_type = "&'borrowize str",
generation_expression = "::std::str::from_utf8(&self.bytes).expect(\"test input is utf8\")"
)]
bytes: Vec<u8>,
}
#[test]
fn supports_method_and_generation_expression_overrides() {
let encoded = Encoded {
bytes: b"payload".to_vec(),
};
let view = encoded.as_encoded_view();
assert_eq!(view.bytes, "payload");
}
#[derive(View)]
#[borrowize(method_name = "as_tuple_encoded_view")]
struct TupleEncoded(
#[borrowize(
borrowed_type = "&'borrowize str",
generation_expression = "::std::str::from_utf8(&self.0).expect(\"test input is utf8\")"
)]
Vec<u8>,
);
#[test]
fn supports_tuple_struct_field_overrides() {
let encoded = TupleEncoded(b"payload".to_vec());
let view = encoded.as_tuple_encoded_view();
assert_eq!(view.0, "payload");
}
#[derive(View)]
enum Event<'a> {
Created {
id: u64,
name: String,
tags: Vec<Tag>,
borrowed: &'a Tag,
maybe_name: Option<String>,
maybe_borrowed: Option<&'a Tag>,
},
Empty,
}
#[derive(View)]
enum TupleEvent<'a> {
Created(String, Vec<Tag>, &'a Tag, Option<String>, Option<&'a Tag>),
Empty,
}
#[test]
fn creates_borrowed_view_for_named_enum_variants() {
let tag = Tag("primary");
let event = Event::Created {
id: 34,
name: "enum".to_owned(),
tags: vec![Tag("first"), Tag("second")],
borrowed: &tag,
maybe_name: Some("maybe".to_owned()),
maybe_borrowed: Some(&tag),
};
let view = event.view();
match view {
EventView::Created {
id,
name,
tags,
borrowed,
maybe_name,
maybe_borrowed,
} => {
assert_eq!(*id, 34);
assert_eq!(name, "enum");
assert_eq!(tags, [Tag("first"), Tag("second")]);
assert_eq!(borrowed, &tag);
assert_eq!(maybe_name, Some("maybe"));
assert_eq!(maybe_borrowed, Some(&tag));
}
EventView::Empty => panic!("expected created view"),
}
}
#[test]
fn creates_borrowed_view_for_tuple_enum_variants() {
let tag = Tag("primary");
let event = TupleEvent::Created(
"tuple-enum".to_owned(),
vec![Tag("first"), Tag("second")],
&tag,
Some("maybe".to_owned()),
Some(&tag),
);
let view = event.view();
match view {
TupleEventView::Created(name, tags, borrowed, maybe_name, maybe_borrowed) => {
assert_eq!(name, "tuple-enum");
assert_eq!(tags, [Tag("first"), Tag("second")]);
assert_eq!(borrowed, &tag);
assert_eq!(maybe_name, Some("maybe"));
assert_eq!(maybe_borrowed, Some(&tag));
}
TupleEventView::Empty => panic!("expected tuple event view"),
}
}
#[test]
fn creates_borrowed_view_for_unit_enum_variants() {
let event = Event::Empty;
let view = event.view();
match view {
EventView::Empty => {}
EventView::Created { .. } => panic!("expected empty view"),
}
let tuple_event = TupleEvent::Empty;
let tuple_view = tuple_event.view();
match tuple_view {
TupleEventView::Empty => {}
TupleEventView::Created(..) => panic!("expected empty tuple view"),
}
}
#[derive(View)]
#[allow(dead_code)]
enum GenericEnum<'a, T, const N: usize>
where
T: 'a,
{
Items {
values: Vec<T>,
borrowed: &'a T,
fixed: [u8; N],
},
Tuple(Vec<T>, &'a T, [u8; N]),
Empty,
}
#[test]
fn preserves_generics_and_where_clauses_on_enums() {
let item = 89;
let value = GenericEnum::Items {
values: vec![3, 5, 8],
borrowed: &item,
fixed: [1, 2, 3],
};
let view = value.view();
match view {
GenericEnumView::Items {
values,
borrowed,
fixed,
} => {
assert_eq!(values, &[3, 5, 8]);
assert_eq!(*borrowed, 89);
assert_eq!(fixed, &[1, 2, 3]);
}
GenericEnumView::Empty | GenericEnumView::Tuple(..) => panic!("expected items view"),
}
}
#[test]
fn preserves_generics_and_where_clauses_on_tuple_enum_variants() {
let item = 144;
let value = GenericEnum::Tuple(vec![8, 13], &item, [21, 34]);
let view = value.view();
match view {
GenericEnumView::Tuple(values, borrowed, fixed) => {
assert_eq!(values, &[8, 13]);
assert_eq!(*borrowed, 144);
assert_eq!(fixed, &[21, 34]);
}
GenericEnumView::Items { .. } | GenericEnumView::Empty => panic!("expected tuple view"),
}
}
#[derive(View)]
#[borrowize(view_name = "WireView", method_name = "as_wire_view")]
#[allow(dead_code)]
enum Wire {
Data {
#[borrowize(
borrowed_type = "&'borrowize str",
generation_expression = "::std::str::from_utf8(bytes).expect(\"test input is utf8\")"
)]
bytes: Vec<u8>,
},
Empty,
}
#[test]
fn supports_enum_field_overrides() {
let value = Wire::Data {
bytes: b"payload".to_vec(),
};
let view = value.as_wire_view();
match view {
WireView::Data { bytes } => assert_eq!(bytes, "payload"),
WireView::Empty => panic!("expected data view"),
}
}
#[derive(View)]
#[borrowize(view_name = "TupleWireView", method_name = "as_tuple_wire_view")]
#[allow(dead_code)]
enum TupleWire {
Data(
#[borrowize(
borrowed_type = "&'borrowize str",
generation_expression = "::std::str::from_utf8(field_0.as_slice()).expect(\"test input is utf8\")"
)]
Vec<u8>,
),
Empty,
}
#[test]
fn supports_tuple_enum_field_overrides() {
let value = TupleWire::Data(b"payload".to_vec());
let view = value.as_tuple_wire_view();
match view {
TupleWireView::Data(bytes) => assert_eq!(bytes, "payload"),
TupleWireView::Empty => panic!("expected data view"),
}
}
#[derive(View)]
struct Generic<'a, T, const N: usize>
where
T: 'a,
{
name: String,
items: Vec<T>,
borrowed: &'a T,
fixed: [u8; N],
}
#[test]
fn preserves_generics_and_where_clauses() {
let item = 42;
let value = Generic {
name: "generic".to_owned(),
items: vec![1, 2],
borrowed: &item,
fixed: [9, 8],
};
let view = value.view();
assert_eq!(view.name, "generic");
assert_eq!(view.items, &[1, 2]);
assert_eq!(*view.borrowed, 42);
assert_eq!(view.fixed, &[9, 8]);
}
#[derive(View)]
struct DefaultType<T = String> {
value: T,
}
#[derive(View)]
struct DefaultTupleType<T = String>(T);
#[test]
fn preserves_default_type_arguments_on_view_structs() {
let named = DefaultType {
value: "named-default".to_owned(),
};
let automatic = named.view();
let manual: DefaultTypeView<'_> = DefaultTypeView {
value: &named.value,
};
assert_eq!(automatic.value, manual.value);
let tuple = DefaultTupleType("tuple-default".to_owned());
let automatic = tuple.view();
let manual: DefaultTupleTypeView<'_> = DefaultTupleTypeView(&tuple.0);
assert_eq!(automatic.0, manual.0);
}
#[derive(View)]
struct DefaultConst<const N: usize = 3> {
values: [u8; N],
}
#[derive(View)]
struct DefaultTupleConst<const N: usize = 3>([u8; N]);
#[test]
fn preserves_default_const_arguments_on_view_structs() {
let named = DefaultConst { values: [1, 2, 3] };
let automatic = named.view();
let manual: DefaultConstView<'_> = DefaultConstView {
values: &named.values[..],
};
assert_eq!(automatic.values, manual.values);
let tuple = DefaultTupleConst([5, 8, 13]);
let automatic = tuple.view();
let manual: DefaultTupleConstView<'_> = DefaultTupleConstView(&tuple.0[..]);
assert_eq!(automatic.0, manual.0);
}
#[derive(View)]
struct GenericTuple<'a, T, const N: usize>(String, Vec<T>, &'a T, [u8; N])
where
T: 'a;
#[test]
fn preserves_generics_and_where_clauses_on_tuple_structs() {
let item = 233;
let value = GenericTuple("generic-tuple".to_owned(), vec![3, 5], &item, [8, 13]);
let view = value.view();
assert_eq!(view.0, "generic-tuple");
assert_eq!(view.1, &[3, 5]);
assert_eq!(*view.2, 233);
assert_eq!(view.3, &[8, 13]);
}