1use std::fs;
2
3use arangors_lite::{ClientError, Database};
4use serde::{Deserialize, Serialize};
5
6use crate::schema::{CollectionSchema, GraphSchema, IndexSchema, SchemaDatabaseOperation};
7use crate::Error;
8
9#[derive(Debug, Serialize, Deserialize, Clone, Default)]
12pub struct DatabaseSchema {
13 pub version: Option<u64>,
15 pub collections: Vec<CollectionSchema>,
17 #[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")]
19 pub indexes: Vec<IndexSchema>,
20 #[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")]
22 pub graphs: Vec<GraphSchema>,
23}
24
25impl DatabaseSchema {
26 #[must_use]
28 pub fn collection_index(&self, name: &str) -> Option<usize> {
29 self.collections.iter().position(|c| c.name == name)
30 }
31
32 #[must_use]
34 pub fn collection(&self, name: &str) -> Option<&CollectionSchema> {
35 self.collections.iter().find(|c| c.name == name)
36 }
37
38 #[must_use]
40 pub fn index_index(&self, collection: &str, name: &str) -> Option<usize> {
41 self.indexes
42 .iter()
43 .position(|c| c.name == name && c.collection == collection)
44 }
45
46 #[must_use]
48 pub fn index(&self, collection: &str, name: &str) -> Option<&IndexSchema> {
49 self.indexes
50 .iter()
51 .find(|c| c.name == name && c.collection == collection)
52 }
53
54 #[must_use]
56 pub fn graph_index(&self, name: &str) -> Option<usize> {
57 self.graphs.iter().position(|c| c.0.name == name)
58 }
59
60 #[must_use]
62 pub fn graph(&self, name: &str) -> Option<&GraphSchema> {
63 self.graphs.iter().find(|c| c.0.name == name)
64 }
65
66 pub fn load(path: &str) -> Result<Self, Error> {
72 let file = match fs::read_to_string(path) {
73 Ok(val) => val,
74 Err(error) => {
75 return Err(Error::InitError {
76 item: path.to_string(),
77 message: error.to_string(),
78 });
79 }
80 };
81 let value: Self = match serde_yaml::from_str(&file) {
82 Ok(val) => val,
83 Err(error) => {
84 return Err(Error::InitError {
85 item: path.to_string(),
86 message: error.to_string(),
87 });
88 }
89 };
90 Ok(value)
91 }
92}
93
94#[maybe_async::maybe_async]
95impl SchemaDatabaseOperation for DatabaseSchema {
96 type PoolType = ();
97
98 async fn apply_to_database(
99 &self,
100 database: &Database,
101 silent: bool,
102 ) -> Result<Option<Self::PoolType>, ClientError> {
103 for item in &self.collections {
104 Self::handle_error(item.apply_to_database(database, silent).await, silent)?;
105 }
106 for item in &self.indexes {
107 Self::handle_error(item.apply_to_database(database, silent).await, silent)?;
108 }
109 for item in &self.graphs {
110 Self::handle_error(item.apply_to_database(database, silent).await, silent)?;
111 }
112 Ok(Some(()))
113 }
114
115 async fn drop(&self, database: &Database) -> Result<(), ClientError> {
116 for item in &self.collections {
117 item.drop(database).await?;
118 }
119 for item in &self.indexes {
120 item.drop(database).await?;
121 }
122 for item in &self.graphs {
123 item.drop(database).await?;
124 }
125 Ok(())
126 }
127
128 async fn get(&self, _database: &Database) -> Result<Self::PoolType, ClientError> {
129 Ok(())
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use arangors_lite::graph::{EdgeDefinition, Graph, GraphOptions};
136 use arangors_lite::index::IndexSettings;
137
138 use crate::schema::IndexSchema;
139
140 use super::*;
141
142 fn schema() -> DatabaseSchema {
143 DatabaseSchema {
144 version: None,
145 collections: vec![
146 CollectionSchema {
147 name: "collectionA".to_string(),
148 is_edge_collection: false,
149 wait_for_sync: None,
150 },
151 CollectionSchema {
152 name: "collectionB".to_string(),
153 is_edge_collection: false,
154 wait_for_sync: Some(true),
155 },
156 CollectionSchema {
157 name: "edgeCollectionA".to_string(),
158 is_edge_collection: true,
159 wait_for_sync: None,
160 },
161 ],
162 indexes: vec![
163 IndexSchema {
164 name: "OnUsername".to_string(),
165 collection: "CollectionA".to_string(),
166 fields: vec!["username".to_string()],
167 settings: IndexSettings::Persistent {
168 unique: true,
169 sparse: false,
170 deduplicate: false,
171 },
172 },
173 IndexSchema {
174 name: "OnAgeAndemail".to_string(),
175 collection: "CollectionB".to_string(),
176 fields: vec!["age".to_string(), "email".to_string()],
177 settings: IndexSettings::Ttl { expire_after: 3600 },
178 },
179 ],
180 graphs: vec![GraphSchema(Graph {
181 name: "namedGraph".to_string(),
182 edge_definitions: vec![EdgeDefinition {
183 collection: "edgeCollection1".to_string(),
184 from: vec!["collectionA".to_string()],
185 to: vec!["collectionB".to_string(), "collectionC".to_string()],
186 }],
187 orphan_collections: vec![],
188 is_smart: None,
189 is_disjoint: None,
190 options: Some(GraphOptions {
191 smart_graph_attribute: None,
192 number_of_shards: None,
193 replication_factor: Some(10),
194 write_concern: None,
195 }),
196 })],
197 }
198 }
199
200 #[test]
201 fn serialization_works() {
202 let schema = schema();
203 serde_yaml::to_string(&schema).unwrap();
204 }
205}