use alux_shape::{FieldAlg, ShapeAlg, ShapeDeclareExt, ShapeExt, ShapeProgramAlg, ShapeProgramExt, Sorts, Words};
use alux_shape_text::TextShape;
use std::marker::PhantomData;
#[derive(Default)]
struct UserShapeProgram(PhantomData<()>);
impl<This> ShapeProgramAlg<This> for UserShapeProgram
where
This: ShapeAlg + FieldAlg,
{
type Ty = <This as Sorts>::Ty;
fn compile_shape(self, alg: &This) -> Self::Ty {
let _ = self;
let builder = alg;
let record = {
builder
.record(&["user"])
.field(&["display", "name"], builder.text())
.field(&["email"], builder.opt(builder.text()))
};
record.into_shape()
}
}
#[derive(Default)]
struct TimestampsShapeProgram(PhantomData<()>);
impl<This> ShapeProgramAlg<This> for TimestampsShapeProgram
where
This: ShapeAlg + FieldAlg,
{
type Ty = <This as Sorts>::Ty;
fn compile_shape(self, alg: &This) -> Self::Ty {
let _ = self;
let builder = alg;
let record = { builder.record(&["timestamps"]).field(&["created", "at"], builder.int(false, 64)) };
record.into_shape()
}
}
#[derive(Default)]
struct StoredUserShapeProgram(PhantomData<()>);
impl<This> ShapeProgramAlg<This> for StoredUserShapeProgram
where
This: ShapeAlg + FieldAlg,
UserShapeProgram: ShapeProgramAlg<This, Ty = <This as Sorts>::Ty>,
TimestampsShapeProgram: ShapeProgramAlg<This, Ty = <This as Sorts>::Ty>,
{
type Ty = <This as Sorts>::Ty;
fn compile_shape(self, alg: &This) -> Self::Ty {
let _ = self;
let builder = alg;
let record = {
builder
.record(&["stored", "user"])
.merge(builder.program(UserShapeProgram::default()))
.merge(builder.program(TimestampsShapeProgram::default()))
};
record.into_shape()
}
}
const USER: Words<'static> = &["user"];
const STORED_USER: Words<'static> = &["stored", "user"];
const TIMESTAMPS: Words<'static> = &["timestamps"];
const DISPLAY_NAME: Words<'static> = &["display", "name"];
const EMAIL: Words<'static> = &["email"];
const CREATED_AT: Words<'static> = &["created", "at"];
fn user<A>(alg: &A) -> A::Ty
where
A: ShapeAlg + FieldAlg,
{
let display_name = alg.field(DISPLAY_NAME, alg.text());
let email = alg.field(EMAIL, alg.opt(alg.text()));
alg.named_product(USER, vec![display_name, email])
}
fn timestamps<A>(alg: &A) -> A::Ty
where
A: ShapeAlg + FieldAlg,
{
alg.named_product(TIMESTAMPS, vec![alg.field(CREATED_AT, alg.int(false, 64))])
}
fn stored_user<A>(alg: &A) -> A::Ty
where
A: ShapeAlg + FieldAlg,
{
let members = vec![alg.merge(user(alg)), alg.merge(timestamps(alg))];
alg.named_product(STORED_USER, members)
}
#[test]
fn every_authoring_form_folds_to_one_shape() {
assert_eq!(TextShape.compile_shape(UserShapeProgram::default()), user(&TextShape));
assert_eq!(TextShape.compile_shape(TimestampsShapeProgram::default()), timestamps(&TextShape));
assert_eq!(TextShape.compile_shape(StoredUserShapeProgram::default()), stored_user(&TextShape));
}
#[test]
fn a_declaration_takes_its_name_from_the_declaration() {
assert_eq!(TextShape.compile_shape(UserShapeProgram::default()), "user { display_name: text, email: text? }",);
}
#[test]
fn a_nested_declaration_is_merged_and_not_nested() {
assert_eq!(
TextShape.compile_shape(StoredUserShapeProgram::default()),
"stored_user { ..user { display_name: text, email: text? }, \
..timestamps { created_at: u64 } }",
);
}