mod primitive;
pub use primitive::{FieldPrimitive, SerializeFormat};
use super::{
AutoStrategy, BelongsTo, Constraint, Embedded, HasMany, HasOne, Model, ModelId, Schema,
VariantId,
};
use crate::{Result, driver, stmt};
use std::fmt;
#[derive(Debug, Clone)]
pub struct Field {
pub id: FieldId,
pub name: FieldName,
pub ty: FieldTy,
pub nullable: bool,
pub primary_key: bool,
pub auto: Option<AutoStrategy>,
pub versionable: bool,
pub constraints: Vec<Constraint>,
pub variant: Option<VariantId>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FieldId {
pub model: ModelId,
pub index: usize,
}
#[derive(Debug, Clone)]
pub struct FieldName {
pub app: Option<String>,
pub storage: Option<String>,
}
impl FieldName {
#[track_caller]
pub fn app_unwrap(&self) -> &str {
self.app.as_deref().unwrap()
}
pub fn storage_name(&self) -> Option<&str> {
self.storage.as_deref().or(self.app.as_deref())
}
pub fn storage_name_unwrap(&self) -> &str {
self.storage_name()
.expect("must specify app name or storage name")
}
}
impl fmt::Display for FieldName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.app.as_deref().unwrap_or("<unnamed>"))
}
}
#[derive(Clone)]
pub enum FieldTy {
Primitive(FieldPrimitive),
Embedded(Embedded),
BelongsTo(BelongsTo),
HasMany(HasMany),
HasOne(HasOne),
}
impl Field {
pub fn id(&self) -> FieldId {
self.id
}
pub fn name(&self) -> &FieldName {
&self.name
}
pub fn ty(&self) -> &FieldTy {
&self.ty
}
pub fn nullable(&self) -> bool {
self.nullable
}
pub fn primary_key(&self) -> bool {
self.primary_key
}
pub fn auto(&self) -> Option<&AutoStrategy> {
self.auto.as_ref()
}
pub fn is_auto_increment(&self) -> bool {
self.auto().map(|auto| auto.is_increment()).unwrap_or(false)
}
pub fn is_versionable(&self) -> bool {
self.versionable
}
pub fn is_relation(&self) -> bool {
self.ty.is_relation()
}
pub fn full_name(&self, schema: &Schema) -> Option<String> {
self.name.app.as_ref().map(|app_name| {
let model = schema.model(self.id.model);
format!("{}::{}", model.name().upper_camel_case(), app_name)
})
}
pub fn relation_target_id(&self) -> Option<ModelId> {
match &self.ty {
FieldTy::BelongsTo(belongs_to) => Some(belongs_to.target),
FieldTy::HasMany(has_many) => Some(has_many.target),
_ => None,
}
}
pub fn relation_target<'a>(&self, schema: &'a Schema) -> Option<&'a Model> {
self.relation_target_id().map(|id| schema.model(id))
}
pub fn expr_ty(&self) -> &stmt::Type {
match &self.ty {
FieldTy::Primitive(primitive) => &primitive.ty,
FieldTy::Embedded(embedded) => &embedded.expr_ty,
FieldTy::BelongsTo(belongs_to) => &belongs_to.expr_ty,
FieldTy::HasMany(has_many) => &has_many.expr_ty,
FieldTy::HasOne(has_one) => &has_one.expr_ty,
}
}
pub fn pair(&self) -> Option<FieldId> {
match &self.ty {
FieldTy::Primitive(_) => None,
FieldTy::Embedded(_) => None,
FieldTy::BelongsTo(belongs_to) => belongs_to.pair,
FieldTy::HasMany(has_many) => Some(has_many.pair),
FieldTy::HasOne(has_one) => Some(has_one.pair),
}
}
pub(crate) fn verify(&self, db: &driver::Capability) -> Result<()> {
if let FieldTy::Primitive(primitive) = &self.ty
&& let Some(storage_ty) = &primitive.storage_ty
{
storage_ty.verify(db)?;
}
Ok(())
}
}
impl FieldTy {
pub fn is_primitive(&self) -> bool {
matches!(self, Self::Primitive(..))
}
pub fn as_primitive(&self) -> Option<&FieldPrimitive> {
match self {
Self::Primitive(primitive) => Some(primitive),
_ => None,
}
}
#[track_caller]
pub fn as_primitive_unwrap(&self) -> &FieldPrimitive {
match self {
Self::Primitive(simple) => simple,
_ => panic!("expected simple field, but was {self:?}"),
}
}
#[track_caller]
pub fn as_primitive_mut_unwrap(&mut self) -> &mut FieldPrimitive {
match self {
Self::Primitive(simple) => simple,
_ => panic!("expected simple field, but was {self:?}"),
}
}
pub fn is_embedded(&self) -> bool {
matches!(self, Self::Embedded(..))
}
pub fn as_embedded(&self) -> Option<&Embedded> {
match self {
Self::Embedded(embedded) => Some(embedded),
_ => None,
}
}
#[track_caller]
pub fn as_embedded_unwrap(&self) -> &Embedded {
match self {
Self::Embedded(embedded) => embedded,
_ => panic!("expected embedded field, but was {self:?}"),
}
}
#[track_caller]
pub fn as_embedded_mut_unwrap(&mut self) -> &mut Embedded {
match self {
Self::Embedded(embedded) => embedded,
_ => panic!("expected embedded field, but was {self:?}"),
}
}
pub fn is_relation(&self) -> bool {
matches!(
self,
Self::BelongsTo(..) | Self::HasMany(..) | Self::HasOne(..)
)
}
pub fn is_has_n(&self) -> bool {
matches!(self, Self::HasMany(..) | Self::HasOne(..))
}
pub fn is_has_many(&self) -> bool {
matches!(self, Self::HasMany(..))
}
pub fn as_has_many(&self) -> Option<&HasMany> {
match self {
Self::HasMany(has_many) => Some(has_many),
_ => None,
}
}
#[track_caller]
pub fn as_has_many_unwrap(&self) -> &HasMany {
match self {
Self::HasMany(has_many) => has_many,
_ => panic!("expected field to be `HasMany`, but was {self:?}"),
}
}
#[track_caller]
pub fn as_has_many_mut_unwrap(&mut self) -> &mut HasMany {
match self {
Self::HasMany(has_many) => has_many,
_ => panic!("expected field to be `HasMany`, but was {self:?}"),
}
}
pub fn as_has_one(&self) -> Option<&HasOne> {
match self {
Self::HasOne(has_one) => Some(has_one),
_ => None,
}
}
pub fn is_has_one(&self) -> bool {
matches!(self, Self::HasOne(..))
}
#[track_caller]
pub fn as_has_one_unwrap(&self) -> &HasOne {
match self {
Self::HasOne(has_one) => has_one,
_ => panic!("expected field to be `HasOne`, but it was {self:?}"),
}
}
#[track_caller]
pub fn as_has_one_mut_unwrap(&mut self) -> &mut HasOne {
match self {
Self::HasOne(has_one) => has_one,
_ => panic!("expected field to be `HasOne`, but it was {self:?}"),
}
}
pub fn is_belongs_to(&self) -> bool {
matches!(self, Self::BelongsTo(..))
}
pub fn as_belongs_to(&self) -> Option<&BelongsTo> {
match self {
Self::BelongsTo(belongs_to) => Some(belongs_to),
_ => None,
}
}
#[track_caller]
pub fn as_belongs_to_unwrap(&self) -> &BelongsTo {
match self {
Self::BelongsTo(belongs_to) => belongs_to,
_ => panic!("expected field to be `BelongsTo`, but was {self:?}"),
}
}
#[track_caller]
pub fn as_belongs_to_mut_unwrap(&mut self) -> &mut BelongsTo {
match self {
Self::BelongsTo(belongs_to) => belongs_to,
_ => panic!("expected field to be `BelongsTo`, but was {self:?}"),
}
}
}
impl fmt::Debug for FieldTy {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Primitive(ty) => ty.fmt(fmt),
Self::Embedded(ty) => ty.fmt(fmt),
Self::BelongsTo(ty) => ty.fmt(fmt),
Self::HasMany(ty) => ty.fmt(fmt),
Self::HasOne(ty) => ty.fmt(fmt),
}
}
}
impl FieldId {
pub(crate) fn placeholder() -> Self {
Self {
model: ModelId::placeholder(),
index: usize::MAX,
}
}
pub(crate) fn is_placeholder(&self) -> bool {
self.index == usize::MAX && self.model == ModelId::placeholder()
}
}
impl From<&Self> for FieldId {
fn from(val: &Self) -> Self {
*val
}
}
impl From<&Field> for FieldId {
fn from(val: &Field) -> Self {
val.id
}
}
impl From<FieldId> for usize {
fn from(val: FieldId) -> Self {
val.index
}
}
impl fmt::Debug for FieldId {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "FieldId({}/{})", self.model.0, self.index)
}
}