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
use std::str;
use std::str::FromStr;
use toml_edit::{DocumentMut, InlineTable, Item, Table, Value};
use crate::errors::TomliError;
use crate::{parser, ValueType};
pub fn exec(
mut document: DocumentMut,
query: &str,
value: &str,
value_type: ValueType,
) -> Result<(), TomliError> {
// Editing the whole document makes no sense
// If the user wants to do this, then he should use echo (or a similiar tool) to edit the file manually
if query == "." || query.is_empty() {
return Err(TomliError::InvalidInputQuery(
"set",
"Editing the document as a whole is currently not supported",
));
}
let mut item = document.as_item_mut();
let toml_path = parser::evaluate(query)?;
// When parsing the toml path we want to keep track of whether we are currently in a inlined
// table or not. Depending on this, we either crate a new table or a new inlined table.
let mut inline_table = false;
for segment in toml_path.iter() {
match segment {
parser::Item::Key(key) => {
item = match item {
// Either retrieve the item from a table or create a new one
Item::Table(t) => t.entry(key).or_insert(Item::None),
Item::ArrayOfTables(_) | Item::Value(Value::Array(_)) => {
return Err(TomliError::InvalidKeyAccess(key.to_string()))
}
// Either retrieve the item from a table or create a new one
// Also mark that we are now in an inline table
Item::Value(Value::InlineTable(t)) => {
inline_table = true;
t.entry(key)
.or_insert(Value::InlineTable(toml_edit::InlineTable::new()));
item.get_mut(key).unwrap()
}
// Create a new table / inline table and add the new item to it
// The way this works is the follwing:
//
// The first element in a toml document will always be a table
// This means that item will either be an actual item or an empty item (see the
// first match arm)
// Since we already have the item, we just set it to either a table or inline
// table and add a new key / value pair to it and return that item
_ => {
*item = if inline_table {
let mut table = InlineTable::new();
table.insert(key, Value::from_str("").unwrap());
Item::Value(Value::InlineTable(table))
} else {
let mut table = Table::new();
table.insert(key, toml_edit::value(""));
Item::Table(table)
};
// We can unwrap since we just added this key
item.get_mut(key).unwrap()
}
};
}
parser::Item::ArrayIndex(index) => {
item = match item {
Item::ArrayOfTables(_) | Item::Value(Value::Array(_)) => {
// Either return the item at index "index" or show an out of bounds message
// TODO: Maybe we want to replace the out of bounds message with just
// appending the item to the end of the array
if item.get(*index).is_none() {
return Err(TomliError::IndexOutOfBounds(*index));
}
item.get_mut(index).unwrap()
}
// We only accept arrays here
// All other types are an invalid access
_ => return Err(TomliError::InvalidKeyAccess(index.to_string())),
};
}
};
}
if item.is_table() || item.is_array() || item.is_array_of_tables() {
return Err(TomliError::InvalidInputQuery(
"set",
"Transforming a table or an array to a key / pair value is not allowed",
));
}
match value_type {
ValueType::Str => *item = toml_edit::value(value),
ValueType::Int => *item = toml_edit::value::<i64>(i64::from_str(value)?),
ValueType::Float => *item = toml_edit::value::<f64>(f64::from_str(value)?),
ValueType::Bool => *item = toml_edit::value::<bool>(bool::from_str(value)?),
};
println!("{document}");
Ok(())
}