From d5f39e4e60a793f375d4cc092e77836a36cfdf79 Mon Sep 17 00:00:00 2001 From: Akulij Date: Wed, 21 May 2025 12:11:05 +0500 Subject: [PATCH] create Parcelable trait with default implementations --- src/utils.rs | 2 + src/utils/parcelable.rs | 110 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 src/utils/parcelable.rs diff --git a/src/utils.rs b/src/utils.rs index 53f66e7..04ff503 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,5 @@ +pub mod parcelable; + use serde::{Deserialize, Serialize}; use teloxide::types::InlineKeyboardButton; diff --git a/src/utils/parcelable.rs b/src/utils/parcelable.rs new file mode 100644 index 0000000..8e5c396 --- /dev/null +++ b/src/utils/parcelable.rs @@ -0,0 +1,110 @@ +use std::collections::HashMap; + +pub enum ParcelType<'a, F> { + Function(&'a mut F), + Parcelable(&'a mut dyn Parcelable), + Other(()), +} + +#[derive(thiserror::Error, Debug)] +pub enum ParcelableError { + #[error("error to get field: {0:?}")] + FieldError(String), + #[error("error when addressing nested element: {0:?}")] + NestError(String), + #[error("error to resolve Parcelable: {0:?}")] + ResolveError(String), +} + +pub type ParcelableResult = Result; + +pub trait Parcelable { + fn get_field(&mut self, name: &str) -> ParcelableResult>; + + fn resolve(&mut self) -> ParcelableResult> + where + Self: Sized + 'static, + { + let root = ParcelableResult::Ok(ParcelType::Parcelable(self)); + root + } + + /// Get nested field by name, which is fields joined by dot + /// for example: passing name "field1.somefield" will be the same + /// as using `struct.field1.somefield`, by dynamically + fn get_nested(&mut self, name: &str) -> ParcelableResult> + where + Self: Sized + 'static, + { + let root = ParcelableResult::Ok(ParcelType::Parcelable(self)); + name.split('.') + .fold(root, |s: ParcelableResult>, field| match s? { + ParcelType::Parcelable(p) => p.get_field(field), + _ => Err(ParcelableError::NestError(format!( + "Failed to get field {field}. End of nestment" + ))), + }) + } +} + +impl Parcelable for String { + fn get_field(&mut self, _name: &str) -> ParcelableResult> { + todo!() + } + + fn resolve(&mut self) -> ParcelableResult> + where + Self: Sized + 'static, + { + Ok(ParcelType::Other(())) + } +} + +impl> Parcelable for Option { + fn get_field(&mut self, name: &str) -> ParcelableResult> { + Err(ParcelableError::FieldError(format!( + "tried to get field {name}, but calls of get_field are not allowed on Option" + ))) + } + + fn resolve(&mut self) -> crate::utils::parcelable::ParcelableResult> + where + Self: Sized + 'static, + { + match self { + Some(v) => Ok(v.resolve()?), + None => Err(ParcelableError::ResolveError("Option was None".to_string())), + } + } +} + +impl + 'static> Parcelable for HashMap { + fn get_field(&mut self, name: &str) -> ParcelableResult> { + match self.get_mut(name) { + Some(v) => Ok(Parcelable::resolve(v)?), + None => Err(ParcelableError::FieldError(format!( + "tried to get value by key {name}, but this key does not exists" + ))), + } + } +} + +impl + 'static> Parcelable for Vec { + fn get_field(&mut self, name: &str) -> ParcelableResult> { + let index: usize = match name.parse() { + Ok(index) => index, + Err(err) => { + return Err(ParcelableError::FieldError(format!( + "Failed to parse field name `{name}` as an array index, err: {err}" + ))) + } + }; + let veclen = self.len(); + let value = match self.get_mut(index) { + Some(value) => value, + None => return Err(ParcelableError::FieldError(format!("Failed to get vec element with index {index}, probably out of bound (vec len: {veclen})"))), + }; + + Parcelable::resolve(value) + } +}