use std::fs;
use arangors_lite::{ClientError, Database};
use serde::{Deserialize, Serialize};
use crate::schema::{CollectionSchema, GraphSchema, IndexSchema, SchemaDatabaseOperation};
use crate::Error;
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct DatabaseSchema {
pub version: Option<u64>,
pub collections: Vec<CollectionSchema>,
#[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")]
pub indexes: Vec<IndexSchema>,
#[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")]
pub graphs: Vec<GraphSchema>,
}
impl DatabaseSchema {
#[must_use]
pub fn collection_index(&self, name: &str) -> Option<usize> {
self.collections.iter().position(|c| c.name == name)
}
#[must_use]
pub fn collection(&self, name: &str) -> Option<&CollectionSchema> {
self.collections.iter().find(|c| c.name == name)
}
#[must_use]
pub fn index_index(&self, collection: &str, name: &str) -> Option<usize> {
self.indexes
.iter()
.position(|c| c.name == name && c.collection == collection)
}
#[must_use]
pub fn index(&self, collection: &str, name: &str) -> Option<&IndexSchema> {
self.indexes
.iter()
.find(|c| c.name == name && c.collection == collection)
}
#[must_use]
pub fn graph_index(&self, name: &str) -> Option<usize> {
self.graphs.iter().position(|c| c.0.name == name)
}
#[must_use]
pub fn graph(&self, name: &str) -> Option<&GraphSchema> {
self.graphs.iter().find(|c| c.0.name == name)
}
pub fn load(path: &str) -> Result<Self, Error> {
let file = match fs::read_to_string(path) {
Ok(val) => val,
Err(error) => {
return Err(Error::InitError {
item: path.to_string(),
message: error.to_string(),
});
}
};
let value: Self = match serde_yaml::from_str(&file) {
Ok(val) => val,
Err(error) => {
return Err(Error::InitError {
item: path.to_string(),
message: error.to_string(),
});
}
};
Ok(value)
}
}
#[maybe_async::maybe_async]
impl SchemaDatabaseOperation for DatabaseSchema {
type PoolType = ();
async fn apply_to_database(
&self,
database: &Database,
silent: bool,
) -> Result<Option<Self::PoolType>, ClientError> {
for item in &self.collections {
Self::handle_error(item.apply_to_database(database, silent).await, silent)?;
}
for item in &self.indexes {
Self::handle_error(item.apply_to_database(database, silent).await, silent)?;
}
for item in &self.graphs {
Self::handle_error(item.apply_to_database(database, silent).await, silent)?;
}
Ok(Some(()))
}
async fn drop(&self, database: &Database) -> Result<(), ClientError> {
for item in &self.collections {
item.drop(database).await?;
}
for item in &self.indexes {
item.drop(database).await?;
}
for item in &self.graphs {
item.drop(database).await?;
}
Ok(())
}
async fn get(&self, _database: &Database) -> Result<Self::PoolType, ClientError> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use arangors_lite::graph::{EdgeDefinition, Graph, GraphOptions};
use arangors_lite::index::IndexSettings;
use crate::schema::IndexSchema;
use super::*;
fn schema() -> DatabaseSchema {
DatabaseSchema {
version: None,
collections: vec![
CollectionSchema {
name: "collectionA".to_string(),
is_edge_collection: false,
wait_for_sync: None,
},
CollectionSchema {
name: "collectionB".to_string(),
is_edge_collection: false,
wait_for_sync: Some(true),
},
CollectionSchema {
name: "edgeCollectionA".to_string(),
is_edge_collection: true,
wait_for_sync: None,
},
],
indexes: vec![
IndexSchema {
name: "OnUsername".to_string(),
collection: "CollectionA".to_string(),
fields: vec!["username".to_string()],
settings: IndexSettings::Persistent {
unique: true,
sparse: false,
deduplicate: false,
},
},
IndexSchema {
name: "OnAgeAndemail".to_string(),
collection: "CollectionB".to_string(),
fields: vec!["age".to_string(), "email".to_string()],
settings: IndexSettings::Ttl { expire_after: 3600 },
},
],
graphs: vec![GraphSchema(Graph {
name: "namedGraph".to_string(),
edge_definitions: vec![EdgeDefinition {
collection: "edgeCollection1".to_string(),
from: vec!["collectionA".to_string()],
to: vec!["collectionB".to_string(), "collectionC".to_string()],
}],
orphan_collections: vec![],
is_smart: None,
is_disjoint: None,
options: Some(GraphOptions {
smart_graph_attribute: None,
number_of_shards: None,
replication_factor: Some(10),
write_concern: None,
}),
})],
}
}
#[test]
fn serialization_works() {
let schema = schema();
serde_yaml::to_string(&schema).unwrap();
}
}