rof_rs/object_format/
rof.rs

1use crate::object_format::{DataValue, PropertyType};
2use std::fs::OpenOptions;
3
4use std::io::{BufReader, Read, Write};
5
6use super::data_value::bool::DataValueBool;
7use super::data_value::enum_value::DataValueEnum;
8use super::ignore_str_split::{ignoring_compliant_split_str, SplitIgnoreRule, SplitIgnoreRuleType};
9use super::property::data_value_from_string;
10
11/// ## Rof
12/// A struct that contains an inner ```DataValue``` trait implementing object which contains utility functions for easy saving and loading that type to files, also is the backbone for the higher level ```RofCompat``` api.
13///
14/// A Rof ```DataValue``` does not strictly have to be a struct or enum, you can save and load any ```DataValue``` implementing type including
15/// * ```DataValueBool```
16/// * ```DataValueChararacter```
17/// * ```DataValueFloat```
18/// * ```DataValueInteger```
19/// * ```DataValueString```
20/// * ```DataValueTuple``` (although this data value type does exist, the RofCompat API does not support types in it's current state)
21/// * ```DataValueArray```
22/// * ```DataValueHashmap```
23/// * ```DataValueEnum```
24/// * ```DataValueStruct```
25///
26/// You cannot create your own ```DataValue``` types, as when deserializing a Rof from a string, a function named ```data_value_from_string``` checks the string against every ```DataValue``` type
27/// until it finds a type that when deserialized doesn't return ```None```. As this function only checks against inbuilt types that it knows about, you cannot create your own
28/// as they would not get deserialized.
29
30#[derive(Debug)]
31pub struct Rof {
32    inner: Box<dyn DataValue>,
33}
34
35impl Rof {
36    /// Create a ```Rof``` from an inner DataValue implementing type
37    pub fn new(inner: Box<dyn DataValue>) -> Self {
38        Self { inner }
39    }
40
41    /// ## Serialize
42    /// Serialize the ```Rof``` to a string
43    ///
44    /// ## Argmuents
45    ///
46    /// * `pretty_print` - A bool which when set to true adds unneccesary white space, tabs and newlines to the serialized output that will not change the data, but will make the output string more human-readable if saved to a file.
47    pub fn serialize(&self, pretty_print: bool) -> String {
48        match self.inner.is_implicit() {
49            true => self.inner.serialize(pretty_print, 0).1,
50            false => match pretty_print {
51                true => format!(
52                    "{}: {}",
53                    self.inner.serialize(pretty_print, 0).1,
54                    self.inner.get_type().serialize(pretty_print)
55                ),
56                false => format!(
57                    "{}:{}",
58                    self.inner.serialize(pretty_print, 0).1,
59                    self.inner.get_type().serialize(pretty_print)
60                ),
61            },
62        }
63    }
64
65    /// ## Deserialize
66    /// Deserialize a string to a ```Rof```
67    ///
68    /// ## Arguments
69    ///
70    /// * `serialized_rof` - A string slice that represents a ```DataValue``` implementing data type in string form.
71    ///
72    /// If this function fails to to deserialize the type, it will default to returning ```DataValueEnum::none()```
73    pub fn deserialize(serialized_rof: &str) -> Self {
74        match &ignoring_compliant_split_str(
75            serialized_rof,
76            ':',
77            true,
78            vec![
79                SplitIgnoreRule::new(SplitIgnoreRuleType::PAIR('"')).set_ecapsulates_raw_text(true),
80                SplitIgnoreRule::new(SplitIgnoreRuleType::PAIR('\''))
81                    .set_ecapsulates_raw_text(true),
82                SplitIgnoreRule::new(SplitIgnoreRuleType::NEST('{', '}')),
83            ],
84        )[..]
85        {
86            [implicit_value] => Self {
87                inner: data_value_from_string(&PropertyType::implicit(), implicit_value.trim()),
88            },
89            [explicit_value, explicit_type] => Self {
90                inner: data_value_from_string(
91                    &PropertyType::deserialize(&explicit_type.trim()),
92                    explicit_value.trim(),
93                ),
94            },
95            _ => Self {
96                inner: Box::new(DataValueEnum::none()),
97            },
98        }
99    }
100
101    /// ## Load From File
102    /// Loads a ```Rof``` object from file by loading the file as a string and using that on the ```deserialize``` function to convert that to a Rof
103    ///
104    /// ## Arguments
105    ///
106    /// * `file_path` - A string slice representing the load file path
107    ///
108    /// If the function fails in any way, ```DataValueEnum::none()``` will be returned
109    pub fn load_from_file(file_path: &str) -> Self {
110        match OpenOptions::new().read(true).open(file_path) {
111            Ok(file) => {
112                let mut reader = BufReader::new(file);
113
114                let mut data = String::new();
115
116                reader
117                    .read_to_string(&mut data)
118                    .expect("Unable to read rof from file");
119
120                Self::deserialize(&data)
121            }
122            Err(_) => Self::new(Box::new(DataValueEnum::none())),
123        }
124    }
125
126    /// ## Save To File
127    /// Saves the inner ``DataValue``` implementing value of the ```Rof``` to a file using the object on the ```serialize``` function to serialize the object to a string
128    ///
129    /// Will return an error if the file saving fails
130    ///
131    /// ## Arguments
132    ///
133    /// * `file_path` - A string slice representing the save file path
134    /// * `pretty_print` - A bool which when set to true adds unneccesary white space, tabs and newlines to the serialized output that will not change the data, but will make the output file more human-readable.
135    pub fn save_to_file(&self, file_path: &str, pretty_print: bool) -> Result<(), ()> {
136        match OpenOptions::new()
137            .write(true)
138            .truncate(true)
139            .create(true)
140            .open(file_path)
141        {
142            Ok(mut file) => match file.write_all(self.serialize(pretty_print).as_bytes()) {
143                Ok(_) => Ok(()),
144                Err(_) => Err(()),
145            },
146            Err(_) => Err(()),
147        }
148    }
149
150    /// ## Get Object
151    /// Returns an **immutable** clone of the inner ```DataValue``` implementing object which this ```Rof``` contains.
152    ///
153    /// It is important to note that the returned value is immutable
154    /// meaning you cannot get the inner value, modify something and run the ```serialize``` or ```save_to_file``` function on the same ```Rof```, as the inner value of the ```Rof``` will not have changed. You will need to create a new ```Rof``` with the newly modified
155    /// ```DataValue``` implementing object for the changes to be reflected for the file.
156
157    pub fn get_object(&self) -> Box<dyn DataValue> {
158        self.inner.clone_data_value()
159    }
160}