use std::fmt;
use indexmap::IndexMap;
use ltk_hash::{BinHash, Hash as _, HashValue, HashWidth, WadHash};
use crate::{
path::{KeyLiteral, MapKey, PropertyPath, Segment, Subscript, ValuePath, ValueSegment},
property::{values, Kind},
walk::{self, Declaration, TreeValue as _},
Bin, BinObject, PropertyValueEnum, ValueSlot,
};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ValueShape {
pub kind: Kind,
pub item_kind: Option<Kind>,
pub key_kind: Option<Kind>,
pub class: Option<BinHash>,
}
impl ValueShape {
#[must_use]
pub fn of(value: &PropertyValueEnum) -> Self {
walk::owned::declaration(value).into()
}
#[must_use]
#[inline]
pub fn matches(&self, other: &Self) -> bool {
self == other
}
}
impl From<Declaration> for ValueShape {
fn from(declaration: Declaration) -> Self {
Self {
kind: declaration.kind,
item_kind: declaration.item_kind,
key_kind: declaration.key_kind,
class: declaration
.class
.filter(|_| declaration.kind == Kind::Embedded),
}
}
}
impl fmt::Display for ValueShape {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.kind)?;
match (self.key_kind, self.item_kind) {
(Some(key), Some(item)) => write!(f, "[{key:?}, {item:?}]")?,
(None, Some(item)) => write!(f, "[{item:?}]")?,
_ => {}
}
match self.class {
Some(class) => write!(f, " {class:08x}"),
None => Ok(()),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("{kind} (segment {segment})")]
pub struct ResolveError {
segment: usize,
kind: ResolveErrorKind,
}
impl ResolveError {
pub(crate) fn new(segment: usize, kind: ResolveErrorKind) -> Self {
Self { segment, kind }
}
#[must_use]
#[inline]
pub fn segment(&self) -> usize {
self.segment
}
#[must_use]
#[inline]
pub fn kind(&self) -> ResolveErrorKind {
self.kind
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum ResolveErrorKind {
#[error("no object {0:08x}")]
MissingObject(BinHash),
#[error("no property {0:08x}")]
MissingProperty(BinHash),
#[error("the pointer is null")]
NullPointer,
#[error("cannot descend into a {0:?}")]
CannotDescend(Kind),
#[error("a {0:?} cannot be subscripted that way")]
NotIndexable(Kind),
#[error("index {index} is out of range, the length is {len}")]
IndexOutOfRange {
index: u32,
len: usize,
},
#[error("the key does not convert to {0:?}")]
InvalidKey(Kind),
#[error("no entry has that key")]
KeyNotFound,
#[error("a path into an object starts with a field")]
FieldExpected,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum PatchError {
#[error(transparent)]
Resolve(#[from] ResolveError),
#[error("type mismatch: the property is {expected}, the patch carries {found}")]
TypeMismatch {
expected: ValueShape,
found: ValueShape,
},
}
#[derive(Debug, Clone, Copy)]
enum Slot {
Item(usize),
OptionValue,
Entry(usize),
}
const MISSING: &str = "a Slot only ever names a slot that exists";
type Properties = IndexMap<BinHash, PropertyValueEnum>;
type Landing<'a, 'p> = (&'a Properties, Segment<'p>, usize);
type LandingMut<'a, 'p> = (&'a mut Properties, Segment<'p>, usize);
fn descend_check(value: &PropertyValueEnum) -> Result<(), ResolveErrorKind> {
use PropertyValueEnum as V;
match value {
V::Struct(pointer) if *pointer.class_hash == 0 => Err(ResolveErrorKind::NullPointer),
V::Struct(_) | V::Embedded(_) => Ok(()),
other => Err(ResolveErrorKind::CannotDescend(other.kind())),
}
}
fn properties_of(value: &PropertyValueEnum) -> Option<&Properties> {
use PropertyValueEnum as V;
match value {
V::Struct(pointer) if *pointer.class_hash == 0 => None,
V::Struct(inner) | V::Embedded(values::Embedded(inner)) => Some(&inner.properties),
_ => None,
}
}
fn properties_of_mut(value: &mut PropertyValueEnum) -> Option<&mut Properties> {
use PropertyValueEnum as V;
match value {
V::Struct(pointer) if *pointer.class_hash == 0 => None,
V::Struct(inner) | V::Embedded(values::Embedded(inner)) => Some(&mut inner.properties),
_ => None,
}
}
fn slot_for(
value: &PropertyValueEnum,
subscript: &Subscript<'_>,
) -> Result<Slot, ResolveErrorKind> {
use PropertyValueEnum as V;
match (value, subscript) {
(V::Container(list), Subscript::Index(index))
| (V::UnorderedContainer(values::UnorderedContainer(list)), Subscript::Index(index)) => {
match (*index as usize) < list.len() {
true => Ok(Slot::Item(*index as usize)),
false => Err(ResolveErrorKind::IndexOutOfRange {
index: *index,
len: list.len(),
}),
}
}
(V::Optional(option), Subscript::Index(index)) => match (*index, option.is_some()) {
(0, true) => Ok(Slot::OptionValue),
_ => Err(ResolveErrorKind::IndexOutOfRange {
index: *index,
len: usize::from(option.is_some()),
}),
},
(V::Map(map), Subscript::Key(literal)) => {
let wanted =
key_in(map, literal).ok_or(ResolveErrorKind::InvalidKey(map.key_kind()))?;
map.entries()
.iter()
.position(|(key, _)| key_eq(key, &wanted))
.map(Slot::Entry)
.ok_or(ResolveErrorKind::KeyNotFound)
}
(value, _) => Err(ResolveErrorKind::NotIndexable(value.kind())),
}
}
fn take(value: &PropertyValueEnum, slot: Slot) -> &PropertyValueEnum {
use PropertyValueEnum as V;
match (value, slot) {
(V::Container(list), Slot::Item(index))
| (V::UnorderedContainer(values::UnorderedContainer(list)), Slot::Item(index)) => {
list.get(index).expect(MISSING)
}
(V::Optional(option), Slot::OptionValue) => option.value().expect(MISSING),
(V::Map(map), Slot::Entry(index)) => &map.entries().get(index).expect(MISSING).1,
_ => unreachable!("{MISSING}"),
}
}
fn take_mut(value: &mut PropertyValueEnum, slot: Slot) -> ValueSlot<'_> {
use PropertyValueEnum as V;
match (value, slot) {
(V::Container(list), Slot::Item(index))
| (V::UnorderedContainer(values::UnorderedContainer(list)), Slot::Item(index)) => {
list.slot(index).expect(MISSING)
}
(V::Optional(option), Slot::OptionValue) => option.slot().expect(MISSING),
(V::Map(map), Slot::Entry(index)) => map.slot(index).expect(MISSING),
_ => unreachable!("{MISSING}"),
}
}
fn key_in(map: &values::Map, literal: &KeyLiteral<'_>) -> Option<PropertyValueEnum> {
match (map.key_hash_width(), literal) {
(Some(HashWidth::W8), KeyLiteral::Number(text)) => {
Some(values::Hash::new(HashValue::wide(text.parse().ok()?)).into())
}
(Some(HashWidth::W8), _) => None,
_ => key_as(map.key_kind(), literal),
}
}
fn key_as(kind: Kind, literal: &KeyLiteral<'_>) -> Option<PropertyValueEnum> {
use KeyLiteral as L;
Some(match (kind, literal) {
(Kind::Bool, L::Bool(value)) => values::Bool::new(*value).into(),
(Kind::BitBool, L::Bool(value)) => values::BitBool::new(*value).into(),
(Kind::I8, L::Number(text)) => values::I8::new(text.parse().ok()?).into(),
(Kind::U8, L::Number(text)) => values::U8::new(text.parse().ok()?).into(),
(Kind::I16, L::Number(text)) => values::I16::new(text.parse().ok()?).into(),
(Kind::U16, L::Number(text)) => values::U16::new(text.parse().ok()?).into(),
(Kind::I32, L::Number(text)) => values::I32::new(text.parse().ok()?).into(),
(Kind::U32, L::Number(text)) => values::U32::new(text.parse().ok()?).into(),
(Kind::I64, L::Number(text)) => values::I64::new(text.parse().ok()?).into(),
(Kind::U64, L::Number(text)) => values::U64::new(text.parse().ok()?).into(),
(Kind::F32, L::Number(text)) => values::F32::new(text.parse().ok()?).into(),
(Kind::String, L::String(text)) => values::String::new(text.to_string()).into(),
(Kind::Hash, L::String(text)) => values::Hash::new(BinHash::hash_str(text)).into(),
(Kind::Hash, L::Number(text)) => values::Hash::new(text.parse::<u32>().ok()?).into(),
(Kind::WadChunkLink, L::String(text)) => {
values::WadChunkLink::new(WadHash::hash_str(text)).into()
}
(Kind::WadChunkLink, L::Number(text)) => {
values::WadChunkLink::new(text.parse::<u64>().ok()?).into()
}
_ => return None,
})
}
macro_rules! match_key {
($key:expr, $wanted:expr, [$($variant:ident,)*]) => {
match ($key, $wanted) {
$((PropertyValueEnum::$variant(key), PropertyValueEnum::$variant(wanted)) => {
key.value == wanted.value
})*
_ => false,
}
};
}
fn key_eq(key: &PropertyValueEnum, wanted: &PropertyValueEnum) -> bool {
match_key!(
key,
wanted,
[
Bool,
BitBool,
I8,
U8,
I16,
U16,
I32,
U32,
I64,
U64,
F32,
String,
Hash,
WadChunkLink,
]
)
}
fn step<'a>(
properties: &'a Properties,
segment: &Segment<'_>,
index: usize,
) -> Result<&'a PropertyValueEnum, ResolveError> {
let name_hash = segment.name_hash();
let value = properties
.get(&name_hash)
.ok_or_else(|| ResolveError::new(index, ResolveErrorKind::MissingProperty(name_hash)))?;
match &segment.subscript {
None => Ok(value),
Some(subscript) => {
let slot = slot_for(value, subscript).map_err(|kind| ResolveError::new(index, kind))?;
Ok(take(value, slot))
}
}
}
fn step_mut<'a>(
properties: &'a mut Properties,
segment: &Segment<'_>,
index: usize,
) -> Result<ValueSlot<'a>, ResolveError> {
let name_hash = segment.name_hash();
let value = properties
.get_mut(&name_hash)
.ok_or_else(|| ResolveError::new(index, ResolveErrorKind::MissingProperty(name_hash)))?;
match &segment.subscript {
None => Ok(ValueSlot::free(value)),
Some(subscript) => {
let slot = slot_for(value, subscript).map_err(|kind| ResolveError::new(index, kind))?;
Ok(take_mut(value, slot))
}
}
}
fn locate<'a, 'p>(
properties: &'a Properties,
path: &'p PropertyPath,
) -> Result<Landing<'a, 'p>, ResolveError> {
let mut properties = properties;
let mut segments = path.segments().enumerate().peekable();
loop {
let (index, segment) = segments
.next()
.expect("a PropertyPath has at least one segment");
if segments.peek().is_none() {
return Ok((properties, segment, index));
}
let value = step(properties, &segment, index)?;
descend_check(value).map_err(|kind| ResolveError::new(index + 1, kind))?;
properties = properties_of(value).expect("descend_check accepted this value");
}
}
fn locate_mut<'a, 'p>(
properties: &'a mut Properties,
path: &'p PropertyPath,
) -> Result<LandingMut<'a, 'p>, ResolveError> {
let mut properties = properties;
let mut segments = path.segments().enumerate().peekable();
loop {
let (index, segment) = segments
.next()
.expect("a PropertyPath has at least one segment");
if segments.peek().is_none() {
return Ok((properties, segment, index));
}
let value = step_mut(properties, &segment, index)?.into_inner();
descend_check(value).map_err(|kind| ResolveError::new(index + 1, kind))?;
properties = properties_of_mut(value).expect("descend_check accepted this value");
}
}
pub(crate) fn walk<'a>(
properties: &'a Properties,
path: &PropertyPath,
) -> Result<&'a PropertyValueEnum, ResolveError> {
let (properties, segment, index) = locate(properties, path)?;
step(properties, &segment, index)
}
pub(crate) fn walk_mut<'a>(
properties: &'a mut Properties,
path: &PropertyPath,
) -> Result<ValueSlot<'a>, ResolveError> {
let (properties, segment, index) = locate_mut(properties, path)?;
step_mut(properties, &segment, index)
}
pub(crate) fn patch_in(
properties: &mut Properties,
path: &PropertyPath,
value: PropertyValueEnum,
) -> Result<Option<PropertyValueEnum>, PatchError> {
let (properties, segment, index) = locate_mut(properties, path)?;
let name_hash = segment.name_hash();
if !properties.contains_key(&name_hash) {
return match segment.subscript {
None => {
properties.insert(name_hash, value);
Ok(None)
}
Some(_) => {
Err(ResolveError::new(index, ResolveErrorKind::MissingProperty(name_hash)).into())
}
};
}
replace(step_mut(properties, &segment, index)?, value)
}
fn replace(
mut slot: ValueSlot<'_>,
value: PropertyValueEnum,
) -> Result<Option<PropertyValueEnum>, PatchError> {
let expected = ValueShape::of(slot.get());
let found = ValueShape::of(&value);
if !expected.matches(&found) {
return Err(PatchError::TypeMismatch { expected, found });
}
Ok(Some(slot.set(value).expect(
"the shapes match, so the kinds do and no slot can reject the value",
)))
}
pub(crate) fn probe(
properties: &Properties,
path: &PropertyPath,
found: ValueShape,
) -> Result<bool, PatchError> {
let (properties, segment, index) = locate(properties, path)?;
let name_hash = segment.name_hash();
let Some(value) = properties.get(&name_hash) else {
return match segment.subscript {
None => Ok(true),
Some(_) => {
Err(ResolveError::new(index, ResolveErrorKind::MissingProperty(name_hash)).into())
}
};
};
let leaf = match &segment.subscript {
None => value,
Some(subscript) => {
let slot = slot_for(value, subscript).map_err(|kind| ResolveError::new(index, kind))?;
take(value, slot)
}
};
let expected = ValueShape::of(leaf);
match expected.matches(&found) {
true => Ok(false),
false => Err(PatchError::TypeMismatch { expected, found }),
}
}
impl BinObject {
pub fn resolve(&self, path: &PropertyPath) -> Result<&PropertyValueEnum, ResolveError> {
walk(&self.properties, path)
}
pub fn resolve_mut(&mut self, path: &PropertyPath) -> Result<ValueSlot<'_>, ResolveError> {
walk_mut(&mut self.properties, path)
}
pub fn patch(
&mut self,
path: &PropertyPath,
value: PropertyValueEnum,
) -> Result<Option<PropertyValueEnum>, PatchError> {
patch_in(&mut self.properties, path, value)
}
pub(crate) fn probe(
&self,
path: &PropertyPath,
value: &PropertyValueEnum,
) -> Result<bool, PatchError> {
probe(&self.properties, path, ValueShape::of(value))
}
}
impl values::Struct {
pub fn resolve(&self, path: &PropertyPath) -> Result<&PropertyValueEnum, ResolveError> {
self.null_check()?;
walk(&self.properties, path)
}
pub fn resolve_mut(&mut self, path: &PropertyPath) -> Result<ValueSlot<'_>, ResolveError> {
self.null_check()?;
walk_mut(&mut self.properties, path)
}
pub fn patch(
&mut self,
path: &PropertyPath,
value: PropertyValueEnum,
) -> Result<Option<PropertyValueEnum>, PatchError> {
self.null_check()?;
patch_in(&mut self.properties, path, value)
}
fn null_check(&self) -> Result<(), ResolveError> {
match *self.class_hash {
0 => Err(ResolveError::new(0, ResolveErrorKind::NullPointer)),
_ => Ok(()),
}
}
}
impl values::Embedded {
pub fn resolve(&self, path: &PropertyPath) -> Result<&PropertyValueEnum, ResolveError> {
walk(&self.0.properties, path)
}
pub fn resolve_mut(&mut self, path: &PropertyPath) -> Result<ValueSlot<'_>, ResolveError> {
walk_mut(&mut self.0.properties, path)
}
pub fn patch(
&mut self,
path: &PropertyPath,
value: PropertyValueEnum,
) -> Result<Option<PropertyValueEnum>, PatchError> {
patch_in(&mut self.0.properties, path, value)
}
}
impl PropertyValueEnum {
pub fn resolve(&self, path: &PropertyPath) -> Result<&PropertyValueEnum, ResolveError> {
descend_check(self).map_err(|kind| ResolveError::new(0, kind))?;
walk(
properties_of(self).expect("descend_check accepted this value"),
path,
)
}
pub fn resolve_mut(&mut self, path: &PropertyPath) -> Result<ValueSlot<'_>, ResolveError> {
descend_check(self).map_err(|kind| ResolveError::new(0, kind))?;
walk_mut(
properties_of_mut(self).expect("descend_check accepted this value"),
path,
)
}
}
impl Bin {
pub fn resolve(
&self,
object_hash: impl Into<BinHash>,
path: &PropertyPath,
) -> Result<&PropertyValueEnum, ResolveError> {
self.object(object_hash)?.resolve(path)
}
pub fn resolve_mut(
&mut self,
object_hash: impl Into<BinHash>,
path: &PropertyPath,
) -> Result<ValueSlot<'_>, ResolveError> {
let object_hash = object_hash.into();
self.objects
.get_mut(&object_hash)
.ok_or_else(|| ResolveError::new(0, ResolveErrorKind::MissingObject(object_hash)))?
.resolve_mut(path)
}
pub fn patch(
&mut self,
object_hash: impl Into<BinHash>,
path: &PropertyPath,
value: PropertyValueEnum,
) -> Result<Option<PropertyValueEnum>, PatchError> {
let object_hash = object_hash.into();
let object = self
.objects
.get_mut(&object_hash)
.ok_or_else(|| ResolveError::new(0, ResolveErrorKind::MissingObject(object_hash)))?;
object.patch(path, value)
}
fn object(&self, object_hash: impl Into<BinHash>) -> Result<&BinObject, ResolveError> {
let object_hash = object_hash.into();
self.objects
.get(&object_hash)
.ok_or_else(|| ResolveError::new(0, ResolveErrorKind::MissingObject(object_hash)))
}
}
impl MapKey {
#[must_use]
pub fn from_literal(literal: &KeyLiteral<'_>, kind: Kind) -> Option<Self> {
(&key_as(kind, literal)?).map_key().ok()
}
}
enum Cursor<'a> {
Properties(&'a Properties),
Value(&'a PropertyValueEnum),
}
enum CursorMut<'a> {
Properties(&'a mut Properties),
Value(ValueSlot<'a>),
}
fn field_of(
properties: &Properties,
field: BinHash,
) -> Result<&PropertyValueEnum, ResolveErrorKind> {
properties
.get(&field)
.ok_or(ResolveErrorKind::MissingProperty(field))
}
fn slot_at(value: &PropertyValueEnum, segment: &ValueSegment) -> Result<Slot, ResolveErrorKind> {
use PropertyValueEnum as V;
let out_of_range = |index: usize, len: usize| ResolveErrorKind::IndexOutOfRange {
index: u32::try_from(index).unwrap_or(u32::MAX),
len,
};
match (value, segment) {
(V::Container(list), ValueSegment::Index(index))
| (V::UnorderedContainer(values::UnorderedContainer(list)), ValueSegment::Index(index)) => {
match *index < list.len() {
true => Ok(Slot::Item(*index)),
false => Err(out_of_range(*index, list.len())),
}
}
(V::Optional(option), ValueSegment::Index(index)) => match (*index, option.is_some()) {
(0, true) => Ok(Slot::OptionValue),
_ => Err(out_of_range(*index, usize::from(option.is_some()))),
},
(V::Map(map), ValueSegment::Key(key)) => {
if key.kind() != map.key_kind() {
return Err(ResolveErrorKind::InvalidKey(map.key_kind()));
}
map.entries()
.iter()
.position(|(entry, _)| entry.map_key().is_ok_and(|entry| entry == *key))
.map(Slot::Entry)
.ok_or(ResolveErrorKind::KeyNotFound)
}
(value, _) => Err(ResolveErrorKind::NotIndexable(value.kind())),
}
}
fn walk_at<'a>(
mut cursor: Cursor<'a>,
segments: &[ValueSegment],
offset: usize,
) -> Result<Cursor<'a>, ResolveError> {
for (index, segment) in segments.iter().enumerate() {
let error = |kind| ResolveError::new(offset + index, kind);
let value = match (cursor, segment) {
(Cursor::Properties(properties), ValueSegment::Field(field)) => {
field_of(properties, *field).map_err(error)?
}
(Cursor::Value(value), ValueSegment::Field(field)) => {
descend_check(value).map_err(error)?;
let properties = properties_of(value).expect("descend_check accepted this value");
field_of(properties, *field).map_err(error)?
}
(Cursor::Properties(_), _) => return Err(error(ResolveErrorKind::FieldExpected)),
(Cursor::Value(value), subscript) => {
take(value, slot_at(value, subscript).map_err(error)?)
}
};
cursor = Cursor::Value(value);
}
Ok(cursor)
}
fn walk_at_mut<'a>(
mut cursor: CursorMut<'a>,
segments: &[ValueSegment],
offset: usize,
) -> Result<CursorMut<'a>, ResolveError> {
for (index, segment) in segments.iter().enumerate() {
let error = |kind| ResolveError::new(offset + index, kind);
let slot = match (cursor, segment) {
(CursorMut::Properties(properties), ValueSegment::Field(field)) => ValueSlot::free(
properties
.get_mut(field)
.ok_or_else(|| error(ResolveErrorKind::MissingProperty(*field)))?,
),
(CursorMut::Value(slot), ValueSegment::Field(field)) => {
let value = slot.into_inner();
descend_check(value).map_err(error)?;
let properties =
properties_of_mut(value).expect("descend_check accepted this value");
ValueSlot::free(
properties
.get_mut(field)
.ok_or_else(|| error(ResolveErrorKind::MissingProperty(*field)))?,
)
}
(CursorMut::Properties(_), _) => return Err(error(ResolveErrorKind::FieldExpected)),
(CursorMut::Value(slot), subscript) => {
let value = slot.into_inner();
let at = slot_at(value, subscript).map_err(error)?;
take_mut(value, at)
}
};
cursor = CursorMut::Value(slot);
}
Ok(cursor)
}
fn resolve_in<'a>(
properties: &'a Properties,
path: &ValuePath,
) -> Result<&'a PropertyValueEnum, ResolveError> {
match walk_at(Cursor::Properties(properties), path.segments(), 0)? {
Cursor::Value(value) => Ok(value),
Cursor::Properties(_) => Err(ResolveError::new(0, ResolveErrorKind::FieldExpected)),
}
}
fn resolve_in_mut<'a>(
properties: &'a mut Properties,
path: &ValuePath,
) -> Result<ValueSlot<'a>, ResolveError> {
match walk_at_mut(CursorMut::Properties(properties), path.segments(), 0)? {
CursorMut::Value(slot) => Ok(slot),
CursorMut::Properties(_) => Err(ResolveError::new(0, ResolveErrorKind::FieldExpected)),
}
}
fn patch_at_in(
properties: &mut Properties,
path: &ValuePath,
value: PropertyValueEnum,
) -> Result<Option<PropertyValueEnum>, PatchError> {
let segments = path.segments();
let Some((ValueSegment::Field(field), parents)) = segments.split_last() else {
return replace(resolve_in_mut(properties, path)?, value);
};
let properties = match walk_at_mut(CursorMut::Properties(properties), parents, 0)? {
CursorMut::Properties(properties) => properties,
CursorMut::Value(slot) => {
let value = slot.into_inner();
descend_check(value).map_err(|kind| ResolveError::new(parents.len(), kind))?;
properties_of_mut(value).expect("descend_check accepted this value")
}
};
match properties.get_mut(field) {
None => {
properties.insert(*field, value);
Ok(None)
}
Some(existing) => replace(ValueSlot::free(existing), value),
}
}
impl BinObject {
pub fn resolve_at(&self, path: &ValuePath) -> Result<&PropertyValueEnum, ResolveError> {
resolve_in(&self.properties, path)
}
pub fn resolve_at_mut(&mut self, path: &ValuePath) -> Result<ValueSlot<'_>, ResolveError> {
resolve_in_mut(&mut self.properties, path)
}
pub fn patch_at(
&mut self,
path: &ValuePath,
value: PropertyValueEnum,
) -> Result<Option<PropertyValueEnum>, PatchError> {
patch_at_in(&mut self.properties, path, value)
}
}
impl values::Struct {
pub fn resolve_at(&self, path: &ValuePath) -> Result<&PropertyValueEnum, ResolveError> {
self.null_check()?;
resolve_in(&self.properties, path)
}
pub fn resolve_at_mut(&mut self, path: &ValuePath) -> Result<ValueSlot<'_>, ResolveError> {
self.null_check()?;
resolve_in_mut(&mut self.properties, path)
}
pub fn patch_at(
&mut self,
path: &ValuePath,
value: PropertyValueEnum,
) -> Result<Option<PropertyValueEnum>, PatchError> {
self.null_check()?;
patch_at_in(&mut self.properties, path, value)
}
}
impl values::Embedded {
pub fn resolve_at(&self, path: &ValuePath) -> Result<&PropertyValueEnum, ResolveError> {
resolve_in(&self.0.properties, path)
}
pub fn resolve_at_mut(&mut self, path: &ValuePath) -> Result<ValueSlot<'_>, ResolveError> {
resolve_in_mut(&mut self.0.properties, path)
}
pub fn patch_at(
&mut self,
path: &ValuePath,
value: PropertyValueEnum,
) -> Result<Option<PropertyValueEnum>, PatchError> {
patch_at_in(&mut self.0.properties, path, value)
}
}
impl PropertyValueEnum {
pub fn resolve_at(&self, path: &ValuePath) -> Result<&PropertyValueEnum, ResolveError> {
match walk_at(Cursor::Value(self), path.segments(), 0)? {
Cursor::Value(value) => Ok(value),
Cursor::Properties(_) => unreachable!("a walk from a value never returns properties"),
}
}
pub fn resolve_at_mut(&mut self, path: &ValuePath) -> Result<ValueSlot<'_>, ResolveError> {
match walk_at_mut(CursorMut::Value(ValueSlot::free(self)), path.segments(), 0)? {
CursorMut::Value(slot) => Ok(slot),
CursorMut::Properties(_) => {
unreachable!("a walk from a value never returns properties")
}
}
}
}
impl Bin {
pub fn resolve_at(
&self,
object_hash: impl Into<BinHash>,
path: &ValuePath,
) -> Result<&PropertyValueEnum, ResolveError> {
self.object(object_hash)?.resolve_at(path)
}
pub fn resolve_at_mut(
&mut self,
object_hash: impl Into<BinHash>,
path: &ValuePath,
) -> Result<ValueSlot<'_>, ResolveError> {
self.object_mut(object_hash)?.resolve_at_mut(path)
}
pub fn patch_at(
&mut self,
object_hash: impl Into<BinHash>,
path: &ValuePath,
value: PropertyValueEnum,
) -> Result<Option<PropertyValueEnum>, PatchError> {
self.object_mut(object_hash)?.patch_at(path, value)
}
fn object_mut(
&mut self,
object_hash: impl Into<BinHash>,
) -> Result<&mut BinObject, ResolveError> {
let object_hash = object_hash.into();
self.objects
.get_mut(&object_hash)
.ok_or_else(|| ResolveError::new(0, ResolveErrorKind::MissingObject(object_hash)))
}
}