#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
use core::fmt;
use thiserror::Error;
#[cfg(feature = "rusqlite")]
use rusqlite::types::{
FromSql,
FromSqlResult,
ToSql,
ToSqlOutput,
ValueRef
};
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};
#[cfg(test)]
mod tests;
pub trait Keyed {
type KeyType;
fn key(&self) -> Result<&Key<Self::KeyType>>;
}
pub trait Label {
type LabelType;
fn label(&self) -> Result<&Self::LabelType>;
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, PartialEq, Eq, Hash, Default, Debug)]
pub struct Tag {
pub key: String,
pub label: String,
}
pub trait Tagged {
fn tag(&self) -> Result<Tag>;
fn has_tag(&self) -> bool;
}
impl<K, T, L> Tagged for T
where
T: Keyed<KeyType = K> + Label<LabelType = L>,
K: fmt::Display,
L: fmt::Display,
{
fn tag(&self) -> Result<Tag> {
Ok(
Tag {
key: self.key()?.to_string(),
label: self.label()?.to_string(),
}
)
}
fn has_tag(&self) -> bool {
self.key().map(|v| v.is_some()).unwrap_or(false)
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
#[derive(Clone, PartialEq, Eq, Hash, Default, Debug)]
pub struct Key<K>(pub Option<K>);
impl<K> Key<K> {
pub fn new(value: K) -> Self {
Self(Some(value))
}
pub fn into_entity<T>(self) -> Entity<K, T> {
Entity::Key(self)
}
pub fn to_entity<T>(&self) -> Entity<K, T> where K: Clone {
Entity::Key(self.clone())
}
}
impl<K> core::ops::Deref for Key<K> {
type Target = Option<K>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<K> core::ops::DerefMut for Key<K> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<K: fmt::Display> fmt::Display for Key<K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
Some(value) => write!(f, "{value}"),
None => write!(f, "None"),
}
}
}
impl<K> From<Option<K>> for Key<K> {
fn from(value: Option<K>) -> Self {
Self(value)
}
}
impl<K: Clone> From<&Option<K>> for Key<K> {
fn from(value: &Option<K>) -> Self {
Self(value.as_ref().cloned())
}
}
#[cfg(feature = "rusqlite")]
impl<K: FromSql> FromSql for Key<K> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
match value {
ValueRef::Null => Ok(Key(None)),
_ => FromSql::column_result(value).map(|v| Key(Some(v))),
}
}
}
#[cfg(feature = "rusqlite")]
impl<K: ToSql> ToSql for Key<K> {
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
self.0.to_sql()
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub enum Entity<K, T> {
Key(Key<K>),
Data(Box<T>),
#[default]
None,
}
impl<K, T> Keyed for Entity<K, T>
where
T: Keyed<KeyType = K>,
{
type KeyType = K;
fn key(&self) -> Result<&Key<Self::KeyType>> {
match self {
Entity::Key(key) => Ok(key),
Entity::Data(data) => data.key(),
Entity::None => Err(Error::EntityEmpty),
}
}
}
impl<K, T> Entity<K, T> {
pub fn data(&self) -> Result<&T> {
match self {
Entity::Data(data) => Ok(data),
Entity::Key(_) => Err(Error::EntityNotFetched),
Entity::None => Err(Error::EntityEmpty),
}
}
pub fn data_mut(&mut self) -> Result<&mut T> {
match self {
Entity::Data(ref mut data) => Ok(data),
Entity::Key(_) => Err(Error::EntityNotFetched),
Entity::None => Err(Error::EntityEmpty),
}
}
pub fn is_key(&self) -> bool {
matches!(self, Self::Key(..))
}
pub fn is_data(&self) -> bool {
matches!(self, Self::Data(..))
}
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
}
impl<K, T> From<T> for Entity<K, T> {
fn from(entity: T) -> Self {
Self::Data(Box::new(entity))
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub enum EntityLabel<K, T, L> {
KeyLabel(Key<K>, L),
Data(Box<T>),
#[default]
None,
}
impl<K, T, L> Keyed for EntityLabel<K, T, L>
where
T: Keyed<KeyType = K>,
{
type KeyType = K;
fn key(&self) -> Result<&Key<Self::KeyType>> {
match self {
EntityLabel::KeyLabel(key, _) => Ok(key),
EntityLabel::Data(data) => data.key(),
EntityLabel::None => Err(Error::EntityLabelEmpty),
}
}
}
impl<K, T, L> Label for EntityLabel<K, T, L>
where
T: Label<LabelType = L>,
{
type LabelType = L;
fn label(&self) -> Result<&Self::LabelType> {
match self {
EntityLabel::KeyLabel(_, label) => Ok(label),
EntityLabel::Data(data) => data.label(),
EntityLabel::None => Err(Error::EntityLabelEmpty),
}
}
}
impl<K, T, L> EntityLabel<K, T, L> {
pub fn data(&self) -> Result<&T> {
match self {
EntityLabel::Data(data) => Ok(data),
EntityLabel::KeyLabel(..) => Err(Error::EntityLabelNotFetched),
EntityLabel::None => Err(Error::EntityLabelEmpty),
}
}
pub fn data_mut(&mut self) -> Result<&mut T> {
match self {
EntityLabel::Data(ref mut data) => Ok(data),
EntityLabel::KeyLabel(..) => Err(Error::EntityLabelNotFetched),
EntityLabel::None => Err(Error::EntityLabelEmpty),
}
}
pub fn is_keylabel(&self) -> bool {
matches!(self, Self::KeyLabel(..))
}
pub fn is_data(&self) -> bool {
matches!(self, Self::Data(..))
}
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
}
impl<K, T, L> From<T> for EntityLabel<K, T, L> {
fn from(entity: T) -> Self {
Self::Data(Box::new(entity))
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub enum Many<T> {
Data(Vec<T>),
NotFetched,
#[default]
None,
}
impl<T> Many<T> {
pub fn data(&self) -> Result<&Vec<T>> {
match self {
Many::Data(data) => Ok(data),
Many::NotFetched => Err(Error::ManyNotFetched),
Many::None => Err(Error::ManyEmpty),
}
}
pub fn data_mut(&mut self) -> Result<&mut Vec<T>> {
match self {
Many::Data(ref mut data) => Ok(data),
Many::NotFetched => Err(Error::ManyNotFetched),
Many::None => Err(Error::ManyEmpty),
}
}
pub fn is_data(&self) -> bool {
matches!(self, Self::Data(..))
}
pub fn is_not_fetched(&self) -> bool {
matches!(self, Self::NotFetched)
}
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
}
impl<T> From<Vec<T>> for Many<T> {
fn from(entities: Vec<T>) -> Self {
Self::Data(entities)
}
}
#[derive(Error, Debug)]
pub enum Error {
#[error("nothing set for this Entity")]
EntityEmpty,
#[error("nothing set for this EntityLabel")]
EntityLabelEmpty,
#[error("data was not fetched from the database for this Entity")]
EntityNotFetched,
#[error("data was not fetched from the database for this EntityLabel")]
EntityLabelNotFetched,
#[error("no data set for this Many")]
ManyEmpty,
#[error("data were not fetched from the database for this Many")]
ManyNotFetched,
}
pub type Result<T> = core::result::Result<T, Error>;
pub type Int = usize;
pub type EntityInt<T> = Entity<Int, T>;
pub type EntityString<T> = Entity<String, T>;
pub type EntityLabelInt<T> = EntityLabel<Int, T, String>;
pub type EntityLabelString<T> = EntityLabel<String, T, String>;
pub mod prelude {
#[cfg(feature = "derive")]
pub use dbent_derive::{
Entity,
Label,
};
pub use crate::{
Key,
Keyed,
Label,
Tagged,
Tag,
Entity,
EntityLabel,
Many,
Int,
EntityInt,
EntityString,
EntityLabelInt,
EntityLabelString,
};
}