easy_configuration_format/
data.rs

1use std::{collections::HashMap, ops::{Deref, DerefMut}};
2
3
4
5/// Holds data for a file's contents, layout, and version
6#[derive(Debug, Clone, PartialEq)]
7pub struct File {
8	/// Contents of file
9	pub values: HashMap<String, Value>,
10	/// Layout of file
11	pub layout: Vec<LayoutEntry>,
12	/// Version of file (strongly recommended to hold the latest version of settings that your application supports)
13	pub version: usize,
14}
15
16impl Deref for File {
17	type Target = HashMap<String, Value>;
18	fn deref(&self) -> &Self::Target {
19		&self.values
20	}
21}
22
23impl DerefMut for File {
24	fn deref_mut(&mut self) -> &mut Self::Target {
25		&mut self.values
26	}
27}
28
29
30
31/// Describes the layout of a loaded settings file
32#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
33pub enum LayoutEntry {
34	/// Empty line
35	Empty,
36	/// Key-value pair
37	Key (String),
38	/// Comment
39	Comment (String),
40}
41
42
43
44/// Represents a setting value
45#[derive(Debug, Clone, PartialEq, PartialOrd)]
46pub enum Value {
47	/// Intentionally empty value
48	Empty,
49	/// String value, can be any number of lines
50	String (String),
51	/// Int value
52	I64 (i64),
53	/// Float value
54	F64 (f64),
55	/// Bool value
56	Bool (bool),
57}
58
59impl Value {
60	/// Used for formatting settings
61	pub fn format(&self) -> String {
62		match self {
63			Self::Empty => String::from("empty"),
64			Self::String (string_value) => {
65				if string_value.contains("\n") {
66					let mut output = String::from("\"\n");
67					for line in string_value.split('\n') {
68						output.push('"');
69						output += line;
70						output.push('\n');
71					}
72					output
73				} else {
74					format!("\"{string_value}\"")
75				}
76			}
77			Self::I64 (i64_value) => i64_value.to_string(),
78			Self::F64 (f64_value) => f64_value.to_string(),
79			Self::Bool (true) => String::from("true"),
80			Self::Bool (false) => String::from("false"),
81		}
82	}
83	/// Returns "Empty", "String", "Int", "Float", or "Bool" according to enum state
84	pub const fn type_as_string(&self) -> &'static str {
85		match self {
86			Self::Empty => "Empty",
87			Self::String (_) => "String",
88			Self::I64 (_) => "Int",
89			Self::F64 (_) => "Float",
90			Self::Bool (_) => "Bool",
91		}
92	}
93	/// Returns "an Empty", "a String", "an Int", "a Float", or "a Bool" according to enum state
94	pub const  fn type_as_singular_string(&self) -> &'static str {
95		match self {
96			Self::Empty => "an Empty",
97			Self::String (_) => "a String",
98			Self::I64 (_) => "an Int",
99			Self::F64 (_) => "a Float",
100			Self::Bool (_) => "a Bool",
101		}
102	}
103}
104
105
106
107/// Errors while parsing settings
108#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
109pub struct ParseEntryError {
110	/// Line number of invalid entry (using 1-based indexing)
111	pub line: usize,
112	/// Error message / reason for being invalid
113	pub message: String,
114}
115
116impl ParseEntryError {
117	pub(crate) fn new(raw_line: usize, message: impl Into<String>) -> Self {
118		Self {
119			line: raw_line + 1,
120			message: message.into(),
121		}
122	}
123}
124
125impl std::error::Error for ParseEntryError {}
126
127impl std::fmt::Display for ParseEntryError {
128	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129		write!(f, "Invalid configuration entry at line {}: {}", self.line, self.message)
130	}
131}
132
133
134
135/// Errors while formatting settings
136/// Right now the only error that can occur is having a key specified in the layout that isn't defined in the settings hashmap
137#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
138pub struct FormatEntryError {
139	/// Name of missing key
140	pub missing_key: String,
141}
142
143impl FormatEntryError {
144	pub(crate) fn new(missing_key: impl Into<String>) -> Self {
145		Self {
146			missing_key: missing_key.into(),
147		}
148	}
149}
150
151impl std::error::Error for FormatEntryError {}
152
153impl std::fmt::Display for FormatEntryError {
154	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155		write!(f, "Failed to format configuration entry, no value found for key {}", self.missing_key)
156	}
157}