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
use std::str;
use std::str::FromStr;
use toml_edit::{Array, Datetime, DocumentMut, InlineTable, Item, Table, Value};
use crate::errors::TomliError;
use crate::{ValueType, parser};
pub(crate) fn parse_toml_path(
path: Vec<parser::Item>,
root: &mut Item,
dotted_key: bool,
) -> Result<&mut Item, TomliError> {
let mut item = root;
// 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 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)
.expect("BUG: Could not find key that was just inserted")
}
// 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(""));
Item::Value(Value::InlineTable(table))
} else {
let mut table = Table::new();
table.set_dotted(dotted_key);
table.insert(key, toml_edit::value(""));
Item::Table(table)
};
item.get_mut(key)
.expect("BUG: Could not find key that was just inserted")
}
};
}
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)
.expect("BUG: Expected item at index but could not find it")
}
_ => {
let mut array = Array::new();
array.push("");
*item = Item::Value(Value::Array(array));
inline_table = true;
item.get_mut(0)
.expect("BUG: Expected item at index but could not find it")
}
};
}
};
}
Ok(item)
}
pub fn exec(
document: &mut DocumentMut,
query: &str,
value: &str,
value_type: ValueType,
dotted_key: bool,
) -> Result<String, 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 item = parse_toml_path(parser::evaluate(query)?, document.as_item_mut(), dotted_key)?;
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)?),
ValueType::Datetime => *item = toml_edit::value::<Datetime>(Datetime::from_str(value)?),
};
Ok(document.to_string())
}