use crate::error::Error;
use crate::loader::PropertyLoader;
use crate::BindPath;
pub const MAX_ARRAY_SIZE: usize = 1024;
const MAX_ARRAY_KEY_SIZE_STR: usize = 4;
pub struct BindContext<T, U> {
pub path: T,
pub loader: U,
}
impl<T, U> BindContext<T, U>
where
T: BindPath,
U: PropertyLoader,
{
pub fn new(path: T, loader: U) -> Self {
Self { path, loader }
}
}
pub trait ConfigBinder<T, U>
where
Self: Sized,
T: BindPath,
U: PropertyLoader,
{
fn bind(&mut self, path: &mut T, loader: &U) -> crate::error::Result<()>;
}
pub trait ConfigInitializer<T, U>: Sized
where
T: BindPath,
U: PropertyLoader,
{
fn initialize(path: &mut T, loader: &U) -> crate::error::Result<Self>;
}
#[cfg(feature = "std")]
impl<T, U> ConfigBinder<T, U> for std::string::String
where
T: BindPath,
U: PropertyLoader,
{
#[cold]
#[inline(never)]
fn bind(&mut self, path: &mut T, loader: &U) -> crate::error::Result<()> {
*self = Self::initialize(path, loader)?;
Ok(())
}
}
#[cfg(feature = "std")]
impl<T, U> ConfigInitializer<T, U> for std::string::String
where
T: BindPath,
U: PropertyLoader,
{
#[cold]
#[inline(never)]
fn initialize(path: &mut T, loader: &U) -> crate::error::Result<Self> {
loader.load_str_value(path.current_path()).map(Into::into)
}
}
#[cfg(not(feature = "std"))]
impl<const N: usize, T, U> ConfigBinder<T, U> for heapless::String<N>
where
T: BindPath,
U: PropertyLoader,
{
#[cold]
#[inline(never)]
fn bind(&mut self, path: &mut T, loader: &U) -> crate::error::Result<()> {
*self = Self::initialize(path, loader)?;
Ok(())
}
}
#[cfg(not(feature = "std"))]
impl<const N: usize, T, U> ConfigInitializer<T, U> for heapless::String<N>
where
T: BindPath,
U: PropertyLoader,
{
#[cold]
#[inline(never)]
fn initialize(path: &mut T, loader: &U) -> crate::error::Result<Self> {
loader.load_str_value(path.current_path()).map(Into::into)
}
}
pub enum ArrayConfigIndicesMode<'a> {
ZeroIndexed,
Custom(&'a [&'a str]),
}
pub struct ArrayConfigBinder<'a, T> {
mode: ArrayConfigIndicesMode<'a>,
items: &'a mut [T],
}
pub struct ArrayConfigInitializer;
impl ArrayConfigInitializer {
#[cold]
#[inline(never)]
pub fn initialize<B, U, T, const N: usize>(
mode: ArrayConfigIndicesMode<'_>,
path: &mut T,
loader: &U,
) -> crate::error::Result<[B; N]>
where
B: ConfigInitializer<T, U>,
T: BindPath,
U: PropertyLoader,
{
assert!(
N <= MAX_ARRAY_SIZE,
"Array size exceeds maximum allowed size of {}",
MAX_ARRAY_SIZE
);
let mut items = heapless::Vec::<B, N>::new();
match mode {
ArrayConfigIndicesMode::ZeroIndexed => {
for index in 0..N {
let key: heapless::String<{ MAX_ARRAY_KEY_SIZE_STR }> =
heapless::String::try_from(index as u32)
.expect("Index too large for heapless::String<4>");
path.push_array_index(key.as_str());
let result = B::initialize(path, loader);
path.pop_array_index();
items.push(result?).unwrap_or_else(|_| unreachable!());
}
}
ArrayConfigIndicesMode::Custom(indices) => {
assert_eq!(
indices.len(),
N,
"Array indices length does not match array size"
);
for index in indices {
path.push_array_index(index);
let result = B::initialize(path, loader);
path.pop_array_index();
items.push(result?).unwrap_or_else(|_| unreachable!());
}
}
}
Ok(items.into_array().unwrap_or_else(|_| unreachable!()))
}
}
impl<'a, T> ArrayConfigBinder<'a, T> {
pub fn new(mode: ArrayConfigIndicesMode<'a>, items: &'a mut [T]) -> Self {
Self { mode, items }
}
}
impl<'a, B, U, T> ConfigBinder<T, U> for ArrayConfigBinder<'a, B>
where
B: ConfigBinder<T, U>,
T: BindPath,
U: PropertyLoader,
{
#[cold]
#[inline(never)]
fn bind(&mut self, path: &mut T, loader: &U) -> crate::error::Result<()> {
use ArrayConfigIndicesMode::*;
assert!(
self.items.len() <= MAX_ARRAY_SIZE,
"Array size exceeds maximum allowed size of {}",
MAX_ARRAY_SIZE
);
let mut result = Err(Error::NotFound);
match self.mode {
ZeroIndexed => {
for (i, item) in self.items.iter_mut().enumerate() {
let key: heapless::String<{ MAX_ARRAY_KEY_SIZE_STR }> =
heapless::String::try_from(i as u32)
.expect("Index too large for heapless::String<4>");
path.push_array_index(key.as_str());
match item.bind(path, loader) {
Ok(_) => {
result = result.or(Ok(()));
}
Err(e) => {
if e != Error::NotFound {
return Err(e);
}
}
}
path.pop_array_index();
}
}
Custom(indices) => {
for (i, index) in indices.iter().enumerate() {
path.push_array_index(index);
match self.items[i].bind(path, loader) {
Ok(_) => {
result = result.or(Ok(()));
}
Err(e) => {
if e != Error::NotFound {
return Err(e);
}
}
}
path.pop_array_index();
}
}
}
result
}
}
pub struct ArrayRefBinder<'a, T> {
array_ref: &'static str,
prefix: Option<&'static str>,
value: &'a mut T,
}
pub struct ArrayRefInitializer;
impl ArrayRefInitializer {
#[cold]
#[inline(never)]
pub fn initialize<B, T, U>(
array_ref: &'static str,
prefix: Option<&'static str>,
path: &mut T,
loader: &U,
) -> crate::error::Result<B>
where
B: ConfigInitializer<T, U>,
T: BindPath,
U: PropertyLoader,
{
let index: crate::str_ty!() = loader.load_str_value(path.current_path())?;
let key = if let Some(prefix) = prefix {
index.strip_prefix(prefix)
} else {
Some(index.as_str())
}
.ok_or(Error::NotFound)?;
let mut ref_path = T::new();
ref_path.push(array_ref);
ref_path.push_array_index(key);
B::initialize(&mut ref_path, loader)
}
}
impl<'a, T> ArrayRefBinder<'a, T> {
pub fn new(array_ref: &'static str, prefix: Option<&'static str>, value: &'a mut T) -> Self {
Self {
array_ref,
prefix,
value,
}
}
}
impl<'a, T, U, B> ConfigBinder<T, U> for ArrayRefBinder<'a, B>
where
B: ConfigBinder<T, U>,
T: BindPath,
U: PropertyLoader,
{
#[cold]
#[inline(never)]
fn bind(&mut self, path: &mut T, loader: &U) -> crate::error::Result<()> {
let index: crate::str_ty!() = loader.load_str_value(path.current_path())?;
let key = if let Some(prefix) = self.prefix {
index.strip_prefix(prefix)
} else {
Some(index.as_str())
};
if let Some(key) = key {
let mut ref_path = T::new();
ref_path.push(self.array_ref);
ref_path.push_array_index(key);
self.value.bind(&mut ref_path, loader)?;
}
Ok(())
}
}
impl<T, U> ConfigBinder<T, U> for bool
where
T: BindPath,
U: PropertyLoader,
{
#[cold]
#[inline(never)]
fn bind(&mut self, path: &mut T, loader: &U) -> crate::error::Result<()> {
*self = Self::initialize(path, loader)?;
Ok(())
}
}
impl<T, U> ConfigInitializer<T, U> for bool
where
T: BindPath,
U: PropertyLoader,
{
#[cold]
#[inline(never)]
fn initialize(path: &mut T, loader: &U) -> crate::error::Result<Self> {
loader.load_bool_value(path.current_path())
}
}
trait Numeric {}
impl Numeric for i32 {}
impl Numeric for u32 {}
impl Numeric for u16 {}
impl Numeric for i16 {}
impl Numeric for i8 {}
impl Numeric for u8 {}
impl<N, T, U> ConfigBinder<T, U> for N
where
N: Numeric + TryFrom<i32>,
T: BindPath,
U: PropertyLoader,
{
#[cold]
#[inline(never)]
fn bind(&mut self, path: &mut T, loader: &U) -> crate::error::Result<()> {
*self = Self::initialize(path, loader)?;
Ok(())
}
}
impl<N, T, U> ConfigInitializer<T, U> for N
where
N: Numeric + TryFrom<i32>,
T: BindPath,
U: PropertyLoader,
{
#[cold]
#[inline(never)]
fn initialize(path: &mut T, loader: &U) -> crate::error::Result<Self> {
loader
.load_number_value(path.current_path())?
.try_into()
.map_err(|_| Error::Overflow)
}
}
impl<T, U, V> ConfigBinder<T, U> for Option<V>
where
T: BindPath,
U: PropertyLoader,
V: ConfigBinder<T, U> + ConfigInitializer<T, U>,
{
#[cold]
#[inline(never)]
fn bind(&mut self, path: &mut T, loader: &U) -> crate::error::Result<()> {
if let Some(value) = self.as_mut() {
return match value.bind(path, loader) {
Ok(()) | Err(Error::NotFound) => Ok(()),
Err(Error::Required) => {
*self = None;
Ok(())
}
Err(e) => Err(e),
};
}
match V::initialize(path, loader) {
Ok(value) => {
*self = Some(value);
Ok(())
}
Err(Error::NotFound | Error::Required) => Ok(()),
Err(e) => Err(e),
}
}
}
impl<T, U, V> ConfigInitializer<T, U> for Option<V>
where
T: BindPath,
U: PropertyLoader,
V: ConfigBinder<T, U> + ConfigInitializer<T, U>,
{
#[cold]
#[inline(never)]
fn initialize(path: &mut T, loader: &U) -> crate::error::Result<Self> {
let mut value = None;
value.bind(path, loader)?;
Ok(value)
}
}