|
@@ -1,10 +1,10 @@
|
|
-//! Schema specification
|
|
|
|
|
|
+//! Schema specification types.
|
|
//!
|
|
//!
|
|
-//! Terminology used:
|
|
|
|
-//! - domain: one side of an association/relation, the "pointed-from" side for one-sided relations
|
|
|
|
-//! - range: one side of an association/relation, the "pointed-to" side for one-sided relations
|
|
|
|
-//! - local: the current side of an association
|
|
|
|
-//! - remote: the opposite side of an association
|
|
|
|
|
|
+//! The following terminology used in some types in this module:
|
|
|
|
+//! - *domain*: one side of an association/relation, the "pointed-from" side for one-sided relations
|
|
|
|
+//! - *range*: one side of an association/relation, the "pointed-to" side for one-sided relations
|
|
|
|
+//! - *local*: the current side of an association
|
|
|
|
+//! - *remote*: the opposite side of an association
|
|
|
|
|
|
use query::Queryable;
|
|
use query::Queryable;
|
|
|
|
|
|
@@ -16,9 +16,12 @@ use crate::{
|
|
};
|
|
};
|
|
use crate::{DBResult, Error};
|
|
use crate::{DBResult, Error};
|
|
|
|
|
|
-use self::datum::{ConcreteDatum, DatumDiscriminatorRef};
|
|
|
|
|
|
+use self::{datum::{ConcreteDatum, DatumDiscriminatorRef}, entity::EntityPartList};
|
|
|
|
|
|
|
|
+/// Types related to datums, or struct fields.
|
|
pub mod datum;
|
|
pub mod datum;
|
|
|
|
+/// Types related to entities, or structs that can be serialized into/deserialized from the
|
|
|
|
+/// database.
|
|
pub mod entity;
|
|
pub mod entity;
|
|
|
|
|
|
mod build;
|
|
mod build;
|
|
@@ -47,14 +50,17 @@ impl<T: Entity> Stored<T> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// Retrieve the entity ID of the stored entity.
|
|
pub fn id(&self) -> T::ID {
|
|
pub fn id(&self) -> T::ID {
|
|
self.id
|
|
self.id
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// Consume the `Stored<>` wrapper and return the wrapped data.
|
|
pub fn wrapped(self) -> T {
|
|
pub fn wrapped(self) -> T {
|
|
self.wrap
|
|
self.wrap
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// Synchronize the wrapped value with the corresponding database row.
|
|
pub fn sync(&mut self) -> DBResult<()> {
|
|
pub fn sync(&mut self) -> DBResult<()> {
|
|
let txn = Transaction::new(&self.db)?;
|
|
let txn = Transaction::new(&self.db)?;
|
|
query::update_entity(&self.db, self)?;
|
|
query::update_entity(&self.db, self)?;
|
|
@@ -112,7 +118,11 @@ impl<T: Entity> PartialEq for Stored<T> {
|
|
/// define the relation. Can be restricted to be injective if this is desired. Doing so will incur
|
|
/// define the relation. Can be restricted to be injective if this is desired. Doing so will incur
|
|
/// a small runtime cost, since an extra index must be maintained.
|
|
/// a small runtime cost, since an extra index must be maintained.
|
|
pub trait Relation: 'static {
|
|
pub trait Relation: 'static {
|
|
|
|
+ /// The domain of the relation, aka the "pointed-from" type. This is interchangeable with
|
|
|
|
+ /// `Range` unless `INJECTIVE` is set to true.
|
|
type Domain: Entity;
|
|
type Domain: Entity;
|
|
|
|
+ /// The range of the relation, aka the "pointed-to" type. This is interchangeable with
|
|
|
|
+ /// `Domain` unless `INJECTIVE` is set to true.
|
|
type Range: Entity;
|
|
type Range: Entity;
|
|
|
|
|
|
/// If true, then each Range-type entity can only be referred to by a single Domain-type
|
|
/// If true, then each Range-type entity can only be referred to by a single Domain-type
|
|
@@ -120,12 +130,17 @@ pub trait Relation: 'static {
|
|
/// invertible for a two-way map.
|
|
/// invertible for a two-way map.
|
|
const INJECTIVE: bool = false;
|
|
const INJECTIVE: bool = false;
|
|
|
|
|
|
|
|
+ /// A unique, human-readable name for the relation, which should be short and not include
|
|
|
|
+ /// spaces.
|
|
const NAME: &'static str;
|
|
const NAME: &'static str;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/// Enumeration used to represent which side of a relation an [`AssocInterface`] trait implementation is representing.
|
|
#[derive(Debug)]
|
|
#[derive(Debug)]
|
|
pub enum LocalSide {
|
|
pub enum LocalSide {
|
|
|
|
+ /// The side of the relation that the [`AssocInterface`] represents is the `Domain` side.
|
|
Domain,
|
|
Domain,
|
|
|
|
+ /// The side of the relation that the [`AssocInterface`] represents is the `Range` side.
|
|
Range,
|
|
Range,
|
|
}
|
|
}
|
|
|
|
|
|
@@ -166,7 +181,10 @@ impl<T: Entity> std::fmt::Debug for AssocMap<T> {
|
|
|
|
|
|
impl<T: Entity> Default for AssocMap<T> {
|
|
impl<T: Entity> Default for AssocMap<T> {
|
|
fn default() -> Self {
|
|
fn default() -> Self {
|
|
- Self::empty()
|
|
|
|
|
|
+ Self {
|
|
|
|
+ data: None,
|
|
|
|
+ _ghost: Default::default(),
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -190,15 +208,6 @@ impl<T: Entity> AssocInterface for AssocMap<T> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-impl<T: Entity> AssocMap<T> {
|
|
|
|
- pub fn empty() -> Self {
|
|
|
|
- Self {
|
|
|
|
- data: None,
|
|
|
|
- _ghost: Default::default(),
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
impl<T: Entity> Datum for AssocMap<T> {
|
|
impl<T: Entity> Datum for AssocMap<T> {
|
|
fn sql_type() -> &'static str {
|
|
fn sql_type() -> &'static str {
|
|
unreachable!()
|
|
unreachable!()
|
|
@@ -265,7 +274,10 @@ impl<R: Relation> Clone for AssocDomain<R> {
|
|
|
|
|
|
impl<R: Relation> Default for AssocDomain<R> {
|
|
impl<R: Relation> Default for AssocDomain<R> {
|
|
fn default() -> Self {
|
|
fn default() -> Self {
|
|
- Self::empty()
|
|
|
|
|
|
+ Self {
|
|
|
|
+ data: None,
|
|
|
|
+ _ghost: Default::default(),
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -278,15 +290,6 @@ impl<R: Relation> std::fmt::Debug for AssocDomain<R> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-impl<R: Relation> AssocDomain<R> {
|
|
|
|
- pub fn empty() -> Self {
|
|
|
|
- Self {
|
|
|
|
- data: None,
|
|
|
|
- _ghost: Default::default(),
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
impl<R: Relation> AssocInterface for AssocDomain<R> {
|
|
impl<R: Relation> AssocInterface for AssocDomain<R> {
|
|
type RemoteEntity = R::Range;
|
|
type RemoteEntity = R::Range;
|
|
const SIDE: LocalSide = LocalSide::Domain;
|
|
const SIDE: LocalSide = LocalSide::Domain;
|
|
@@ -368,7 +371,10 @@ impl<R: Relation> Clone for AssocRange<R> {
|
|
|
|
|
|
impl<R: Relation> Default for AssocRange<R> {
|
|
impl<R: Relation> Default for AssocRange<R> {
|
|
fn default() -> Self {
|
|
fn default() -> Self {
|
|
- Self::empty()
|
|
|
|
|
|
+ Self {
|
|
|
|
+ data: None,
|
|
|
|
+ _ghost: Default::default(),
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -381,15 +387,6 @@ impl<R: Relation> std::fmt::Debug for AssocRange<R> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-impl<R: Relation> AssocRange<R> {
|
|
|
|
- pub fn empty() -> Self {
|
|
|
|
- Self {
|
|
|
|
- data: None,
|
|
|
|
- _ghost: Default::default(),
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
impl<R: Relation> AssocInterface for AssocRange<R> {
|
|
impl<R: Relation> AssocInterface for AssocRange<R> {
|
|
type RemoteEntity = R::Domain;
|
|
type RemoteEntity = R::Domain;
|
|
const SIDE: LocalSide = LocalSide::Range;
|
|
const SIDE: LocalSide = LocalSide::Range;
|
|
@@ -557,6 +554,7 @@ pub struct IDMap<T: Entity> {
|
|
}
|
|
}
|
|
|
|
|
|
impl<T: Entity> IDMap<T> {
|
|
impl<T: Entity> IDMap<T> {
|
|
|
|
+ /// Construct a non-empty instance of an `IDMap`.
|
|
pub fn build(db: Connection) -> Self {
|
|
pub fn build(db: Connection) -> Self {
|
|
Self {
|
|
Self {
|
|
conn: db,
|
|
conn: db,
|
|
@@ -591,36 +589,30 @@ impl<T: Entity> Insertable<T> for IDMap<T> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-pub struct Index<T: Entity, Key: Datum> {
|
|
|
|
|
|
+/// Search index definition.
|
|
|
|
+pub struct Index<T: Entity, Keys: EntityPartList> {
|
|
_conn: Connection,
|
|
_conn: Connection,
|
|
- _ghost: std::marker::PhantomData<(T, Key)>,
|
|
|
|
|
|
+ _ghost: std::marker::PhantomData<(T, Keys)>,
|
|
}
|
|
}
|
|
|
|
|
|
/// Represents a single root-level table or index in a database.
|
|
/// Represents a single root-level table or index in a database.
|
|
pub trait DatabaseItem {
|
|
pub trait DatabaseItem {
|
|
- fn item_key() -> &'static str;
|
|
|
|
- fn dependency_keys() -> &'static [&'static str];
|
|
|
|
-
|
|
|
|
- fn is_index() -> bool {
|
|
|
|
- false
|
|
|
|
- }
|
|
|
|
- fn index_over() -> &'static str {
|
|
|
|
- unreachable!()
|
|
|
|
- }
|
|
|
|
- fn index_columns() -> &'static [&'static str] {
|
|
|
|
- unreachable!()
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ /// Accept an entity visitor for entity discovery.
|
|
fn accept_entity_visitor(visitor: &mut impl EntityVisitor);
|
|
fn accept_entity_visitor(visitor: &mut impl EntityVisitor);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/// Visitor trait for iterating across the types in a [`DatabaseSpec`].
|
|
pub trait DatabaseItemVisitor {
|
|
pub trait DatabaseItemVisitor {
|
|
|
|
+ ///
|
|
fn visit<DI: DatabaseItem>(&mut self)
|
|
fn visit<DI: DatabaseItem>(&mut self)
|
|
where
|
|
where
|
|
Self: Sized;
|
|
Self: Sized;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/// Trait representing a type that can be used as a field in a type implementing [`Database`] via
|
|
|
|
+/// the derivation macro.
|
|
pub trait DatabaseSpec {
|
|
pub trait DatabaseSpec {
|
|
|
|
+ /// Accept an entity visitor.
|
|
fn accept_entity_visitor(visitor: &mut impl EntityVisitor);
|
|
fn accept_entity_visitor(visitor: &mut impl EntityVisitor);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -632,6 +624,9 @@ impl<T: Entity> DatabaseSpec for IDMap<T> {
|
|
|
|
|
|
/// A root structure for the database specification graph.
|
|
/// A root structure for the database specification graph.
|
|
pub trait Database {
|
|
pub trait Database {
|
|
|
|
+ /// Open the SQLite database at the given URI and return it as an instance of a schema.
|
|
|
|
+ ///
|
|
|
|
+ /// This function will attempt to create the database file if it is not present.
|
|
fn open_path<U: AsRef<str>>(uri: U) -> DBResult<Self>
|
|
fn open_path<U: AsRef<str>>(uri: U) -> DBResult<Self>
|
|
where
|
|
where
|
|
Self: Sized,
|
|
Self: Sized,
|
|
@@ -659,6 +654,7 @@ pub trait Database {
|
|
where
|
|
where
|
|
Self: Sized;
|
|
Self: Sized;
|
|
|
|
|
|
|
|
+ /// Accept a visitor for iteration.
|
|
fn accept_item_visitor(visitor: &mut impl DatabaseItemVisitor)
|
|
fn accept_item_visitor(visitor: &mut impl DatabaseItemVisitor)
|
|
where
|
|
where
|
|
Self: Sized;
|
|
Self: Sized;
|