use crate::{
	Schema,
	Table
};

/// Indicate that a [`Table`] is *part of* a [`Schema`]
///
/// Do not implement this trait manually, it will be implemented by the [`#[database]`](crate::database) proc-macro.
///
/// The unit type `()` is used to mean "this [`Table`] does not reference anything" -- as such it counts as "part of" every [`Schema`].
pub trait PartOf<S: Schema> {}
impl<S: Schema> PartOf<S> for () {}

/// Check that every type in a nested tuple is [`PartOf`] the given [`Schema`]
///
/// This "unrolls" the nested type.
pub trait ValidFor<S: Schema> {}

impl<S: Schema> ValidFor<S> for () {}
impl<S: Schema, T: Table> ValidFor<S> for T
	where T: PartOf<S>
{}
impl<S: Schema, T: Table, L> ValidFor<S> for (T, L)
	where T: ValidFor<S>,
		L: ValidFor<S>
{}

/// Check that every type that a [`Table`] references is part of [`Schema`] `S`
///
/// Do not implement this trait, just make sure your [`Table`]s only contain valid references.
pub trait RefsArePartOf<S: Schema> {}
impl<S: Schema, T: Table> RefsArePartOf<S> for T
	where T::References: ValidFor<S>
{}

/// Check whether a [`Table`] is valid for use in a given [`Schema`]
///
/// A Table is valid IFF all of it's fields only reference tables that are also part of the [`Schema`].
///
/// An `impl` for this is generated by the [`#[database]`](crate::database) proc-macro.
/// This then causes the compiler to verify that [`RefsArePartOf`] is implemented for the given [`Table`] & [`Schema`] pairing.
pub trait IsValidFor<S: Schema>: RefsArePartOf<S> {}
