use derive_where::derive_where;
use toasty::stmt::Path;
#[derive_where(Clone, Debug)]
pub struct Lens<M, T, P = T> {
path: Path<M, P>,
#[derive_where(skip(Debug))]
read: fn(&M) -> &T,
}
impl<M, T, P> Lens<M, T, P> {
#[doc(hidden)]
pub fn new(path: impl Into<Path<M, P>>, read: fn(&M) -> &T) -> Self {
Self {
path: path.into(),
read,
}
}
pub fn path(&self) -> &Path<M, P> {
&self.path
}
pub fn read<'a>(&self, record: &'a M) -> &'a T {
(self.read)(record)
}
pub(crate) fn reader(&self) -> fn(&M) -> &T {
self.read
}
}
impl<M, T, P> From<Lens<M, T, P>> for Path<M, P> {
fn from(lens: Lens<M, T, P>) -> Self {
lens.path
}
}
#[macro_export]
macro_rules! lens {
($($model:ident)::+ . $($field:ident).+) => {
$crate::Lens::new(
<$($model)::+>::fields()$(.$field())+,
|record: &$($model)::+| &record$(.$field)+,
)
};
}
#[derive_where(Clone, Debug, Default)]
pub struct Reads<M> {
fields: Vec<String>,
unresolved: bool,
#[derive_where(skip(Debug))]
_model: std::marker::PhantomData<fn() -> M>,
}
impl<M> Reads<M> {
pub(crate) fn fields(&self) -> &[String] {
&self.fields
}
pub(crate) fn is_unresolved(&self) -> bool {
self.unresolved
}
pub fn nothing() -> Self {
Self::default()
}
pub fn of<T>(path: impl Into<Path<M, T>>) -> Self
where
M: toasty::schema::Model,
{
Self::nothing().and(path)
}
pub fn and<T>(self, path: impl Into<Path<M, T>>) -> Self
where
M: toasty::schema::Model,
{
let path = crate::toasty_compat::model::ModelPath::of(&path.into());
match crate::toasty_compat::model::root_field::<M>(&path) {
Some(field) => self.named(field),
None => Self {
unresolved: true,
..self
},
}
}
pub(crate) fn of_predicate(predicate: &toasty::stmt::Expr<bool>) -> Self
where
M: toasty::schema::Model,
{
let (fields, unresolved) = crate::toasty_compat::predicate::root_fields::<M>(predicate);
let reads = fields.into_iter().fold(Self::nothing(), Self::named);
Self {
unresolved,
..reads
}
}
pub(crate) fn named(mut self, name: impl Into<String>) -> Self {
let name = name.into();
if !self.fields.contains(&name) {
self.fields.push(name);
}
self
}
pub(crate) fn union(&self, other: &Self) -> Self {
let mut union = other
.fields
.iter()
.fold(self.clone(), |reads, field| reads.named(field.clone()));
union.unresolved |= other.unresolved;
union
}
pub(crate) fn reads_any(&self, fields: &[String]) -> bool {
(self.unresolved && !fields.is_empty())
|| self.fields.iter().any(|field| fields.contains(field))
}
}
impl<M: toasty::schema::Model, T, P> From<Lens<M, T, P>> for Reads<M> {
fn from(lens: Lens<M, T, P>) -> Self {
Self::of(lens)
}
}
impl<M: toasty::schema::Model, T> From<Path<M, T>> for Reads<M> {
fn from(path: Path<M, T>) -> Self {
Self::of(path)
}
}
pub(crate) fn shows<M>(reads: Option<Reads<M>>, hidden: &[String]) -> bool {
reads.is_none_or(|reads| !reads.reads_any(hidden))
}