blob: 7456aa34f74df9e2ecd702254eff0ac8bf6c356b [file] [log] [blame] [edit]
use std::str::FromStr;
use crate::error::{ConfigError, Result};
use crate::map::Map;
use crate::value::{Value, ValueKind};
mod parser;
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub enum Expression {
Identifier(String),
Child(Box<Self>, String),
Subscript(Box<Self>, isize),
}
impl FromStr for Expression {
type Err = ConfigError;
fn from_str(s: &str) -> Result<Self> {
parser::from_str(s).map_err(ConfigError::PathParse)
}
}
fn sindex_to_uindex(index: isize, len: usize) -> usize {
if index >= 0 {
index as usize
} else {
len - (index.abs() as usize)
}
}
impl Expression {
pub fn get(self, root: &Value) -> Option<&Value> {
match self {
Self::Identifier(id) => {
match root.kind {
// `x` access on a table is equivalent to: map[x]
ValueKind::Table(ref map) => map.get(&id),
// all other variants return None
_ => None,
}
}
Self::Child(expr, key) => {
match expr.get(root) {
Some(value) => {
match value.kind {
// Access on a table is identical to Identifier, it just forwards
ValueKind::Table(ref map) => map.get(&key),
// all other variants return None
_ => None,
}
}
_ => None,
}
}
Self::Subscript(expr, index) => match expr.get(root) {
Some(value) => match value.kind {
ValueKind::Array(ref array) => {
let index = sindex_to_uindex(index, array.len());
if index >= array.len() {
None
} else {
Some(&array[index])
}
}
_ => None,
},
_ => None,
},
}
}
pub fn get_mut<'a>(&self, root: &'a mut Value) -> Option<&'a mut Value> {
match *self {
Self::Identifier(ref id) => match root.kind {
ValueKind::Table(ref mut map) => map.get_mut(id),
_ => None,
},
Self::Child(ref expr, ref key) => match expr.get_mut(root) {
Some(value) => match value.kind {
ValueKind::Table(ref mut map) => map.get_mut(key),
_ => None,
},
_ => None,
},
Self::Subscript(ref expr, index) => match expr.get_mut(root) {
Some(value) => match value.kind {
ValueKind::Array(ref mut array) => {
let index = sindex_to_uindex(index, array.len());
if index >= array.len() {
None
} else {
Some(&mut array[index])
}
}
_ => None,
},
_ => None,
},
}
}
pub fn get_mut_forcibly<'a>(&self, root: &'a mut Value) -> Option<&'a mut Value> {
match *self {
Self::Identifier(ref id) => match root.kind {
ValueKind::Table(ref mut map) => Some(
map.entry(id.clone())
.or_insert_with(|| Value::new(None, ValueKind::Nil)),
),
_ => None,
},
Self::Child(ref expr, ref key) => match expr.get_mut_forcibly(root) {
Some(value) => {
if let ValueKind::Table(ref mut map) = value.kind {
Some(
map.entry(key.clone())
.or_insert_with(|| Value::new(None, ValueKind::Nil)),
)
} else {
*value = Map::<String, Value>::new().into();
if let ValueKind::Table(ref mut map) = value.kind {
Some(
map.entry(key.clone())
.or_insert_with(|| Value::new(None, ValueKind::Nil)),
)
} else {
unreachable!();
}
}
}
_ => None,
},
Self::Subscript(ref expr, index) => match expr.get_mut_forcibly(root) {
Some(value) => {
match value.kind {
ValueKind::Array(_) => (),
_ => *value = Vec::<Value>::new().into(),
}
match value.kind {
ValueKind::Array(ref mut array) => {
let index = sindex_to_uindex(index, array.len());
if index >= array.len() {
array
.resize((index + 1) as usize, Value::new(None, ValueKind::Nil));
}
Some(&mut array[index])
}
_ => None,
}
}
_ => None,
},
}
}
pub fn set(&self, root: &mut Value, value: Value) {
match *self {
Self::Identifier(ref id) => {
// Ensure that root is a table
match root.kind {
ValueKind::Table(_) => {}
_ => {
*root = Map::<String, Value>::new().into();
}
}
match value.kind {
ValueKind::Table(ref incoming_map) => {
// Pull out another table
let target = if let ValueKind::Table(ref mut map) = root.kind {
map.entry(id.clone())
.or_insert_with(|| Map::<String, Value>::new().into())
} else {
unreachable!();
};
// Continue the deep merge
for (key, val) in incoming_map {
Self::Identifier(key.clone()).set(target, val.clone());
}
}
_ => {
if let ValueKind::Table(ref mut map) = root.kind {
// Just do a simple set
if let Some(existing) = map.get_mut(id) {
*existing = value;
} else {
map.insert(id.clone(), value);
}
}
}
}
}
Self::Child(ref expr, ref key) => {
if let Some(parent) = expr.get_mut_forcibly(root) {
if !matches!(parent.kind, ValueKind::Table(_)) {
// Didn't find a table. Oh well. Make a table and do this anyway
*parent = Map::<String, Value>::new().into();
}
Self::Identifier(key.clone()).set(parent, value);
}
}
Self::Subscript(ref expr, index) => {
if let Some(parent) = expr.get_mut_forcibly(root) {
if !matches!(parent.kind, ValueKind::Array(_)) {
*parent = Vec::<Value>::new().into()
}
if let ValueKind::Array(ref mut array) = parent.kind {
let uindex = sindex_to_uindex(index, array.len());
if uindex >= array.len() {
array.resize((uindex + 1) as usize, Value::new(None, ValueKind::Nil));
}
array[uindex] = value;
}
}
}
}
}
}