Ergonomics improvements, including a kw
macro. (#537) r=emily
* Add TypedValue::instant(micros). * Add From<f64> for TypedValue. * Add lookup_values_for_attribute to Conn. * Add q_explain to Queryable. * Expose an iterator over FindSpec's columns. * Export edn from mentat crate. Export QueryExecutionResult. * Implement Display for Variable and Element. * Introduce a `kw` macro. This allows you to write: ```rust kw!(:foo/bar) ``` instead of ```rust NamespacedKeyword::new("foo", "bar") ``` … and it's more efficient, too. Add `mentat::open`, eliminate use of `mentat_db` in some places.
This commit is contained in:
parent
3bf7459315
commit
2614f498be
11 changed files with 190 additions and 106 deletions
|
@ -8,15 +8,15 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
extern crate chrono;
|
||||||
extern crate enum_set;
|
extern crate enum_set;
|
||||||
|
extern crate ordered_float;
|
||||||
|
extern crate uuid;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
extern crate ordered_float;
|
|
||||||
|
|
||||||
extern crate chrono;
|
|
||||||
extern crate edn;
|
extern crate edn;
|
||||||
extern crate uuid;
|
|
||||||
|
|
||||||
pub mod values;
|
pub mod values;
|
||||||
|
|
||||||
|
@ -231,6 +231,12 @@ impl TypedValue {
|
||||||
pub fn current_instant() -> TypedValue {
|
pub fn current_instant() -> TypedValue {
|
||||||
Utc::now().into()
|
Utc::now().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct a new `TypedValue::Instant` instance from the provided
|
||||||
|
/// microsecond timestamp.
|
||||||
|
pub fn instant(micros: i64) -> TypedValue {
|
||||||
|
DateTime::<Utc>::from_micros(micros).into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait MicrosecondPrecision {
|
trait MicrosecondPrecision {
|
||||||
|
@ -301,6 +307,12 @@ impl From<i32> for TypedValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<f64> for TypedValue {
|
||||||
|
fn from(value: f64) -> TypedValue {
|
||||||
|
TypedValue::Double(OrderedFloat(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Type safe representation of the possible return values from SQLite's `typeof`
|
/// Type safe representation of the possible return values from SQLite's `typeof`
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
|
||||||
pub enum SQLTypeAffinity {
|
pub enum SQLTypeAffinity {
|
||||||
|
|
|
@ -42,4 +42,10 @@ pub use types::{
|
||||||
Value,
|
Value,
|
||||||
ValueAndSpan,
|
ValueAndSpan,
|
||||||
};
|
};
|
||||||
pub use symbols::{Keyword, NamespacedKeyword, PlainSymbol, NamespacedSymbol};
|
|
||||||
|
pub use symbols::{
|
||||||
|
Keyword,
|
||||||
|
NamespacedKeyword,
|
||||||
|
NamespacedSymbol,
|
||||||
|
PlainSymbol,
|
||||||
|
};
|
||||||
|
|
|
@ -10,6 +10,13 @@
|
||||||
|
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ns_keyword {
|
||||||
|
($ns: expr, $name: expr) => {{
|
||||||
|
$crate::NamespacedKeyword::new($ns, $name)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
/// A simplification of Clojure's Symbol.
|
/// A simplification of Clojure's Symbol.
|
||||||
#[derive(Clone,Debug,Eq,Hash,Ord,PartialOrd,PartialEq)]
|
#[derive(Clone,Debug,Eq,Hash,Ord,PartialOrd,PartialEq)]
|
||||||
pub struct PlainSymbol(pub String);
|
pub struct PlainSymbol(pub String);
|
||||||
|
@ -136,6 +143,8 @@ impl NamespacedKeyword {
|
||||||
/// let keyword = NamespacedKeyword::new("foo", "bar");
|
/// let keyword = NamespacedKeyword::new("foo", "bar");
|
||||||
/// assert_eq!(keyword.to_string(), ":foo/bar");
|
/// assert_eq!(keyword.to_string(), ":foo/bar");
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// See also the `kw!` macro in the main `mentat` crate.
|
||||||
pub fn new<T>(namespace: T, name: T) -> Self where T: Into<String> {
|
pub fn new<T>(namespace: T, name: T) -> Self where T: Into<String> {
|
||||||
let n = name.into();
|
let n = name.into();
|
||||||
let ns = namespace.into();
|
let ns = namespace.into();
|
||||||
|
@ -302,13 +311,6 @@ impl Display for NamespacedKeyword {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! ns_keyword {
|
|
||||||
($ns: expr, $name: expr) => {{
|
|
||||||
$crate::NamespacedKeyword::new($ns, $name)
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ns_keyword_macro() {
|
fn test_ns_keyword_macro() {
|
||||||
assert_eq!(ns_keyword!("test", "name").to_string(),
|
assert_eq!(ns_keyword!("test", "name").to_string(),
|
||||||
|
|
|
@ -127,6 +127,12 @@ impl fmt::Debug for Variable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Variable {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct QueryFunction(pub PlainSymbol);
|
pub struct QueryFunction(pub PlainSymbol);
|
||||||
|
|
||||||
|
@ -443,6 +449,16 @@ pub enum Element {
|
||||||
// Pull(Pull), // TODO
|
// Pull(Pull), // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Element {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
&Element::Variable(ref var) => {
|
||||||
|
write!(f, "{}", var)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Limit {
|
pub enum Limit {
|
||||||
None,
|
None,
|
||||||
|
@ -543,6 +559,16 @@ impl FindSpec {
|
||||||
pub fn requires_distinct(&self) -> bool {
|
pub fn requires_distinct(&self) -> bool {
|
||||||
!self.is_unit_limited()
|
!self.is_unit_limited()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn columns<'s>(&'s self) -> Box<Iterator<Item=&Element> + 's> {
|
||||||
|
use FindSpec::*;
|
||||||
|
match self {
|
||||||
|
&FindScalar(ref e) => Box::new(std::iter::once(e)),
|
||||||
|
&FindColl(ref e) => Box::new(std::iter::once(e)),
|
||||||
|
&FindTuple(ref v) => Box::new(v.iter()),
|
||||||
|
&FindRel(ref v) => Box::new(v.iter()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Datomic accepts variable or placeholder. DataScript accepts recursive bindings. Mentat sticks
|
// Datomic accepts variable or placeholder. DataScript accepts recursive bindings. Mentat sticks
|
||||||
|
|
22
src/conn.rs
22
src/conn.rs
|
@ -105,6 +105,8 @@ pub struct Conn {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Queryable {
|
pub trait Queryable {
|
||||||
|
fn q_explain<T>(&self, query: &str, inputs: T) -> Result<QueryExplanation>
|
||||||
|
where T: Into<Option<QueryInputs>>;
|
||||||
fn q_once<T>(&self, query: &str, inputs: T) -> Result<QueryResults>
|
fn q_once<T>(&self, query: &str, inputs: T) -> Result<QueryResults>
|
||||||
where T: Into<Option<QueryInputs>>;
|
where T: Into<Option<QueryInputs>>;
|
||||||
fn lookup_values_for_attribute<E>(&self, entity: E, attribute: &edn::NamespacedKeyword) -> Result<Vec<TypedValue>>
|
fn lookup_values_for_attribute<E>(&self, entity: E, attribute: &edn::NamespacedKeyword) -> Result<Vec<TypedValue>>
|
||||||
|
@ -135,6 +137,11 @@ impl<'a, 'c> Queryable for InProgressRead<'a, 'c> {
|
||||||
self.0.q_once(query, inputs)
|
self.0.q_once(query, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn q_explain<T>(&self, query: &str, inputs: T) -> Result<QueryExplanation>
|
||||||
|
where T: Into<Option<QueryInputs>> {
|
||||||
|
self.0.q_explain(query, inputs)
|
||||||
|
}
|
||||||
|
|
||||||
fn lookup_values_for_attribute<E>(&self, entity: E, attribute: &edn::NamespacedKeyword) -> Result<Vec<TypedValue>>
|
fn lookup_values_for_attribute<E>(&self, entity: E, attribute: &edn::NamespacedKeyword) -> Result<Vec<TypedValue>>
|
||||||
where E: Into<Entid> {
|
where E: Into<Entid> {
|
||||||
self.0.lookup_values_for_attribute(entity, attribute)
|
self.0.lookup_values_for_attribute(entity, attribute)
|
||||||
|
@ -156,6 +163,14 @@ impl<'a, 'c> Queryable for InProgress<'a, 'c> {
|
||||||
inputs)
|
inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn q_explain<T>(&self, query: &str, inputs: T) -> Result<QueryExplanation>
|
||||||
|
where T: Into<Option<QueryInputs>> {
|
||||||
|
q_explain(&*(self.transaction),
|
||||||
|
&self.schema,
|
||||||
|
query,
|
||||||
|
inputs)
|
||||||
|
}
|
||||||
|
|
||||||
fn lookup_values_for_attribute<E>(&self, entity: E, attribute: &edn::NamespacedKeyword) -> Result<Vec<TypedValue>>
|
fn lookup_values_for_attribute<E>(&self, entity: E, attribute: &edn::NamespacedKeyword) -> Result<Vec<TypedValue>>
|
||||||
where E: Into<Entid> {
|
where E: Into<Entid> {
|
||||||
lookup_values_for_attribute(&*(self.transaction), &self.schema, entity, attribute)
|
lookup_values_for_attribute(&*(self.transaction), &self.schema, entity, attribute)
|
||||||
|
@ -362,6 +377,13 @@ impl Conn {
|
||||||
q_explain(sqlite, &*self.current_schema(), query, inputs)
|
q_explain(sqlite, &*self.current_schema(), query, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lookup_values_for_attribute(&self,
|
||||||
|
sqlite: &rusqlite::Connection,
|
||||||
|
entity: Entid,
|
||||||
|
attribute: &edn::NamespacedKeyword) -> Result<Vec<TypedValue>> {
|
||||||
|
lookup_values_for_attribute(sqlite, &*self.current_schema(), entity, attribute)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn lookup_value_for_attribute(&self,
|
pub fn lookup_value_for_attribute(&self,
|
||||||
sqlite: &rusqlite::Connection,
|
sqlite: &rusqlite::Connection,
|
||||||
entity: Entid,
|
entity: Entid,
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
#![macro_use]
|
||||||
|
|
||||||
// We have a little bit of a dilemma in Mentat.
|
// We have a little bit of a dilemma in Mentat.
|
||||||
// The public data format for transacting is, fundamentally, a big string: EDN.
|
// The public data format for transacting is, fundamentally, a big string: EDN.
|
||||||
// The internal data format for transacting is required to encode the complexities of
|
// The internal data format for transacting is required to encode the complexities of
|
||||||
|
@ -23,7 +25,7 @@
|
||||||
// a: Entid::Ident(NamespacedKeyword::new("test", "a1")),
|
// a: Entid::Ident(NamespacedKeyword::new("test", "a1")),
|
||||||
// v: Value::Text("v1".into()),
|
// v: Value::Text("v1".into()),
|
||||||
// }),
|
// }),
|
||||||
// a: Entid::Ident(NamespacedKeyword::new("test", "a")),
|
// a: Entid::Ident(kw!(:test/a)),
|
||||||
// v: AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("v".into()), Span(44, 47))),
|
// v: AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("v".into()), Span(44, 47))),
|
||||||
// }));
|
// }));
|
||||||
//
|
//
|
||||||
|
@ -322,30 +324,23 @@ impl FromThing<KnownEntid> for TypedValueOr<TempIdHandle> {
|
||||||
mod testing {
|
mod testing {
|
||||||
extern crate mentat_db;
|
extern crate mentat_db;
|
||||||
|
|
||||||
use mentat_core::{
|
|
||||||
Entid,
|
|
||||||
HasSchema,
|
|
||||||
NamespacedKeyword,
|
|
||||||
TypedValue,
|
|
||||||
};
|
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
Error,
|
Error,
|
||||||
|
ErrorKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::ErrorKind::{
|
// For matching inside a test.
|
||||||
DbError,
|
|
||||||
};
|
|
||||||
|
|
||||||
use mentat_db::TxReport;
|
|
||||||
|
|
||||||
use mentat_db::ErrorKind::{
|
use mentat_db::ErrorKind::{
|
||||||
UnrecognizedEntid,
|
UnrecognizedEntid,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ::{
|
use ::{
|
||||||
Conn,
|
Conn,
|
||||||
|
Entid,
|
||||||
|
HasSchema,
|
||||||
Queryable,
|
Queryable,
|
||||||
|
TypedValue,
|
||||||
|
TxReport,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -378,7 +373,7 @@ mod testing {
|
||||||
let mut in_progress = conn.begin_transaction(&mut sqlite).expect("begun successfully");
|
let mut in_progress = conn.begin_transaction(&mut sqlite).expect("begun successfully");
|
||||||
|
|
||||||
// This should fail: unrecognized entid.
|
// This should fail: unrecognized entid.
|
||||||
if let Err(Error(DbError(UnrecognizedEntid(e)), _)) = in_progress.transact_terms(terms, tempids) {
|
if let Err(Error(ErrorKind::DbError(UnrecognizedEntid(e)), _)) = in_progress.transact_terms(terms, tempids) {
|
||||||
assert_eq!(e, 999);
|
assert_eq!(e, 999);
|
||||||
} else {
|
} else {
|
||||||
panic!("Should have rejected the entid.");
|
panic!("Should have rejected the entid.");
|
||||||
|
@ -390,9 +385,9 @@ mod testing {
|
||||||
let mut sqlite = mentat_db::db::new_connection("").unwrap();
|
let mut sqlite = mentat_db::db::new_connection("").unwrap();
|
||||||
let mut conn = Conn::connect(&mut sqlite).unwrap();
|
let mut conn = Conn::connect(&mut sqlite).unwrap();
|
||||||
|
|
||||||
let foo_one = NamespacedKeyword::new("foo", "one");
|
let foo_one = kw!(:foo/one);
|
||||||
let foo_many = NamespacedKeyword::new("foo", "many");
|
let foo_many = kw!(:foo/many);
|
||||||
let foo_ref = NamespacedKeyword::new("foo", "ref");
|
let foo_ref = kw!(:foo/ref);
|
||||||
let report: TxReport;
|
let report: TxReport;
|
||||||
|
|
||||||
// Give ourselves a schema to work with!
|
// Give ourselves a schema to work with!
|
||||||
|
|
72
src/lib.rs
72
src/lib.rs
|
@ -18,7 +18,7 @@ extern crate lazy_static;
|
||||||
|
|
||||||
extern crate rusqlite;
|
extern crate rusqlite;
|
||||||
|
|
||||||
extern crate edn;
|
pub extern crate edn;
|
||||||
extern crate mentat_core;
|
extern crate mentat_core;
|
||||||
extern crate mentat_db;
|
extern crate mentat_db;
|
||||||
extern crate mentat_query;
|
extern crate mentat_query;
|
||||||
|
@ -30,7 +30,44 @@ extern crate mentat_sql;
|
||||||
extern crate mentat_tx;
|
extern crate mentat_tx;
|
||||||
extern crate mentat_tx_parser;
|
extern crate mentat_tx_parser;
|
||||||
|
|
||||||
use rusqlite::Connection;
|
pub use mentat_core::{
|
||||||
|
Attribute,
|
||||||
|
Entid,
|
||||||
|
HasSchema,
|
||||||
|
NamespacedKeyword,
|
||||||
|
TypedValue,
|
||||||
|
Uuid,
|
||||||
|
ValueType,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use mentat_db::{
|
||||||
|
CORE_SCHEMA_VERSION,
|
||||||
|
DB_SCHEMA_CORE,
|
||||||
|
TxReport,
|
||||||
|
new_connection,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Produce the appropriate `NamespacedKeyword` for the provided namespace and name.
|
||||||
|
/// This lives here because we can't re-export macros:
|
||||||
|
/// https://github.com/rust-lang/rust/issues/29638.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! kw {
|
||||||
|
( : $ns:ident / $n:ident ) => {
|
||||||
|
// We don't need to go through `new` -- `ident` is strict enough.
|
||||||
|
$crate::NamespacedKeyword {
|
||||||
|
namespace: stringify!($ns).into(),
|
||||||
|
name: stringify!($n).into(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
( : $ns:ident$(. $nss:ident)+ / $n:ident ) => {
|
||||||
|
// We don't need to go through `new` -- `ident` is strict enough.
|
||||||
|
$crate::NamespacedKeyword {
|
||||||
|
namespace: concat!(stringify!($ns) $(, ".", stringify!($nss))+).into(),
|
||||||
|
name: stringify!($n).into(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod ident;
|
pub mod ident;
|
||||||
|
@ -43,29 +80,17 @@ pub fn get_name() -> String {
|
||||||
return String::from("mentat");
|
return String::from("mentat");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will ultimately not return the sqlite connection directly
|
/// Open a Mentat store at the provided path.
|
||||||
pub fn get_connection() -> Connection {
|
pub fn open(path: &str) -> errors::Result<(rusqlite::Connection, Conn)> {
|
||||||
return Connection::open_in_memory().unwrap();
|
let mut connection = new_connection(path)?;
|
||||||
|
let conn = Conn::connect(&mut connection)?;
|
||||||
|
Ok((connection, conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use mentat_core::{
|
|
||||||
Attribute,
|
|
||||||
Entid,
|
|
||||||
TypedValue,
|
|
||||||
Uuid,
|
|
||||||
ValueType,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use mentat_db::{
|
|
||||||
CORE_SCHEMA_VERSION,
|
|
||||||
DB_SCHEMA_CORE,
|
|
||||||
new_connection,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use query::{
|
pub use query::{
|
||||||
IntoResult,
|
IntoResult,
|
||||||
NamespacedKeyword,
|
|
||||||
PlainSymbol,
|
PlainSymbol,
|
||||||
|
QueryExecutionResult,
|
||||||
QueryExplanation,
|
QueryExplanation,
|
||||||
QueryInputs,
|
QueryInputs,
|
||||||
QueryPlanStep,
|
QueryPlanStep,
|
||||||
|
@ -84,9 +109,16 @@ pub use conn::{
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use edn::symbols::Keyword;
|
use edn::symbols::Keyword;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_import_edn() {
|
fn can_import_edn() {
|
||||||
assert_eq!("foo", Keyword::new("foo").0);
|
assert_eq!("foo", Keyword::new("foo").0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_kw() {
|
||||||
|
assert_eq!(kw!(:foo/bar), NamespacedKeyword::new("foo", "bar"));
|
||||||
|
assert_eq!(kw!(:org.mozilla.foo/bar_baz), NamespacedKeyword::new("org.mozilla.foo", "bar_baz"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,11 @@
|
||||||
//! Typical use is the following:
|
//! Typical use is the following:
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
|
//! #[macro_use(kw)]
|
||||||
//! extern crate mentat;
|
//! extern crate mentat;
|
||||||
//! extern crate mentat_db; // So we can use SQLite connection utilities.
|
|
||||||
//!
|
//!
|
||||||
//! use mentat::{
|
//! use mentat::{
|
||||||
//! Conn,
|
//! Conn,
|
||||||
//! NamespacedKeyword,
|
|
||||||
//! ValueType,
|
//! ValueType,
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
|
@ -41,8 +40,7 @@
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
//! let mut sqlite = mentat_db::db::new_connection("").expect("SQLite connected");
|
//! let (mut sqlite, mut conn) = mentat::open("").expect("connected");
|
||||||
//! let mut conn = Conn::connect(&mut sqlite).expect("connected");
|
|
||||||
//!
|
//!
|
||||||
//! {
|
//! {
|
||||||
//! // Read the list of installed vocabularies.
|
//! // Read the list of installed vocabularies.
|
||||||
|
@ -64,10 +62,10 @@
|
||||||
//!
|
//!
|
||||||
//! // Make sure our vocabulary is installed, and install if necessary.
|
//! // Make sure our vocabulary is installed, and install if necessary.
|
||||||
//! in_progress.ensure_vocabulary(&Definition {
|
//! in_progress.ensure_vocabulary(&Definition {
|
||||||
//! name: NamespacedKeyword::new("example", "links"),
|
//! name: kw!(:example/links),
|
||||||
//! version: 1,
|
//! version: 1,
|
||||||
//! attributes: vec![
|
//! attributes: vec![
|
||||||
//! (NamespacedKeyword::new("link", "title"),
|
//! (kw!(:link/title),
|
||||||
//! vocabulary::AttributeBuilder::default()
|
//! vocabulary::AttributeBuilder::default()
|
||||||
//! .value_type(ValueType::String)
|
//! .value_type(ValueType::String)
|
||||||
//! .multival(false)
|
//! .multival(false)
|
||||||
|
@ -85,22 +83,19 @@
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use mentat_core::{
|
|
||||||
Attribute,
|
|
||||||
Entid,
|
|
||||||
HasSchema,
|
|
||||||
KnownEntid,
|
|
||||||
NamespacedKeyword,
|
|
||||||
TypedValue,
|
|
||||||
ValueType,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use mentat_core::attribute;
|
pub use mentat_core::attribute;
|
||||||
use mentat_core::attribute::Unique;
|
use mentat_core::attribute::Unique;
|
||||||
|
use mentat_core::KnownEntid;
|
||||||
|
|
||||||
use ::{
|
use ::{
|
||||||
CORE_SCHEMA_VERSION,
|
CORE_SCHEMA_VERSION,
|
||||||
|
Attribute,
|
||||||
|
Entid,
|
||||||
|
HasSchema,
|
||||||
IntoResult,
|
IntoResult,
|
||||||
|
NamespacedKeyword,
|
||||||
|
TypedValue,
|
||||||
|
ValueType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ::conn::{
|
use ::conn::{
|
||||||
|
@ -170,25 +165,25 @@ impl Vocabularies {
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref DB_SCHEMA_CORE: NamespacedKeyword = {
|
static ref DB_SCHEMA_CORE: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("db.schema", "core")
|
kw!(:db.schema/core)
|
||||||
};
|
};
|
||||||
static ref DB_SCHEMA_ATTRIBUTE: NamespacedKeyword = {
|
static ref DB_SCHEMA_ATTRIBUTE: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("db.schema", "attribute")
|
kw!(:db.schema/attribute)
|
||||||
};
|
};
|
||||||
static ref DB_SCHEMA_VERSION: NamespacedKeyword = {
|
static ref DB_SCHEMA_VERSION: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("db.schema", "version")
|
kw!(:db.schema/version)
|
||||||
};
|
};
|
||||||
static ref DB_IDENT: NamespacedKeyword = {
|
static ref DB_IDENT: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("db", "ident")
|
kw!(:db/ident)
|
||||||
};
|
};
|
||||||
static ref DB_UNIQUE: NamespacedKeyword = {
|
static ref DB_UNIQUE: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("db", "unique")
|
kw!(:db/unique)
|
||||||
};
|
};
|
||||||
static ref DB_UNIQUE_VALUE: NamespacedKeyword = {
|
static ref DB_UNIQUE_VALUE: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("db.unique", "value")
|
kw!(:db.unique/value)
|
||||||
};
|
};
|
||||||
static ref DB_UNIQUE_IDENTITY: NamespacedKeyword = {
|
static ref DB_UNIQUE_IDENTITY: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("db.unique", "identity")
|
kw!(:db.unique/identity)
|
||||||
};
|
};
|
||||||
static ref DB_IS_COMPONENT: NamespacedKeyword = {
|
static ref DB_IS_COMPONENT: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("db", "isComponent")
|
NamespacedKeyword::new("db", "isComponent")
|
||||||
|
@ -197,19 +192,19 @@ lazy_static! {
|
||||||
NamespacedKeyword::new("db", "valueType")
|
NamespacedKeyword::new("db", "valueType")
|
||||||
};
|
};
|
||||||
static ref DB_INDEX: NamespacedKeyword = {
|
static ref DB_INDEX: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("db", "index")
|
kw!(:db/index)
|
||||||
};
|
};
|
||||||
static ref DB_FULLTEXT: NamespacedKeyword = {
|
static ref DB_FULLTEXT: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("db", "fulltext")
|
kw!(:db/fulltext)
|
||||||
};
|
};
|
||||||
static ref DB_CARDINALITY: NamespacedKeyword = {
|
static ref DB_CARDINALITY: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("db", "cardinality")
|
kw!(:db/cardinality)
|
||||||
};
|
};
|
||||||
static ref DB_CARDINALITY_ONE: NamespacedKeyword = {
|
static ref DB_CARDINALITY_ONE: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("db.cardinality", "one")
|
kw!(:db.cardinality/one)
|
||||||
};
|
};
|
||||||
static ref DB_CARDINALITY_MANY: NamespacedKeyword = {
|
static ref DB_CARDINALITY_MANY: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("db.cardinality", "many")
|
kw!(:db.cardinality/many)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Not yet supported.
|
// Not yet supported.
|
||||||
|
@ -574,32 +569,24 @@ impl<T> HasVocabularies for T where T: HasSchema + Queryable {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ::{
|
|
||||||
NamespacedKeyword,
|
|
||||||
Conn,
|
|
||||||
new_connection,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::HasVocabularies;
|
use super::HasVocabularies;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_vocabularies() {
|
fn test_read_vocabularies() {
|
||||||
let mut sqlite = new_connection("").expect("could open conn");
|
let (mut sqlite, mut conn) = ::open("").expect("opened");
|
||||||
let mut conn = Conn::connect(&mut sqlite).expect("could open store");
|
|
||||||
let vocabularies = conn.begin_read(&mut sqlite).expect("in progress")
|
let vocabularies = conn.begin_read(&mut sqlite).expect("in progress")
|
||||||
.read_vocabularies().expect("OK");
|
.read_vocabularies().expect("OK");
|
||||||
assert_eq!(vocabularies.len(), 1);
|
assert_eq!(vocabularies.len(), 1);
|
||||||
let core = vocabularies.get(&NamespacedKeyword::new("db.schema", "core")).expect("exists");
|
let core = vocabularies.get(&kw!(:db.schema/core)).expect("exists");
|
||||||
assert_eq!(core.version, 1);
|
assert_eq!(core.version, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_core_schema() {
|
fn test_core_schema() {
|
||||||
let mut c = new_connection("").expect("could open conn");
|
let (mut sqlite, mut conn) = ::open("").expect("opened");
|
||||||
let mut conn = Conn::connect(&mut c).expect("could open store");
|
let in_progress = conn.begin_transaction(&mut sqlite).expect("in progress");
|
||||||
let in_progress = conn.begin_transaction(&mut c).expect("in progress");
|
|
||||||
let vocab = in_progress.read_vocabularies().expect("vocabulary");
|
let vocab = in_progress.read_vocabularies().expect("vocabulary");
|
||||||
assert_eq!(1, vocab.len());
|
assert_eq!(1, vocab.len());
|
||||||
assert_eq!(1, vocab.get(&NamespacedKeyword::new("db.schema", "core")).expect("core vocab").version);
|
assert_eq!(1, vocab.get(&kw!(:db.schema/core)).expect("core vocab").version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ fn can_import_sqlite() {
|
||||||
data: Option<Vec<u8>>,
|
data: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
let conn = mentat::get_connection();
|
let conn = mentat::new_connection("").expect("SQLite connected");
|
||||||
|
|
||||||
conn.execute("CREATE TABLE person (
|
conn.execute("CREATE TABLE person (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
extern crate mentat;
|
extern crate mentat;
|
||||||
extern crate mentat_core;
|
extern crate mentat_core;
|
||||||
extern crate mentat_db;
|
extern crate mentat_db;
|
||||||
|
@ -482,9 +483,9 @@ fn test_lookup() {
|
||||||
]"#).unwrap().tempids;
|
]"#).unwrap().tempids;
|
||||||
|
|
||||||
let entid = ids.get("b").unwrap();
|
let entid = ids.get("b").unwrap();
|
||||||
let foo_date = NamespacedKeyword::new("foo", "date");
|
let foo_date = kw!(:foo/date);
|
||||||
let foo_many = NamespacedKeyword::new("foo", "many");
|
let foo_many = kw!(:foo/many);
|
||||||
let db_ident = NamespacedKeyword::new("db", "ident");
|
let db_ident = kw!(:db/ident);
|
||||||
let expected = TypedValue::Instant(DateTime::<Utc>::from_str("2016-01-01T11:00:00.000Z").unwrap());
|
let expected = TypedValue::Instant(DateTime::<Utc>::from_str("2016-01-01T11:00:00.000Z").unwrap());
|
||||||
|
|
||||||
// Fetch a value.
|
// Fetch a value.
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
extern crate mentat;
|
extern crate mentat;
|
||||||
extern crate mentat_core;
|
extern crate mentat_core;
|
||||||
extern crate mentat_db;
|
extern crate mentat_db;
|
||||||
|
@ -47,16 +48,16 @@ use mentat::errors::{
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref FOO_NAME: NamespacedKeyword = {
|
static ref FOO_NAME: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("foo", "name")
|
kw!(:foo/name)
|
||||||
};
|
};
|
||||||
|
|
||||||
static ref FOO_MOMENT: NamespacedKeyword = {
|
static ref FOO_MOMENT: NamespacedKeyword = {
|
||||||
NamespacedKeyword::new("foo", "moment")
|
kw!(:foo/moment)
|
||||||
};
|
};
|
||||||
|
|
||||||
static ref FOO_VOCAB: vocabulary::Definition = {
|
static ref FOO_VOCAB: vocabulary::Definition = {
|
||||||
vocabulary::Definition {
|
vocabulary::Definition {
|
||||||
name: NamespacedKeyword::new("org.mozilla", "foo"),
|
name: kw!(:org.mozilla/foo),
|
||||||
version: 1,
|
version: 1,
|
||||||
attributes: vec![
|
attributes: vec![
|
||||||
(FOO_NAME.clone(),
|
(FOO_NAME.clone(),
|
||||||
|
@ -129,24 +130,24 @@ fn test_add_vocab() {
|
||||||
.fulltext(true)
|
.fulltext(true)
|
||||||
.build();
|
.build();
|
||||||
let bar_only = vec![
|
let bar_only = vec![
|
||||||
(NamespacedKeyword::new("foo", "bar"), bar.clone()),
|
(kw!(:foo/bar), bar.clone()),
|
||||||
];
|
];
|
||||||
let baz_only = vec![
|
let baz_only = vec![
|
||||||
(NamespacedKeyword::new("foo", "baz"), baz.clone()),
|
(kw!(:foo/baz), baz.clone()),
|
||||||
];
|
];
|
||||||
let bar_and_baz = vec![
|
let bar_and_baz = vec![
|
||||||
(NamespacedKeyword::new("foo", "bar"), bar.clone()),
|
(kw!(:foo/bar), bar.clone()),
|
||||||
(NamespacedKeyword::new("foo", "baz"), baz.clone()),
|
(kw!(:foo/baz), baz.clone()),
|
||||||
];
|
];
|
||||||
|
|
||||||
let foo_v1_a = vocabulary::Definition {
|
let foo_v1_a = vocabulary::Definition {
|
||||||
name: NamespacedKeyword::new("org.mozilla", "foo"),
|
name: kw!(:org.mozilla/foo),
|
||||||
version: 1,
|
version: 1,
|
||||||
attributes: bar_only.clone(),
|
attributes: bar_only.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let foo_v1_b = vocabulary::Definition {
|
let foo_v1_b = vocabulary::Definition {
|
||||||
name: NamespacedKeyword::new("org.mozilla", "foo"),
|
name: kw!(:org.mozilla/foo),
|
||||||
version: 1,
|
version: 1,
|
||||||
attributes: bar_and_baz.clone(),
|
attributes: bar_and_baz.clone(),
|
||||||
};
|
};
|
||||||
|
@ -244,11 +245,11 @@ fn test_add_vocab() {
|
||||||
.multival(true)
|
.multival(true)
|
||||||
.build();
|
.build();
|
||||||
let bar_and_malformed_baz = vec![
|
let bar_and_malformed_baz = vec![
|
||||||
(NamespacedKeyword::new("foo", "bar"), bar),
|
(kw!(:foo/bar), bar),
|
||||||
(NamespacedKeyword::new("foo", "baz"), malformed_baz.clone()),
|
(kw!(:foo/baz), malformed_baz.clone()),
|
||||||
];
|
];
|
||||||
let foo_v1_malformed = vocabulary::Definition {
|
let foo_v1_malformed = vocabulary::Definition {
|
||||||
name: NamespacedKeyword::new("org.mozilla", "foo"),
|
name: kw!(:org.mozilla/foo),
|
||||||
version: 1,
|
version: 1,
|
||||||
attributes: bar_and_malformed_baz.clone(),
|
attributes: bar_and_malformed_baz.clone(),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue