corium_forms/
schemaform.rs1use corium_core::{Attribute, Cardinality, EntityId, Partition, Schema, Unique, ValueType};
5use corium_db::{Idents, bootstrap};
6use corium_query::edn::Edn;
7use thiserror::Error;
8
9pub const FIRST_ATTR_ID: u64 = 100;
13
14#[derive(Debug, Error, Eq, PartialEq)]
16pub enum SchemaFormError {
17 #[error("attribute definition must be a map: {0}")]
19 NotAMap(String),
20 #[error("attribute requires a :db/ident keyword")]
22 MissingIdent,
23 #[error("unknown or missing :db/valueType {0}")]
25 BadValueType(String),
26 #[error("unknown :db/cardinality {0}")]
28 BadCardinality(String),
29 #[error("unknown :db/unique {0}")]
31 BadUnique(String),
32 #[error("duplicate :db/ident {0}")]
34 DuplicateIdent(String),
35}
36
37fn kw(text: &str) -> Edn {
38 Edn::keyword(text)
39}
40
41pub fn schema_from_edn(forms: &[Edn]) -> Result<(Schema, Idents), SchemaFormError> {
51 let mut schema = Schema::default();
52 let mut idents = Idents::default();
53 bootstrap::install(&mut schema, &mut idents);
54 for (index, form) in forms.iter().enumerate() {
55 if !matches!(form, Edn::Map(_)) {
56 return Err(SchemaFormError::NotAMap(form.to_string()));
57 }
58 let id = EntityId::new(Partition::Db as u32, FIRST_ATTR_ID + index as u64);
59 let ident = form
60 .get(&kw("db/ident"))
61 .and_then(Edn::as_keyword)
62 .ok_or(SchemaFormError::MissingIdent)?
63 .clone();
64 if idents.entid(&ident).is_some() {
65 return Err(SchemaFormError::DuplicateIdent(ident.to_string()));
66 }
67 let value_type_name = form
68 .get(&kw("db/valueType"))
69 .and_then(Edn::as_keyword)
70 .map(|keyword| keyword.name.clone())
71 .ok_or_else(|| SchemaFormError::BadValueType("<missing>".into()))?;
72 let value_type = match value_type_name.as_str() {
73 "string" => ValueType::Str,
74 "long" => ValueType::Long,
75 "double" => ValueType::Double,
76 "boolean" => ValueType::Bool,
77 "instant" => ValueType::Instant,
78 "uuid" => ValueType::Uuid,
79 "keyword" => ValueType::Keyword,
80 "bytes" => ValueType::Bytes,
81 "ref" => ValueType::Ref,
82 other => return Err(SchemaFormError::BadValueType(other.to_owned())),
83 };
84 let cardinality = match form
85 .get(&kw("db/cardinality"))
86 .and_then(Edn::as_keyword)
87 .map(|keyword| keyword.name.clone())
88 {
89 None => Cardinality::One,
90 Some(name) => match name.as_str() {
91 "one" => Cardinality::One,
92 "many" => Cardinality::Many,
93 other => return Err(SchemaFormError::BadCardinality(other.to_owned())),
94 },
95 };
96 let unique = match form
97 .get(&kw("db/unique"))
98 .and_then(Edn::as_keyword)
99 .map(|keyword| keyword.name.clone())
100 {
101 None => None,
102 Some(name) => match name.as_str() {
103 "identity" => Some(Unique::Identity),
104 "value" => Some(Unique::Value),
105 other => return Err(SchemaFormError::BadUnique(other.to_owned())),
106 },
107 };
108 let flag = |name: &str| form.get(&kw(name)) == Some(&Edn::Bool(true));
109 schema.insert(Attribute {
110 id,
111 value_type,
112 cardinality,
113 unique,
114 is_component: flag("db/isComponent"),
115 indexed: flag("db/index") || unique.is_some(),
116 no_history: flag("db/noHistory"),
117 });
118 idents.insert(ident, id);
119 }
120 Ok((schema, idents))
121}