Compare commits

..

14 Commits

4 changed files with 336 additions and 17 deletions

View File

@ -3,7 +3,7 @@
const dialog = { const dialog = {
commands: { commands: {
start: { start: {
buttons: "start_buttons", // default is `null` buttons: start_buttons, // default is `null`
state: "start" state: "start"
}, },
cancel: { cancel: {
@ -20,14 +20,17 @@ const dialog = {
stateful_msg_handlers: { stateful_msg_handlers: {
start: {}, // everything is by default, so just send message `start` start: {}, // everything is by default, so just send message `start`
enter_name: { enter_name: {
handler: "enter_name", // name of the handler function. This field has a // name of the handler function. This field has a side effect:
// side effect: when is set, no automatic sending // when is set, no automatic sending of message, should be sent
// of message, should be sent manually in handler // manually in handler
handler: enter_name,
state: "none" state: "none"
}, },
}, },
} }
function enter_name() {}
const fmt = (number) => number.toString().padStart(2, '0'); const fmt = (number) => number.toString().padStart(2, '0');
const formatDate = (date) => { const formatDate = (date) => {

View File

@ -1,8 +1,10 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::utils::parcelable::{ParcelType, Parcelable, ParcelableError, ParcelableResult};
use itertools::Itertools; use itertools::Itertools;
use quickjs_rusty::serde::from_js; use quickjs_rusty::serde::from_js;
use quickjs_rusty::utils::create_null; use quickjs_rusty::utils::create_empty_object;
use quickjs_rusty::utils::create_string;
use quickjs_rusty::Context; use quickjs_rusty::Context;
use quickjs_rusty::ContextError; use quickjs_rusty::ContextError;
use quickjs_rusty::ExecutionError; use quickjs_rusty::ExecutionError;
@ -22,18 +24,99 @@ pub enum ScriptError {
SerdeError(#[from] quickjs_rusty::serde::Error), SerdeError(#[from] quickjs_rusty::serde::Error),
#[error("error value: {0:?}")] #[error("error value: {0:?}")]
ValueError(#[from] ValueError), ValueError(#[from] ValueError),
#[error("error bot function execution: {0:?}")]
BotFunctionError(String),
} }
pub type ScriptResult<T> = Result<T, ScriptError>; pub type ScriptResult<T> = Result<T, ScriptError>;
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BotFunction(String); // temporal workaround pub struct BotFunction {
func: FunctionMarker,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum FunctionMarker {
/// serde is not able to (de)serialize this, so ignore it and fill
/// in runtime with injection in DeserializeJS
#[serde(skip)]
Function(JsFunction),
StrTemplate(String),
}
impl FunctionMarker {
pub fn as_str_template(&self) -> Option<&String> {
if let Self::StrTemplate(v) = self {
Some(v)
} else {
None
}
}
pub fn as_function(&self) -> Option<&JsFunction> {
if let Self::Function(v) = self {
Some(v)
} else {
None
}
}
pub fn set_js_function(&mut self, f: JsFunction) {
*self = Self::Function(f)
}
}
impl Parcelable<Self> for BotFunction {
fn get_field(
&mut self,
_name: &str,
) -> crate::utils::parcelable::ParcelableResult<ParcelType<Self>> {
todo!()
}
fn resolve(&mut self) -> ParcelableResult<ParcelType<Self>>
where
Self: Sized + 'static,
{
Ok(ParcelType::Function(self))
}
}
impl BotFunction { impl BotFunction {
pub fn call_context(&self, runner: &Runner) -> ScriptResult<JsValue> { pub fn by_name(name: String) -> Self {
let func_name = &self.0; Self {
func: FunctionMarker::StrTemplate(name),
}
}
runner.run_script(&format!("{func_name}()")) pub fn call_context(&self, runner: &Runner) -> ScriptResult<JsValue> {
match &self.func {
FunctionMarker::Function(f) => {
let val = f.call(Default::default())?;
Ok(val)
}
FunctionMarker::StrTemplate(func_name) => runner.run_script(&format!("{func_name}()")),
}
}
pub fn call(&self) -> ScriptResult<JsValue> {
self.call_args(Default::default())
}
pub fn call_args(&self, args: Vec<JsValue>) -> ScriptResult<JsValue> {
if let FunctionMarker::Function(f) = &self.func {
let val = f.call(args)?;
Ok(val)
} else {
Err(ScriptError::BotFunctionError(
"Js Function is not defined".to_string(),
))
}
}
pub fn set_js_function(&mut self, f: JsFunction) {
self.func.set_js_function(f);
} }
} }
@ -61,14 +144,28 @@ impl DeserializerJS {
} }
} }
pub fn deserialize_js<'a, T: Deserialize<'a>>(value: &'a JsValue) -> ScriptResult<T> { pub fn deserialize_js<'a, T: Deserialize<'a> + Parcelable<BotFunction> + 'static>(
value: &'a JsValue,
) -> ScriptResult<T> {
let mut s = Self::new(); let mut s = Self::new();
s.inject_templates(value, "".to_string())?; s.inject_templates(value, "".to_string())?;
let res = value.js_into()?; let mut res = value.js_into()?;
// val.map_functions(s.fn_map); for (k, jsf) in s.fn_map {
let item: ParcelType<'_, BotFunction> =
match Parcelable::<BotFunction>::get_nested(&mut res, &k) {
Ok(item) => item,
Err(err) => {
log::error!("Failed to inject original functions to structs, error: {err}");
continue;
}
};
if let ParcelType::Function(f) = item {
f.set_js_function(jsf);
}
}
Ok(res) Ok(res)
} }
@ -102,7 +199,24 @@ impl DeserializerJS {
Err(err) => return Err(ScriptError::ValueError(err)), Err(err) => return Err(ScriptError::ValueError(err)),
}; };
let res = match self.inject_templates(&p, path.clone() + &k)? { let res = match self.inject_templates(&p, path.clone() + &k)? {
Some(_) => o.set_property(&k, JsValue::new(o.context(), create_null())), Some(_) => {
let fo = JsValue::new(
o.context(),
create_empty_object(o.context()).expect("couldn't create object"),
)
.try_into_object()
.expect("the object created was not an object :/");
fo.set_property(
"func",
JsValue::new(
o.context(),
create_string(o.context(), "somefunc")
.expect("couldn't create string"),
),
)
.expect("wasn't able to set property on object :/");
o.set_property(&k, fo.into_value())
}
None => Ok(()), None => Ok(()),
}; };
match res { match res {
@ -141,6 +255,21 @@ pub enum KeyboardDefinition {
Function(BotFunction), Function(BotFunction),
} }
impl Parcelable<BotFunction> for KeyboardDefinition {
fn get_field(&mut self, _name: &str) -> ParcelableResult<ParcelType<BotFunction>> {
todo!()
}
fn resolve(&mut self) -> ParcelableResult<ParcelType<BotFunction>>
where
Self: Sized + 'static,
{
match self {
KeyboardDefinition::Rows(rows) => Ok(rows.resolve()?),
KeyboardDefinition::Function(f) => Ok(f.resolve()?),
}
}
}
impl ResolveValue for KeyboardDefinition { impl ResolveValue for KeyboardDefinition {
type Value = Vec<<RowDefinition as ResolveValue>::Value>; type Value = Vec<<RowDefinition as ResolveValue>::Value>;
@ -148,7 +277,7 @@ impl ResolveValue for KeyboardDefinition {
match self { match self {
KeyboardDefinition::Rows(rows) => rows.into_iter().map(|r| r.resolve(runner)).collect(), KeyboardDefinition::Rows(rows) => rows.into_iter().map(|r| r.resolve(runner)).collect(),
KeyboardDefinition::Function(f) => { KeyboardDefinition::Function(f) => {
Self::resolve(f.call_context(runner)?.js_into()?, runner) <Self as ResolveValue>::resolve(f.call_context(runner)?.js_into()?, runner)
} }
} }
} }
@ -161,6 +290,21 @@ pub enum RowDefinition {
Function(BotFunction), Function(BotFunction),
} }
impl Parcelable<BotFunction> for RowDefinition {
fn get_field(&mut self, _name: &str) -> ParcelableResult<ParcelType<BotFunction>> {
todo!()
}
fn resolve(&mut self) -> ParcelableResult<ParcelType<BotFunction>>
where
Self: Sized + 'static,
{
match self {
Self::Buttons(buttons) => Ok(buttons.resolve()?),
Self::Function(f) => Ok(f.resolve()?),
}
}
}
impl ResolveValue for RowDefinition { impl ResolveValue for RowDefinition {
type Value = Vec<<ButtonDefinition as ResolveValue>::Value>; type Value = Vec<<ButtonDefinition as ResolveValue>::Value>;
@ -169,7 +313,9 @@ impl ResolveValue for RowDefinition {
RowDefinition::Buttons(buttons) => { RowDefinition::Buttons(buttons) => {
buttons.into_iter().map(|b| b.resolve(runner)).collect() buttons.into_iter().map(|b| b.resolve(runner)).collect()
} }
RowDefinition::Function(f) => Self::resolve(f.call_context(runner)?.js_into()?, runner), RowDefinition::Function(f) => {
<Self as ResolveValue>::resolve(f.call_context(runner)?.js_into()?, runner)
}
} }
} }
} }
@ -190,18 +336,40 @@ impl ResolveValue for ButtonDefinition {
ButtonDefinition::Button(button) => Ok(button), ButtonDefinition::Button(button) => Ok(button),
ButtonDefinition::ButtonLiteral(l) => Ok(ButtonRaw::from_literal(l)), ButtonDefinition::ButtonLiteral(l) => Ok(ButtonRaw::from_literal(l)),
ButtonDefinition::Function(f) => { ButtonDefinition::Function(f) => {
Self::resolve(f.call_context(runner)?.js_into()?, runner) <Self as ResolveValue>::resolve(f.call_context(runner)?.js_into()?, runner)
} }
} }
} }
} }
impl Parcelable<BotFunction> for ButtonDefinition {
fn get_field(&mut self, _name: &str) -> ParcelableResult<ParcelType<BotFunction>> {
todo!()
}
fn resolve(&mut self) -> ParcelableResult<ParcelType<BotFunction>>
where
Self: Sized + 'static,
{
match self {
Self::Button(braw) => Ok(braw.resolve()?),
Self::ButtonLiteral(s) => Ok(s.resolve()?),
Self::Function(f) => Ok(f.resolve()?),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ButtonRaw { pub struct ButtonRaw {
name: ButtonName, name: ButtonName,
callback_name: String, callback_name: String,
} }
impl<F> Parcelable<F> for ButtonRaw {
fn get_field(&mut self, _name: &str) -> ParcelableResult<ParcelType<F>> {
todo!()
}
}
impl ButtonRaw { impl ButtonRaw {
pub fn from_literal(literal: String) -> Self { pub fn from_literal(literal: String) -> Self {
ButtonRaw { ButtonRaw {
@ -234,18 +402,54 @@ pub struct BotMessage {
handler: Option<BotFunction>, handler: Option<BotFunction>,
} }
impl Parcelable<BotFunction> for BotMessage {
fn get_field(&mut self, name: &str) -> ParcelableResult<ParcelType<BotFunction>> {
match name {
"buttons" => Ok(self.buttons.resolve()?),
"state" => Ok(self.state.resolve()?),
"handler" => Ok(self.handler.resolve()?),
field => Err(ParcelableError::FieldError(format!(
"tried to get field {field}, but this field does not exists or private"
))),
}
}
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct BotDialog { pub struct BotDialog {
pub commands: HashMap<String, BotMessage>, pub commands: HashMap<String, BotMessage>,
stateful_msg_handlers: HashMap<String, BotMessage>, stateful_msg_handlers: HashMap<String, BotMessage>,
} }
impl Parcelable<BotFunction> for BotDialog {
fn get_field(&mut self, name: &str) -> Result<ParcelType<BotFunction>, ParcelableError> {
match name {
"commands" => Ok(ParcelType::Parcelable(&mut self.commands)),
"stateful_msg_handlers" => Ok(ParcelType::Parcelable(&mut self.stateful_msg_handlers)),
field => Err(ParcelableError::FieldError(format!(
"tried to get field {field}, but this field does not exists or private"
))),
}
}
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct RunnerConfig { pub struct RunnerConfig {
config: BotConfig, config: BotConfig,
pub dialog: BotDialog, pub dialog: BotDialog,
} }
impl Parcelable<BotFunction> for RunnerConfig {
fn get_field(&mut self, name: &str) -> Result<ParcelType<BotFunction>, ParcelableError> {
match name {
"dialog" => Ok(ParcelType::Parcelable(&mut self.dialog)),
field => Err(ParcelableError::FieldError(format!(
"tried to get field {field}, but this field does not exists or private"
))),
}
}
}
pub struct Runner { pub struct Runner {
context: Context, context: Context,
} }
@ -334,7 +538,7 @@ mod tests {
.run_script("function cancel_buttons() {return 'cancelation'}") .run_script("function cancel_buttons() {return 'cancelation'}")
.unwrap(); .unwrap();
let f = BotFunction("cancel_buttons".to_string()); let f = BotFunction::by_name("cancel_buttons".to_string());
let res = f.call_context(&runner).unwrap(); let res = f.call_context(&runner).unwrap();
println!("RES: {res:?}"); println!("RES: {res:?}");

View File

@ -1,3 +1,5 @@
pub mod parcelable;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use teloxide::types::InlineKeyboardButton; use teloxide::types::InlineKeyboardButton;

110
src/utils/parcelable.rs Normal file
View File

@ -0,0 +1,110 @@
use std::collections::HashMap;
pub enum ParcelType<'a, F> {
Function(&'a mut F),
Parcelable(&'a mut dyn Parcelable<F>),
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<T> = Result<T, ParcelableError>;
pub trait Parcelable<F> {
fn get_field(&mut self, name: &str) -> ParcelableResult<ParcelType<F>>;
fn resolve(&mut self) -> ParcelableResult<ParcelType<F>>
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<ParcelType<F>>
where
Self: Sized + 'static,
{
let root = ParcelableResult::Ok(ParcelType::Parcelable(self));
name.split('.')
.fold(root, |s: ParcelableResult<ParcelType<F>>, field| match s? {
ParcelType::Parcelable(p) => p.get_field(field),
_ => Err(ParcelableError::NestError(format!(
"Failed to get field {field}. End of nestment"
))),
})
}
}
impl<F> Parcelable<F> for String {
fn get_field(&mut self, _name: &str) -> ParcelableResult<ParcelType<F>> {
todo!()
}
fn resolve(&mut self) -> ParcelableResult<ParcelType<F>>
where
Self: Sized + 'static,
{
Ok(ParcelType::Other(()))
}
}
impl<F, T: Parcelable<F>> Parcelable<F> for Option<T> {
fn get_field(&mut self, name: &str) -> ParcelableResult<ParcelType<F>> {
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<ParcelType<F>>
where
Self: Sized + 'static,
{
match self {
Some(v) => Ok(v.resolve()?),
None => Err(ParcelableError::ResolveError("Option was None".to_string())),
}
}
}
impl<F, V: Parcelable<F> + 'static> Parcelable<F> for HashMap<String, V> {
fn get_field(&mut self, name: &str) -> ParcelableResult<ParcelType<F>> {
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<F, T: Parcelable<F> + 'static> Parcelable<F> for Vec<T> {
fn get_field(&mut self, name: &str) -> ParcelableResult<ParcelType<F>> {
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)
}
}