vpin/vpx/
jsonmodel.rs

1use serde::{Deserialize, Serialize};
2use serde_json::to_value;
3use std::collections::HashMap;
4
5use crate::vpx::collection::Collection;
6use crate::vpx::custominfotags::CustomInfoTags;
7use crate::vpx::gamedata::{GameData, GameDataJson};
8use crate::vpx::tableinfo::TableInfo;
9
10#[derive(Serialize, Deserialize)]
11struct CollectionJson {
12    name: String,
13    items: Vec<String>,
14    fire_events: bool,
15    stop_single_events: bool,
16    group_elements: bool,
17}
18
19/// Since we want to decouple out json model from the vpx model, we need to
20/// define a json model that we can serialize to and from. This is a bit of a
21/// pain, but it's the only way to do it.
22#[derive(Serialize, Deserialize)]
23struct TableInfoJson {
24    table_name: Option<String>,
25    author_name: Option<String>,
26    table_blurb: Option<String>,
27    table_rules: Option<String>,
28    author_email: Option<String>,
29    release_date: Option<String>,
30    table_save_rev: Option<String>,
31    table_version: Option<String>,
32    author_website: Option<String>,
33    table_save_date: Option<String>,
34    table_description: Option<String>,
35    properties: HashMap<String, String>,
36    // since the ordering is important, we need to keep track of it
37    properties_order: Vec<String>,
38}
39
40pub fn info_to_json(
41    table_info: &TableInfo,
42    custom_info_tags: &CustomInfoTags,
43) -> serde_json::Value {
44    // TODO convert to a serde
45    let info_json = TableInfoJson {
46        table_name: table_info.table_name.clone(),
47        author_name: table_info.author_name.clone(),
48        table_blurb: table_info.table_blurb.clone(),
49        table_rules: table_info.table_rules.clone(),
50        author_email: table_info.author_email.clone(),
51        release_date: table_info.release_date.clone(),
52        table_save_rev: table_info.table_save_rev.clone(),
53        table_version: table_info.table_version.clone(),
54        author_website: table_info.author_website.clone(),
55        table_save_date: table_info.table_save_date.clone(),
56        table_description: table_info.table_description.clone(),
57        properties: table_info.properties.clone(),
58        properties_order: custom_info_tags.clone(),
59    };
60    to_value(info_json).unwrap()
61}
62
63pub fn json_to_info(
64    json: serde_json::Value,
65    screenshot: Option<Vec<u8>>,
66) -> Result<(TableInfo, CustomInfoTags), serde_json::Error> {
67    let info_json: TableInfoJson = serde_json::from_value(json.clone())?;
68    let table_info = TableInfo {
69        table_name: info_json.table_name,
70        author_name: info_json.author_name,
71        screenshot,
72        table_blurb: info_json.table_blurb,
73        table_rules: info_json.table_rules,
74        author_email: info_json.author_email,
75        release_date: info_json.release_date,
76        table_save_rev: info_json.table_save_rev,
77        table_version: info_json.table_version,
78        author_website: info_json.author_website,
79        table_save_date: info_json.table_save_date,
80        table_description: info_json.table_description,
81        properties: info_json.properties,
82    };
83    let custom_info_tags = info_json.properties_order;
84    Ok((table_info, custom_info_tags))
85}
86
87pub fn collections_json(collections: &[Collection]) -> serde_json::Value {
88    let mut collections_json = Vec::new();
89    for collection in collections {
90        let collection_json = CollectionJson {
91            name: collection.name.clone(),
92            items: collection.items.clone(),
93            fire_events: collection.fire_events,
94            stop_single_events: collection.stop_single_events,
95            group_elements: collection.group_elements,
96        };
97        collections_json.push(collection_json);
98    }
99    to_value(collections_json).unwrap()
100}
101
102pub fn json_to_collections(json: serde_json::Value) -> Result<Vec<Collection>, serde_json::Error> {
103    let collections_json: Vec<CollectionJson> = serde_json::from_value(json)?;
104    let mut collections = Vec::new();
105    for collection_json in collections_json {
106        let collection = Collection {
107            name: collection_json.name,
108            items: collection_json.items,
109            fire_events: collection_json.fire_events,
110            stop_single_events: collection_json.stop_single_events,
111            group_elements: collection_json.group_elements,
112        };
113        collections.push(collection);
114    }
115    Ok(collections)
116}
117
118pub fn game_data_to_json(game_data: &GameData) -> serde_json::Value {
119    let game_data_json = GameDataJson::from_game_data(game_data);
120    to_value(game_data_json).unwrap()
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126    use crate::vpx::collection::Collection;
127    use crate::vpx::custominfotags::CustomInfoTags;
128    use crate::vpx::gamedata::GameData;
129    use crate::vpx::tableinfo::TableInfo;
130    use serde_json::Value;
131
132    #[test]
133    fn test_info_to_json() {
134        let table_info = TableInfo::default();
135        let custom_info_tags = CustomInfoTags::default();
136        let json = info_to_json(&table_info, &custom_info_tags);
137        let (table_info2, custom_info_tags2) = json_to_info(json, None).unwrap();
138        assert_eq!(table_info, table_info2);
139        assert_eq!(custom_info_tags, custom_info_tags2);
140    }
141
142    #[test]
143    fn test_collections_to_json() {
144        let collections = vec![
145            Collection {
146                name: "collection1".to_string(),
147                items: vec!["item1".to_string(), "item2".to_string()],
148                fire_events: true,
149                stop_single_events: false,
150                group_elements: true,
151            },
152            Collection {
153                name: "collection2".to_string(),
154                items: vec!["item3".to_string(), "item4".to_string()],
155                fire_events: false,
156                stop_single_events: true,
157                group_elements: false,
158            },
159        ];
160        let json = collections_json(&collections);
161        let collections2 = json_to_collections(json).unwrap();
162        assert_eq!(collections, collections2);
163    }
164
165    #[test]
166    fn test_game_data_to_json() {
167        let game_data = GameData::default();
168        let json = game_data_to_json(&game_data);
169        // assert that we have a value of type object that at least contains "name": String("Table1")
170        let map = json.as_object().unwrap();
171        let name = map.get("name").unwrap();
172        assert_eq!(name, &Value::String("Table1".to_string()));
173    }
174}