Implement a rudimentary Keyword struct and the beginnings of ident/entid.

This commit is contained in:
Richard Newman 2016-12-21 08:55:00 -08:00
parent 22ebcd65f3
commit 476f04e27b
7 changed files with 144 additions and 0 deletions

View file

@ -6,6 +6,9 @@ authors = ["Richard Newman <rnewman@twinql.com>", "Nicholas Alexander <nalexande
[dependencies]
rusqlite = "0.8.0"
[dependencies.edn]
path = "edn"
[dependencies.mentat_query_parser]
path = "query-parser"

3
edn/Cargo.toml Normal file
View file

@ -0,0 +1,3 @@
[package]
name = "edn"
version = "0.0.1"

86
edn/src/keyword.rs Normal file
View file

@ -0,0 +1,86 @@
// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
/// Just like Clojure's Keyword. We'll need this as part of EDN parsing,
/// but it's also used for identification within Mentat, so we'll define
/// it here first.
/// Callers are expected to follow these rules:
/// http://www.clojure.org/reference/reader#_symbols
#[derive(Clone,Debug,Eq,PartialEq)]
pub struct Keyword {
pub name: String,
pub namespace: Option<String>,
}
/// A symbol, optionally with a namespace, that prints with a leading colon.
/// This concept is imported from Clojure, as it features in EDN and the query
/// syntax that we use.
///
/// Clojure's constraints are looser than ours, allowing empty namespaces or
/// names:
///
/// ```clojure
/// user=> (keyword "" "")
/// :/
/// user=> (keyword "foo" "")
/// :foo/
/// user=> (keyword "" "bar")
/// :/bar
/// ```
///
/// We think that's nonsense, so we only allow keywords like `:bar` and `:foo/bar`,
/// with both namespace and main parts containing no whitespace and no colon or slash:
///
/// ```rust
/// # use edn::keyword::Keyword;
/// let bar = Keyword::new("bar"); // :bar
/// let foo_bar = Keyword::namespaced("foo", "bar"); // :foo/bar
/// assert_eq!("bar", bar.name);
/// assert_eq!(false, bar.namespace.is_some());
/// assert_eq!("bar", foo_bar.name);
/// assert_eq!(true, foo_bar.namespace.is_some());
/// assert_eq!("foo", foo_bar.namespace.expect("Should have a namespace"));
/// ```
///
/// If you're not sure whether your input is well-formed, you should use a
/// reader function first to validate. TODO: implement `read`.
///
impl Keyword {
pub fn new(name: &str) -> Self {
return Keyword { name: name.to_string(), namespace: None };
}
pub fn namespaced(namespace: &str, name: &str) -> Self {
assert!(!name.is_empty(), "Keywords cannot be unnamed.");
assert!(!namespace.is_empty(), "Keywords cannot have an empty non-null namespace.");
// TODO: debug asserts to ensure that neither field matches [ :/].
return Keyword { name: name.to_string(), namespace: Some(namespace.to_string()) };
}
}
impl ToString for Keyword {
/// Print the keyword in EDN format.
///
/// # Examples
///
/// ```rust
/// # use edn::keyword::Keyword;
/// assert_eq!(":baz", Keyword::new("baz").to_string());
/// assert_eq!(":bar/baz", Keyword::namespaced("bar", "baz").to_string());
/// ```
fn to_string(&self) -> String {
// Note that we don't currently do any escaping.
if let Some(ref ns) = self.namespace {
return format!(":{}/{}", ns, self.name);
}
return format!(":{}", self.name);
}
}

11
edn/src/lib.rs Normal file
View file

@ -0,0 +1,11 @@
// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
pub mod keyword;

View file

@ -1,3 +1,10 @@
[package]
name = "mentat_query_parser"
version = "0.0.1"
[dependencies]
[dependencies.edn]
path = "../edn"
[dependencies.mentat_query]
path = "../query"

25
src/ident.rs Normal file
View file

@ -0,0 +1,25 @@
// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
extern crate edn;
use edn::keyword::Keyword;
pub type EntId = u32; // TODO: u64? Not all DB values will be representable in a u32.
/// The ability to transform entity identifiers (entids) into keyword names (idents).
pub trait ToIdent {
fn ident(&self, entid: EntId) -> Option<Keyword>;
}
/// The ability to transform idents into the corresponding entid.
pub trait ToEntId {
fn entid(&self, ident: &Keyword) -> Option<EntId>;
}

View file

@ -8,11 +8,14 @@
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
extern crate edn;
extern crate mentat_query_parser;
extern crate rusqlite;
use rusqlite::Connection;
pub mod ident;
pub fn get_name() -> String {
return String::from("mentat");
}
@ -30,6 +33,12 @@ pub fn get_connection() -> Connection {
#[cfg(test)]
mod tests {
use super::*;
use edn::keyword::Keyword;
#[test]
fn can_import_edn() {
assert_eq!("foo", Keyword::new("foo").name);
}
#[test]
fn can_import_parser() {