1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! To populate the database with pre-created data.

use async_trait::async_trait;
use mongodb::Client;
use serde::{de::DeserializeOwned, ser::Serialize};
use serde_json::Value;
use std::{error::Error, fs, io::ErrorKind};

use crate::{
    meta_store::META_STORE,
    models::{
        caching::Caching,
        db_query_api::{commons::QCommons, paladins::QPaladins},
    },
};

/// To populate the database with pre-created data.
///
/// 1.Create a fixtures folder at the root of the project.
/// 2.Save data files for Models in it.
///
/// # Example:
///
/// ```
/// // Initial data for Model.
/// // ./fixtures/cities.json
/// [
///   {
///     "city_name": "London",
///     "description": "London is the capital city of England and the United Kingdom.",
///  },
///  {
///     "city_name": "Dresden",
///     "description": "Dresden is the capital of the East German state of Saxony.",
///  }
/// ]
///
/// // Run fixtures
/// fn run_migration() -> Result<(), Box<dyn Error>> {
///     ...
///     let fixture_name = "cities";
///     ModelName::run_fixture(&client, fixture_name).await?;
///     Ok(())
/// }
/// ```
///
#[async_trait(?Send)]
pub trait Fixtures: Caching + QPaladins + QCommons {
    async fn run_fixture(client: &Client, fixture_name: &str) -> Result<(), Box<dyn Error>>
    where
        Self: Serialize + DeserializeOwned + Sized,
    {
        // If the collection is not empty, exit the method
        if Self::estimated_document_count(client, None).await? > 0 {
            return Ok(());
        }
        // Get metadata of Model
        let (model_name, model_json, field_type_map) = {
            // Get a key to access the metadata store.
            let key = Self::key()?;
            // Get metadata store.
            let store = META_STORE.lock().await;
            // Get metadata of Model.
            if let Some(meta) = store.get(&key) {
                (
                    meta.model_name.clone(),
                    meta.model_json.clone(),
                    meta.field_type_map.clone(),
                )
            } else {
                Err(format!(
                    "Model key: `{key}` ; Method: `run_fixture()` => \
                    Failed to get data from cache.",
                ))?
            }
        };
        // Get data from fixture file
        let json_val = {
            // Create path
            let fixture_path = format!("./fixtures/{fixture_name}.json");
            // Get json-line
            let json_str = fs::read_to_string(fixture_path.clone()).unwrap_or_else(|error| {
                if error.kind() == ErrorKind::NotFound {
                    Err(format!(
                        "Model: `{model_name}` > Method: \
                        `run_fixture()` => File is missing - {fixture_path}"
                    ))
                    .unwrap()
                } else {
                    Err(format!(
                        "Model: `{model_name}` > Method: \
                        `run_fixture()` => Problem opening the file: {0:?}",
                        error
                    ))
                    .unwrap()
                }
            });
            serde_json::from_str::<Value>(json_str.as_str())?
        };
        // Get an array of fixtures
        if let Some(fixtures_vec) = json_val.as_array() {
            for fixture in fixtures_vec {
                let mut model_json = model_json.clone();
                for (field_name, field_type) in field_type_map.iter() {
                    if let Some(data) = fixture.get(field_name) {
                        let value_key = if field_type == "CheckBox" {
                            "checked"
                        } else {
                            "value"
                        };
                        *model_json
                            .get_mut(field_name)
                            .unwrap()
                            .get_mut(value_key)
                            .unwrap() = data.clone();
                    }
                }
                // Get an instance of the model and save the data to the database
                let mut instance = serde_json::from_value::<Self>(model_json)?;
                let output_data = instance.save(client, None, None).await?;
                if !output_data.is_valid() {
                    Err(format!(
                        "Model: `{model_name}` > Method: `run_fixture()` => {0}",
                        output_data.err_msg()
                    ))?
                }
            }
        } else {
            Err(format!(
                "Model: `{model_name}` > Method: \
                `run_fixture()` => Fixture does not contain an array of objects."
            ))?
        }

        Ok(())
    }
}