use std::{any::Any, borrow::Cow, collections::HashMap, hash::Hash, iter::empty, mem::take};
use crate::{
Configuration,
internal::{
ChangeVerb, CongenChange, CongenInternal, Description, ListDescription, ListKey,
ListKeyType, ListValue, ListVerb, NotSupported, VerbError, self_cast,
},
};
#[derive(Debug, Default, Clone)]
pub enum VecChange<T> {
Append(T),
Update(usize, T),
Remove(usize),
Empty,
#[default]
NoChange,
ApplyMany(Vec<VecChange<T>>),
}
#[derive(Debug, Default, Clone)]
pub enum HashMapChange<K, T> {
Append(K, T),
Update(K, T),
Remove(K),
Empty,
#[default]
NoChange,
ApplyMany(Vec<HashMapChange<K, T>>),
}
pub trait HashMapKey: Eq + Hash + Sized + core::fmt::Display + core::fmt::Debug + 'static {
const KEY_TYPE: ListKeyType;
const TYPE_NAME: &'static str;
fn from_list_key(key: ListKey) -> Result<Self, VerbError>;
}
impl HashMapKey for String {
const KEY_TYPE: ListKeyType = ListKeyType::String;
const TYPE_NAME: &'static str = "String";
fn from_list_key(key: ListKey) -> Result<Self, VerbError> {
let ListKey::Stringy(key) = key else {
return Err(VerbError::WrongKeyType);
};
Ok(key)
}
}
macro_rules! impl_map_key {
($int:ty, $key_type:expr, $key_variant:path) => {
impl HashMapKey for $int {
const KEY_TYPE: ListKeyType = $key_type;
const TYPE_NAME: &'static str = stringify!($int);
fn from_list_key(key: ListKey) -> Result<Self, VerbError> {
let $key_variant(key) = key else {
return Err(VerbError::WrongKeyType);
};
key.try_into().map_err(|_| VerbError::WrongKeyType)
}
}
};
}
impl_map_key!(usize, ListKeyType::Unsigned, ListKey::Unsigned);
impl_map_key!(u8, ListKeyType::Unsigned, ListKey::Unsigned);
impl_map_key!(u16, ListKeyType::Unsigned, ListKey::Unsigned);
impl_map_key!(u32, ListKeyType::Unsigned, ListKey::Unsigned);
impl_map_key!(u64, ListKeyType::Unsigned, ListKey::Unsigned);
impl_map_key!(isize, ListKeyType::Signed, ListKey::Signed);
impl_map_key!(i8, ListKeyType::Signed, ListKey::Signed);
impl_map_key!(i16, ListKeyType::Signed, ListKey::Signed);
impl_map_key!(i32, ListKeyType::Signed, ListKey::Signed);
impl_map_key!(i64, ListKeyType::Signed, ListKey::Signed);
impl<T> Configuration for Vec<T>
where
T: Configuration + 'static,
VecChange<T::CongenChange>: CongenChange<Configuration = Vec<T>>,
{
}
impl<T> CongenInternal for Vec<T>
where
T: Configuration + 'static,
VecChange<T::CongenChange>: CongenChange<Configuration = Vec<T>>,
{
type CongenChange = VecChange<T::CongenChange>;
fn apply_change_with_inner_default(
&mut self,
change: Self::CongenChange,
inner_default: Option<fn() -> Box<dyn Any>>,
) {
let assert_key = |index: usize| {
assert!(
index < self.len(),
"User specified index {index} is to large. List is 0 indexed with a lenght of {} and a largest index of {}",
self.len(),
if self.is_empty() {
"NaN".to_string()
} else {
format!("{}", self.len() - 1)
}
);
};
match change {
VecChange::Append(change) => {
let change = match change.unwrap_field() {
Ok(value) => {
self.push(self_cast(value));
return;
}
Err(change) => change,
};
let Some(default) = inner_default else {
panic!(
"called apply_change_with_default on Append to Vec without a default constructor"
);
};
let mut new_value: Box<T> = default()
.downcast()
.expect("Failed to downcast default value for inner type of list");
new_value.as_mut().apply_change(change);
self.push(*new_value);
}
VecChange::Update(index, change) => {
assert_key(index);
let current = &mut self[index];
current.apply_change(change);
}
VecChange::Remove(index) => {
assert_key(index);
self.remove(index);
}
VecChange::Empty => self.clear(),
VecChange::NoChange => {}
VecChange::ApplyMany(changes) => {
for change in changes {
self.apply_change_with_inner_default(change, inner_default);
}
}
}
}
fn description(field_name: &'static str) -> Description {
let inner_desc = Box::new(T::description(""));
ListDescription {
field_name,
type_name: Self::type_name(),
inner_desc,
has_default: false,
key_type: ListKeyType::Unsigned,
append_requires_key: false,
}
.into()
}
fn default() -> Result<Self, NotSupported> {
Ok(Vec::new())
}
fn type_name() -> Cow<'static, str> {
format!("List<{}>", T::type_name()).into()
}
}
impl<K, T> Configuration for HashMap<K, T>
where
K: HashMapKey,
T: Configuration + 'static,
HashMapChange<K, T::CongenChange>: CongenChange<Configuration = HashMap<K, T>>,
{
}
impl<K, T> CongenInternal for HashMap<K, T>
where
K: HashMapKey,
T: Configuration + 'static,
HashMapChange<K, T::CongenChange>: CongenChange<Configuration = HashMap<K, T>>,
{
type CongenChange = HashMapChange<K, T::CongenChange>;
fn apply_change_with_inner_default(
&mut self,
change: Self::CongenChange,
inner_default: Option<fn() -> Box<dyn Any>>,
) {
let assert_missing_key = |key: &K| {
assert!(
!self.contains_key(key),
"User specified key '{key}' already exists in map"
);
};
let assert_existing_key = |key: &K| {
assert!(
self.contains_key(key),
"User specified key '{key}' does not exist in map"
);
};
match change {
HashMapChange::Append(key, change) => {
assert_missing_key(&key);
let change = match change.unwrap_field() {
Ok(value) => {
self.insert(key, self_cast(value));
return;
}
Err(change) => change,
};
let Some(default) = inner_default else {
panic!(
"called apply_change_with_default on Append to HashMap without a default constructor"
);
};
let mut new_value: Box<T> = default()
.downcast()
.expect("Failed to downcast default value for inner type of map");
new_value.as_mut().apply_change(change);
self.insert(key, *new_value);
}
HashMapChange::Update(key, change) => {
assert_existing_key(&key);
let current = self
.get_mut(&key)
.expect("checked existence of key before updating");
current.apply_change(change);
}
HashMapChange::Remove(key) => {
assert_existing_key(&key);
self.remove(&key);
}
HashMapChange::Empty => self.clear(),
HashMapChange::NoChange => {}
HashMapChange::ApplyMany(changes) => {
for change in changes {
self.apply_change_with_inner_default(change, inner_default);
}
}
}
}
fn description(field_name: &'static str) -> Description {
let inner_desc = Box::new(T::description(""));
ListDescription {
field_name,
type_name: Self::type_name(),
inner_desc,
has_default: false,
key_type: K::KEY_TYPE,
append_requires_key: true,
}
.into()
}
fn default() -> Result<Self, NotSupported> {
Ok(HashMap::new())
}
fn type_name() -> Cow<'static, str> {
format!("Map<{}, {}>", K::TYPE_NAME, T::type_name()).into()
}
}
impl<C> CongenChange for VecChange<C>
where
C: CongenChange + 'static,
C::Configuration: 'static,
{
type Configuration = Vec<C::Configuration>;
fn empty() -> Self {
VecChange::NoChange
}
fn apply_change(&mut self, change: Self) {
if matches!(change, VecChange::NoChange) {
return;
}
if matches!(change, VecChange::Empty) {
*self = VecChange::Empty;
return;
}
let mut changes = match self {
VecChange::NoChange => {
*self = change;
return;
}
VecChange::ApplyMany(changes) => take(changes),
_ => {
let current = take(self);
vec![current]
}
};
changes.push(change);
*self = VecChange::ApplyMany(changes);
}
fn from_path_and_verb<'a, P>(mut path: P, verb: ChangeVerb) -> Result<Self, VerbError>
where
P: Iterator<Item = &'a str>,
{
assert!(path.next().is_none(), "field path should end at List<T>");
let inner_desc = C::Configuration::description("");
match verb {
ChangeVerb::Set(_)
| ChangeVerb::SetFlag
| ChangeVerb::Unset
| ChangeVerb::UseDefault
| ChangeVerb::SetAny(_) => Err(VerbError::UnsupportedVerb(verb)),
ChangeVerb::List(list) => match inner_desc {
Description::Field(_) => Self::inner_field_from_verb(list),
Description::Composit(_) | Description::List(_) => Self::complex_from_verb(list),
},
}
}
}
impl<C> VecChange<C>
where
C: CongenChange + 'static,
C::Configuration: 'static,
{
fn complex_from_verb(list_verb: ListVerb) -> Result<Self, VerbError> {
match list_verb {
ListVerb::Append {
key: None,
new_value: ListValue::WithPath(path, verb),
} => {
let inner_change = C::from_path_and_verb(path.iter().map(|p| p.as_str()), *verb)?;
Ok(VecChange::Append(inner_change))
}
ListVerb::Append { key: Some(_), .. } => Err(VerbError::WrongKeyType),
ListVerb::Append { .. } => Err(VerbError::WrongListValueType),
ListVerb::Update {
key,
updated_value: ListValue::WithPath(path, verb),
} => {
let inner_change = C::from_path_and_verb(path.iter().map(|p| p.as_str()), *verb)?;
Ok(VecChange::Update(usize::from_list_key(key)?, inner_change))
}
ListVerb::Update { .. } => Err(VerbError::WrongListValueType),
ListVerb::Remove { key } => Ok(VecChange::Remove(usize::from_list_key(key)?)),
ListVerb::Empty => Ok(VecChange::Empty),
}
}
fn inner_field_from_verb(verb: ListVerb) -> Result<Self, VerbError> {
match verb {
ListVerb::Append {
key: None,
new_value: ListValue::Value(new_value),
} => Ok(VecChange::Append(C::from_path_and_verb(
empty(),
ChangeVerb::Set(new_value),
)?)),
ListVerb::Append { key: Some(_), .. } => Err(VerbError::WrongKeyType),
ListVerb::Append { .. } => Err(VerbError::WrongListValueType),
ListVerb::Update {
key,
updated_value: ListValue::Value(updated_value),
} => Ok(VecChange::Update(
usize::from_list_key(key)?,
C::from_path_and_verb(empty(), ChangeVerb::Set(updated_value))?,
)),
ListVerb::Update { .. } => Err(VerbError::WrongListValueType),
ListVerb::Remove { key } => Ok(VecChange::Remove(usize::from_list_key(key)?)),
ListVerb::Empty => Ok(VecChange::Empty),
}
}
}
impl<K, C> CongenChange for HashMapChange<K, C>
where
K: HashMapKey,
C: CongenChange + 'static,
C::Configuration: 'static,
{
type Configuration = HashMap<K, C::Configuration>;
fn empty() -> Self {
HashMapChange::NoChange
}
fn apply_change(&mut self, change: Self) {
if matches!(change, HashMapChange::NoChange) {
return;
}
if matches!(change, HashMapChange::Empty) {
*self = HashMapChange::Empty;
return;
}
let mut changes = match self {
HashMapChange::NoChange => {
*self = change;
return;
}
HashMapChange::ApplyMany(changes) => take(changes),
_ => {
let current = take(self);
vec![current]
}
};
changes.push(change);
*self = HashMapChange::ApplyMany(changes);
}
fn from_path_and_verb<'a, P>(mut path: P, verb: ChangeVerb) -> Result<Self, VerbError>
where
P: Iterator<Item = &'a str>,
{
assert!(
path.next().is_none(),
"field path should end at HashMap<K, T>"
);
let inner_desc = C::Configuration::description("");
match verb {
ChangeVerb::Set(_)
| ChangeVerb::SetFlag
| ChangeVerb::Unset
| ChangeVerb::UseDefault
| ChangeVerb::SetAny(_) => Err(VerbError::UnsupportedVerb(verb)),
ChangeVerb::List(list) => match inner_desc {
Description::Field(_) => Self::inner_field_from_verb(list),
Description::Composit(_) | Description::List(_) => Self::complex_from_verb(list),
},
}
}
}
impl<K, C> HashMapChange<K, C>
where
K: HashMapKey,
C: CongenChange + 'static,
C::Configuration: 'static,
{
fn complex_from_verb(list_verb: ListVerb) -> Result<Self, VerbError> {
match list_verb {
ListVerb::Append {
key: Some(key),
new_value: ListValue::WithPath(path, verb),
} => {
let key = K::from_list_key(key)?;
let inner_change = C::from_path_and_verb(path.iter().map(|p| p.as_str()), *verb)?;
Ok(HashMapChange::Append(key, inner_change))
}
ListVerb::Append { key: None, .. } => Err(VerbError::WrongKeyType),
ListVerb::Append { .. } => Err(VerbError::WrongListValueType),
ListVerb::Update {
key,
updated_value: ListValue::WithPath(path, verb),
} => {
let key = K::from_list_key(key)?;
let inner_change = C::from_path_and_verb(path.iter().map(|p| p.as_str()), *verb)?;
Ok(HashMapChange::Update(key, inner_change))
}
ListVerb::Update { .. } => Err(VerbError::WrongListValueType),
ListVerb::Remove { key } => Ok(HashMapChange::Remove(K::from_list_key(key)?)),
ListVerb::Empty => Ok(HashMapChange::Empty),
}
}
fn inner_field_from_verb(verb: ListVerb) -> Result<Self, VerbError> {
match verb {
ListVerb::Append {
key: Some(key),
new_value: ListValue::Value(new_value),
} => Ok(HashMapChange::Append(
K::from_list_key(key)?,
C::from_path_and_verb(empty(), ChangeVerb::Set(new_value))?,
)),
ListVerb::Append { key: None, .. } => Err(VerbError::WrongKeyType),
ListVerb::Append { .. } => Err(VerbError::WrongListValueType),
ListVerb::Update {
key,
updated_value: ListValue::Value(updated_value),
} => Ok(HashMapChange::Update(
K::from_list_key(key)?,
C::from_path_and_verb(empty(), ChangeVerb::Set(updated_value))?,
)),
ListVerb::Update { .. } => Err(VerbError::WrongListValueType),
ListVerb::Remove { key } => Ok(HashMapChange::Remove(K::from_list_key(key)?)),
ListVerb::Empty => Ok(HashMapChange::Empty),
}
}
}