use core::{
ops::{Deref, DerefMut},
str::FromStr,
};
use alloc::{
string::{String, ToString},
vec::Vec,
};
use crate::{Arc, ConstString, RemappingTarget};
use super::error::Error;
type RemappingEntry = (ConstString, RemappingTarget);
#[derive(Clone, Default)]
#[repr(transparent)]
pub struct RemappingList(Vec<RemappingEntry>);
impl core::fmt::Debug for RemappingList {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Remappings {{ ")?;
write!(f, "{:?}", &self.0)?;
write!(f, " }}")
}
}
impl Deref for RemappingList {
type Target = Vec<RemappingEntry>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for RemappingList {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl RemappingList {
pub fn add(&mut self, key: ConstString, target: ConstString) -> Result<(), Error> {
let remap_to = if target.as_ref() == "{=}" {
(String::from("{") + &key + "}").into()
} else {
target
};
for (original, remapped) in &self.0 {
if original == &key {
return Err(Error::AlreadyRemapped {
name: key,
remapped: remapped.to_string().into(),
});
}
}
let target = RemappingTarget::from_str(&remap_to)?;
self.0.push((key, target));
Ok(())
}
pub(crate) fn add_target(&mut self, entry: RemappingEntry) -> Result<(), Error> {
for (original, _remapped) in &self.0 {
if original == &entry.0 {
return Err(Error::AlreadyRemapped {
name: entry.0,
remapped: "todo!()".into(),
});
}
}
self.0.push(entry);
Ok(())
}
pub fn overwrite(&mut self, key: ConstString, target: ConstString) -> Result<(), Error> {
let remap_to = if target.as_ref() == "{=}" {
(String::from("{") + &key + "}").into()
} else {
target
};
let target = RemappingTarget::from_str(&remap_to)?;
for (original, old_value) in &mut self.0 {
if original == &key {
*old_value = target;
return Ok(());
}
}
self.0.push((key, target));
Ok(())
}
#[must_use]
pub fn find(&self, key: &str) -> RemappingTarget {
for (original, remapped) in &self.0 {
if original.as_ref() == key {
return remapped.clone();
}
}
RemappingTarget::None(key.into())
}
pub fn shrink(&mut self) {
self.0.shrink_to_fit();
}
pub(crate) fn into_inner(self) -> Vec<RemappingEntry> {
self.0
}
pub(crate) fn find_origin(&self, name: &str) -> Option<Arc<str>> {
for (source_name, target) in &self.0 {
match target {
RemappingTarget::BoardPointer(target_name)
| RemappingTarget::LocalPointer(target_name)
| RemappingTarget::RootPointer(target_name) => {
if name == target_name.as_ref() {
return Some(source_name.clone());
}
}
RemappingTarget::StringAssignment(_) | RemappingTarget::None(_) => {
}
}
}
None
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
const fn is_normal<T: Sized + Send + Sync>() {}
#[test]
const fn normal_types() {
is_normal::<RemappingList>();
is_normal::<RemappingEntry>();
}
#[test]
fn find_origin_local_pointer() {
let mut r = RemappingList::default();
r.push(("alias".into(), RemappingTarget::LocalPointer("real".into())));
assert_eq!(r.find_origin("real").unwrap().as_ref(), "alias");
}
#[test]
fn find_origin_root_pointer() {
let mut r = RemappingList::default();
r.push(("alias".into(), RemappingTarget::RootPointer("root_key".into())));
assert_eq!(r.find_origin("root_key").unwrap().as_ref(), "alias");
}
#[test]
fn find_origin_skips_string_assignment() {
let mut r = RemappingList::default();
r.push(("alias".into(), RemappingTarget::StringAssignment("constant".into())));
assert!(r.find_origin("constant").is_none());
}
#[test]
fn find_origin_skips_none() {
let mut r = RemappingList::default();
r.push(("alias".into(), RemappingTarget::None("whatever".into())));
assert!(r.find_origin("whatever").is_none());
}
#[test]
fn find_origin_local_pointer_no_match() {
let mut r = RemappingList::default();
r.push(("alias".into(), RemappingTarget::LocalPointer("real".into())));
assert!(r.find_origin("other").is_none());
}
#[test]
fn add_target_iterates_non_matching_entries() {
let mut list = RemappingList::default();
list.push(("existing".into(), RemappingTarget::BoardPointer("other".into())));
list.add_target(("new_key".into(), RemappingTarget::BoardPointer("target".into())))
.unwrap();
assert_eq!(list.len(), 2);
}
}