use indexmap::map::Entry;
use crate::error::{ConflictKind, Error, ErrorKind, Span};
use crate::value::{ObjectMap, Value};
use super::validate::is_valid_key;
pub(super) fn insert_value(
table: &mut ObjectMap,
path: &str,
value: Value,
line_num: usize,
span: Span,
) -> Result<(), Error> {
if !path.as_bytes().contains(&b'.') {
if !is_valid_key(path) {
return Err(Error::Structured(ErrorKind::InvalidKey {
line: line_num as u32,
key: path.to_string(),
span,
}));
}
return match table.entry(path.into()) {
Entry::Occupied(e) => {
let existing = e.get();
if matches!(existing, Value::Object(_)) && !matches!(value, Value::Object(_))
|| !matches!(existing, Value::Object(_)) && matches!(value, Value::Object(_))
{
Err(Error::Structured(ErrorKind::KeyPathConflict {
line: line_num as u32,
path: path.to_string(),
kind: ConflictKind::Overwrite {
existing: kind_label(existing),
new_kind: kind_label(&value),
},
span,
}))
} else {
Err(Error::Structured(ErrorKind::DuplicateKey {
line: line_num as u32,
key: path.to_string(),
span,
}))
}
}
Entry::Vacant(v) => {
v.insert(value);
Ok(())
}
};
}
insert_dotted(table, path, value, line_num, span)
}
fn insert_dotted(
mut table: &mut ObjectMap,
full_path: &str,
value: Value,
line_num: usize,
span: Span,
) -> Result<(), Error> {
let mut rest = full_path;
loop {
if let Some((part, tail)) = rest.split_once('.') {
if !is_valid_key(part) {
return Err(Error::Structured(ErrorKind::InvalidKey {
line: line_num as u32,
key: full_path.to_string(),
span,
}));
}
let entry = table
.entry(part.into())
.or_insert_with(|| Value::Object(ObjectMap::default()));
table = match entry {
Value::Object(sub) => sub,
_ => {
return Err(Error::Structured(ErrorKind::KeyPathConflict {
line: line_num as u32,
path: full_path.to_string(),
kind: ConflictKind::BlockedByValue,
span,
}));
}
};
rest = tail;
} else {
if !is_valid_key(rest) {
return Err(Error::Structured(ErrorKind::InvalidKey {
line: line_num as u32,
key: full_path.to_string(),
span,
}));
}
return match table.entry(rest.into()) {
Entry::Occupied(_) => Err(Error::Structured(ErrorKind::DuplicateKey {
line: line_num as u32,
key: full_path.to_string(),
span,
})),
Entry::Vacant(v) => {
v.insert(value);
Ok(())
}
};
}
}
}
fn kind_label(v: &Value) -> &'static str {
match v {
Value::Null => "null",
Value::Bool(_) => "bool",
Value::Integer(_) => "integer",
Value::Float(_) => "float",
Value::String(_) => "string",
Value::Array(_) => "array",
Value::Object(_) => "object",
}
}