From 862f4ad3fdecbbde4a02081bcc2dda01d4b98e44 Mon Sep 17 00:00:00 2001 From: tiye Date: Wed, 22 Jun 2022 17:24:06 +0800 Subject: [PATCH] rename to dialog namespace; adding docs; release 0.0.13 --- Cargo.lock | 2 +- demo_respo/src/plugins.rs | 131 +++++++++++++----------- respo/Cargo.toml | 2 +- respo/src/{alerts.rs => dialog.rs} | 12 ++- respo/src/{alerts => dialog}/alert.rs | 38 ++++--- respo/src/{alerts => dialog}/confirm.rs | 31 ++++-- respo/src/{alerts => dialog}/modal.rs | 55 +++++----- respo/src/{alerts => dialog}/prompt.rs | 35 +++++-- respo/src/lib.rs | 2 +- 9 files changed, 183 insertions(+), 125 deletions(-) rename respo/src/{alerts.rs => dialog.rs} (91%) rename respo/src/{alerts => dialog}/alert.rs (83%) rename respo/src/{alerts => dialog}/confirm.rs (88%) rename respo/src/{alerts => dialog}/modal.rs (81%) rename respo/src/{alerts => dialog}/prompt.rs (91%) diff --git a/Cargo.lock b/Cargo.lock index 6bd9325..b2cfe75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,7 +113,7 @@ dependencies = [ [[package]] name = "respo" -version = "0.0.12-a5" +version = "0.0.13" dependencies = [ "cirru_parser", "js-sys", diff --git a/demo_respo/src/plugins.rs b/demo_respo/src/plugins.rs index 6357fd1..27d748b 100644 --- a/demo_respo/src/plugins.rs +++ b/demo_respo/src/plugins.rs @@ -1,12 +1,11 @@ use std::fmt::Debug; -use std::rc::Rc; use respo::space; use serde::{Deserialize, Serialize}; use respo::{button, div, span, ui::ui_button, util, DispatchFn, RespoNode, StatesTree}; -use respo::alerts::{ +use respo::dialog::{ AlertOptions, AlertPlugin, AlertPluginInterface, ConfirmOptions, ConfirmPlugin, ConfirmPluginInterface, ModalOptions, ModalPlugin, ModalPluginInterface, ModalRenderer, PromptOptions, PromptPlugin, PromptPluginInterface, PromptValidator, }; @@ -20,14 +19,24 @@ struct TaskState { } pub fn comp_plugins_demo(states: &StatesTree) -> Result, String> { - respo::util::log!("renrender"); + // respo::util::log!("re-render"); let alert_plugin = AlertPlugin::new(states.pick("info"), AlertOptions::default(), |_dispatch: DispatchFn| { respo::util::log!("on read"); Ok(()) - })?; - let alert_plugin = Rc::new(alert_plugin); - let alert_plugin2 = alert_plugin.clone(); + })? + .share_with_ref(); + + let on_alert = { + let alert_plugin = alert_plugin.clone(); + move |e, dispatch: DispatchFn<_>| -> Result<(), String> { + util::log!("click {:?}", e); + + alert_plugin.show(dispatch, None)?; + + Ok(()) + } + }; let confirm_plugin = ConfirmPlugin::new( states.pick("confirm"), @@ -36,35 +45,60 @@ pub fn comp_plugins_demo(states: &StatesTree) -> Result, Str respo::util::log!("on confirm"); Ok(()) }, - )?; + )? + .share_with_ref(); - let confirm_plugin = Rc::new(confirm_plugin); - let confirm_plugin3 = confirm_plugin.clone(); - let confirm_plugin2 = confirm_plugin; + let on_confirm = { + let confirm_plugin = confirm_plugin.clone(); + move |e, dispatch: DispatchFn<_>| -> Result<(), String> { + util::log!("click {:?}", e); - let options = PromptOptions { - validator: Some(PromptValidator::new(|text| { - if text.len() < 3 { + confirm_plugin.show(dispatch, move || { + respo::util::log!("do something on confirm"); Ok(()) - } else { - Err("too long".to_owned()) - } - })), - multilines: true, - ..Default::default() + })?; + + Ok(()) + } }; - let prompt_plugin = Rc::new(PromptPlugin::new( + let prompt_plugin = PromptPlugin::new( states.pick("prompt"), - options, + PromptOptions { + validator: Some(PromptValidator::new(|text| { + if text.len() < 3 { + Ok(()) + } else { + Err("too long".to_owned()) + } + })), + multilines: true, + ..Default::default() + }, |content, _dispatch: DispatchFn| { respo::util::log!("on prompt: {}", content); Ok(()) }, - )?); - let prompt_plugin2 = prompt_plugin.clone(); + )? + .share_with_ref(); - let modal_plugin = Rc::new(ModalPlugin::new( + let on_prompt = { + let prompt_plugin = prompt_plugin.clone(); + move |e, dispatch: DispatchFn<_>| -> Result<(), String> { + util::log!("click {:?}", e); + + prompt_plugin.show(dispatch, move |content| { + respo::util::log!("do something on prompt: {}", content); + Ok(()) + })?; + + Ok(()) + } + }; + + // declare modal + + let modal_plugin = ModalPlugin::new( states.pick("modal"), ModalOptions { render: ModalRenderer::new(|close_modal: _| { @@ -83,45 +117,18 @@ pub fn comp_plugins_demo(states: &StatesTree) -> Result, Str }), ..ModalOptions::default() }, - )?); - let modal_plugin2 = modal_plugin.clone(); - - let on_alert = move |e, dispatch: DispatchFn<_>| -> Result<(), String> { - util::log!("click {:?}", e); + )? + .share_with_ref(); - alert_plugin.show(dispatch, None)?; + let on_modal = { + let modal_plugin = modal_plugin.clone(); + move |e, dispatch: DispatchFn<_>| -> Result<(), String> { + util::log!("click {:?}", e); - Ok(()) - }; - - let on_confirm = move |e, dispatch: DispatchFn<_>| -> Result<(), String> { - util::log!("click {:?}", e); - - confirm_plugin2.show(dispatch, move || { - respo::util::log!("do something on confirm"); - Ok(()) - })?; - - Ok(()) - }; + modal_plugin.show(dispatch)?; - let on_prompt = move |e, dispatch: DispatchFn<_>| -> Result<(), String> { - util::log!("click {:?}", e); - - prompt_plugin2.show(dispatch, move |content| { - respo::util::log!("do something on prompt: {}", content); Ok(()) - })?; - - Ok(()) - }; - - let on_modal = move |e, dispatch: DispatchFn<_>| -> Result<(), String> { - util::log!("click {:?}", e); - - modal_plugin.show(dispatch, None)?; - - Ok(()) + } }; Ok( @@ -143,10 +150,10 @@ pub fn comp_plugins_demo(states: &StatesTree) -> Result, Str button().class(ui_button()).inner_text("Try Modal").on_click(on_modal).to_owned(), ]) .to_owned(), - alert_plugin2.render()?, - confirm_plugin3.render()?, + alert_plugin.render()?, + confirm_plugin.render()?, prompt_plugin.render()?, - modal_plugin2.render()?, + modal_plugin.render()?, ]) .to_owned(), ) diff --git a/respo/Cargo.toml b/respo/Cargo.toml index 706927f..7cea077 100644 --- a/respo/Cargo.toml +++ b/respo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "respo" -version = "0.0.12-a5" +version = "0.0.13" edition = "2021" description = "a tiny virtual DOM library migrated from ClojureScript" license = "Apache-2.0" diff --git a/respo/src/alerts.rs b/respo/src/dialog.rs similarity index 91% rename from respo/src/alerts.rs rename to respo/src/dialog.rs index a1fff22..001be5d 100644 --- a/respo/src/alerts.rs +++ b/respo/src/dialog.rs @@ -1,3 +1,5 @@ +//! module to provide popup dialogs. + mod alert; mod confirm; mod modal; @@ -11,12 +13,12 @@ use web_sys::{Element, HtmlElement, Node}; use crate::{respo, static_styles, RespoEffectType}; use crate::{CssColor, CssOverflow, CssPosition, CssSize, RespoEffectArg, RespoStyle}; -pub(crate) const BUTTON_NAME: &str = "alert-button"; +pub(crate) const BUTTON_NAME: &str = "dialog-button"; -pub use alert::{comp_alert_modal, AlertOptions, AlertPlugin, AlertPluginInterface}; -pub use confirm::{comp_confirm_modal, ConfirmOptions, ConfirmPlugin, ConfirmPluginInterface}; -pub use modal::{comp_modal, ModalOptions, ModalPlugin, ModalPluginInterface, ModalRenderer}; -pub use prompt::{comp_prompt_modal, PromptOptions, PromptPlugin, PromptPluginInterface, PromptValidator}; +pub use alert::{AlertOptions, AlertPlugin, AlertPluginInterface}; +pub use confirm::{ConfirmOptions, ConfirmPlugin, ConfirmPluginInterface}; +pub use modal::{ModalOptions, ModalPlugin, ModalPluginInterface, ModalRenderer}; +pub use prompt::{PromptOptions, PromptPlugin, PromptPluginInterface, PromptValidator}; pub(crate) fn effect_focus(args: Vec, effect_type: RespoEffectType, el: &Node) -> Result<(), String> { let show: bool = args[0].cast_into()?; diff --git a/respo/src/alerts/alert.rs b/respo/src/dialog/alert.rs similarity index 83% rename from respo/src/alerts/alert.rs rename to respo/src/dialog/alert.rs index e2cc49d..198462e 100644 --- a/respo/src/alerts/alert.rs +++ b/respo/src/dialog/alert.rs @@ -5,24 +5,29 @@ use std::rc::Rc; use serde::{Deserialize, Serialize}; -use crate::alerts::{css_backdrop, css_button, css_card}; +use crate::dialog::{css_backdrop, css_button, css_card}; use crate::ui::{ui_button, ui_center, ui_column, ui_fullscreen, ui_global, ui_row_parted}; use crate::{ button, div, space, span, CssLineHeight, CssPosition, DispatchFn, RespoAction, RespoEvent, RespoNode, RespoStyle, StatesTree, }; -use crate::alerts::{effect_fade, effect_focus, BUTTON_NAME}; +use crate::dialog::{effect_fade, effect_focus, BUTTON_NAME}; +/// The options for alert modal. #[derive(Debug, Clone, Default)] pub struct AlertOptions { - backdrop_style: RespoStyle, - card_style: RespoStyle, - text: Option, - button_text: Option, + /// inline style for backdrop + pub backdrop_style: RespoStyle, + /// inline style for card + pub card_style: RespoStyle, + /// message of the alert modal, defaults to `Alert!` + pub text: Option, + /// text on button + pub button_text: Option, } -pub fn comp_alert_modal(options: AlertOptions, show: bool, on_read: U, on_close: V) -> Result, String> +fn comp_alert_modal(options: AlertOptions, show: bool, on_read: U, on_close: V) -> Result, String> where U: Fn(DispatchFn) -> Result<(), String> + 'static, V: Fn(DispatchFn) -> Result<(), String> + 'static, @@ -34,7 +39,7 @@ where Ok( RespoNode::new_component( - "alert-model", + "alert-modal", div() .style(RespoStyle::default().position(CssPosition::Absolute).to_owned()) .children([if show { @@ -96,24 +101,28 @@ where ) } -/// provides the interfaces to component of alert +/// provides the interfaces to component of alert dialog pub trait AlertPluginInterface where T: Debug + Clone + RespoAction, U: Fn(DispatchFn) -> Result<(), String>, { - /// renders UI + /// renders virtual dom for alert modal fn render(&self) -> Result, String> where T: Clone + Debug; - /// to show alert + /// to show alert, second parameter is a message that could overwrite the default message fn show(&self, dispatch: DispatchFn, text: Option) -> Result<(), String>; /// to close alert fn close(&self, dispatch: DispatchFn) -> Result<(), String>; + /// show alert with options, `on_read` is the callback function when read button is clicked fn new(states: StatesTree, options: AlertOptions, on_read: U) -> Result where Self: std::marker::Sized; + + /// return referencial counted alert plugin + fn share_with_ref(&self) -> Rc; } #[derive(Debug, Clone, Default, Serialize, Deserialize)] @@ -122,7 +131,8 @@ struct AlertPluginState { text: Option, } -/// struct for AlertPlugin +/// abstraction for Alert modal, new with `AlertOption`, +/// just displaying a message, you read it, you close it #[derive(Debug, Clone)] pub struct AlertPlugin where @@ -204,4 +214,8 @@ where Ok(instance) } + + fn share_with_ref(&self) -> Rc { + Rc::new(self.clone()) + } } diff --git a/respo/src/alerts/confirm.rs b/respo/src/dialog/confirm.rs similarity index 88% rename from respo/src/alerts/confirm.rs rename to respo/src/dialog/confirm.rs index e006033..0217f91 100644 --- a/respo/src/alerts/confirm.rs +++ b/respo/src/dialog/confirm.rs @@ -8,26 +8,31 @@ use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::Closure; use wasm_bindgen::{JsCast, JsValue}; -use crate::alerts::{css_backdrop, css_button, css_card}; +use crate::dialog::{css_backdrop, css_button, css_card}; use crate::ui::{ui_button, ui_center, ui_column, ui_fullscreen, ui_global, ui_row_parted}; use crate::{ button, div, respo, space, span, CssLineHeight, CssPosition, DispatchFn, RespoAction, RespoEvent, RespoNode, RespoStyle, StatesTree, }; -use crate::alerts::{effect_fade, effect_focus, BUTTON_NAME}; +use crate::dialog::{effect_fade, effect_focus, BUTTON_NAME}; const NEXT_TASK_NAME: &str = "_RESPO_CONFIRM_NEXT_TASK"; +/// options for confirm dialog #[derive(Debug, Clone, Default)] pub struct ConfirmOptions { - backdrop_style: RespoStyle, + /// inline style for backdrop + pub backdrop_style: RespoStyle, + /// inline style for card card_style: RespoStyle, + /// message to display text: Option, + /// text on button button_text: Option, } -pub fn comp_confirm_modal(options: ConfirmOptions, show: bool, on_confirm: U, on_close: V) -> Result, String> +fn comp_confirm_modal(options: ConfirmOptions, show: bool, on_confirm: U, on_close: V) -> Result, String> where U: Fn(DispatchFn) -> Result<(), String> + 'static, V: Fn(DispatchFn) -> Result<(), String> + 'static, @@ -103,7 +108,7 @@ where ) } -/// provides the interfaces to component of alert +/// provides the interfaces to component of confirm dialog pub trait ConfirmPluginInterface where T: Debug + Clone + RespoAction, @@ -113,16 +118,20 @@ where fn render(&self) -> Result, String> where T: Clone + Debug; - /// to show alert + /// to show dialog, second parameter is a callback when confirmed, + /// the callback is implemented dirty, it perform directly after confirmed fn show(&self, dispatch: DispatchFn, next_task: V) -> Result<(), String> where V: Fn() -> Result<(), String> + 'static; - /// to close alert + /// to close dialog fn close(&self, dispatch: DispatchFn) -> Result<(), String>; + /// creates a new instance of confirm plugin, second parameter is a callback when confirmed fn new(states: StatesTree, options: ConfirmOptions, on_confirm: U) -> Result where Self: std::marker::Sized; + + fn share_with_ref(&self) -> Rc; } #[derive(Debug, Clone, Default, Serialize, Deserialize)] @@ -131,7 +140,7 @@ struct ConfirmPluginState { text: Option, } -/// struct for ConfirmPlugin +/// Popup a confirmation dialog, confirm to process next task #[derive(Debug, Clone)] pub struct ConfirmPlugin where @@ -166,7 +175,7 @@ where let d2 = dispatch.clone(); on_confirm(dispatch)?; let window = web_sys::window().expect("window"); - // dirty global variable + // TODO dirty global variable let task = Reflect::get(&window, &JsValue::from_str(NEXT_TASK_NAME)); if let Ok(f) = task { if f.is_function() { @@ -243,4 +252,8 @@ where Ok(instance) } + + fn share_with_ref(&self) -> Rc { + Rc::new(self.clone()) + } } diff --git a/respo/src/alerts/modal.rs b/respo/src/dialog/modal.rs similarity index 81% rename from respo/src/alerts/modal.rs rename to respo/src/dialog/modal.rs index 41a29cb..74b645d 100644 --- a/respo/src/alerts/modal.rs +++ b/respo/src/dialog/modal.rs @@ -5,26 +5,32 @@ use std::rc::Rc; use serde::{Deserialize, Serialize}; -use crate::alerts::{css_backdrop, css_card}; +use crate::dialog::{css_backdrop, css_card}; use crate::ui::{ui_center, ui_column, ui_fullscreen, ui_global}; use crate::{div, space, span, CssLineHeight, CssPosition, DispatchFn, RespoAction, RespoEvent, RespoNode, RespoStyle, StatesTree}; -use crate::alerts::effect_fade; +use crate::dialog::effect_fade; +/// The options for custom modal. #[derive(Debug, Clone, Default)] pub struct ModalOptions where T: Debug + Clone, { + /// inline style for backdrop pub backdrop_style: RespoStyle, + /// inline style for card pub card_style: RespoStyle, + /// title of the modal, defaults to `Modal` pub title: Option, + /// render body pub render: ModalRenderer, } type ModalRendererFn = dyn Fn(Rc) -> Result<(), String>>) -> Result, String>; +/// wraps render function #[derive(Clone)] pub struct ModalRenderer(Rc>) where @@ -67,7 +73,7 @@ where } } -pub fn comp_modal(options: ModalOptions, show: bool, on_close: U) -> Result, String> +fn comp_modal(options: ModalOptions, show: bool, on_close: U) -> Result, String> where U: Fn(DispatchFn) -> Result<(), String> + 'static, T: Clone + Debug, @@ -107,7 +113,10 @@ where .children([div() .class(ui_column()) .children([ - span().inner_text(options.title.unwrap_or_else(|| "Modal".to_owned())).to_owned(), + div() + .class(ui_center()) + .children([span().inner_text(options.title.unwrap_or_else(|| "Modal".to_owned())).to_owned()]) + .to_owned(), space(None, Some(8)), options.render.run(move |dispatch| -> Result<(), String> { let close = close2.clone(); @@ -129,7 +138,7 @@ where ) } -/// provides the interfaces to component of alert +/// provides the interfaces to component of custom modal dialog pub trait ModalPluginInterface where T: Debug + Clone + RespoAction, @@ -138,23 +147,25 @@ where fn render(&self) -> Result, String> where T: Clone + Debug; - /// to show alert - fn show(&self, dispatch: DispatchFn, text: Option) -> Result<(), String>; - /// to close alert + /// to show modal + fn show(&self, dispatch: DispatchFn) -> Result<(), String>; + /// to close modal fn close(&self, dispatch: DispatchFn) -> Result<(), String>; fn new(states: StatesTree, options: ModalOptions) -> Result where Self: std::marker::Sized; + + /// share it with `Rc` + fn share_with_ref(&self) -> Rc; } #[derive(Debug, Clone, Default, Serialize, Deserialize)] struct ModalPluginState { show: bool, - text: Option, } -/// struct for AlertPlugin +/// a modal that you can render you down card body #[derive(Debug, Clone)] pub struct ModalPlugin where @@ -163,7 +174,6 @@ where state: ModalPluginState, options: ModalOptions, /// tracking content to display - text: Option, cursor: Vec, phantom: PhantomData, } @@ -174,30 +184,20 @@ where { fn render(&self) -> Result, String> { let cursor = self.cursor.clone(); - let state = self.state.to_owned(); comp_modal(self.options.to_owned(), self.state.show, move |dispatch: DispatchFn<_>| { - let s = ModalPluginState { - show: false, - text: state.text.to_owned(), - }; + let s = ModalPluginState { show: false }; dispatch.run_state(&cursor, s)?; Ok(()) }) } - fn show(&self, dispatch: DispatchFn, text: Option) -> Result<(), String> { - let s = ModalPluginState { - show: true, - text: text.or_else(|| self.state.text.to_owned()), - }; + fn show(&self, dispatch: DispatchFn) -> Result<(), String> { + let s = ModalPluginState { show: true }; dispatch.run_state(&self.cursor, s)?; Ok(()) } fn close(&self, dispatch: DispatchFn) -> Result<(), String> { - let s = ModalPluginState { - show: false, - text: self.text.clone(), - }; + let s = ModalPluginState { show: false }; dispatch.run_state(&self.cursor, s)?; Ok(()) } @@ -209,11 +209,14 @@ where let instance = Self { state, options, - text: None, cursor, phantom: PhantomData, }; Ok(instance) } + + fn share_with_ref(&self) -> Rc { + Rc::new(self.clone()) + } } diff --git a/respo/src/alerts/prompt.rs b/respo/src/dialog/prompt.rs similarity index 91% rename from respo/src/alerts/prompt.rs rename to respo/src/dialog/prompt.rs index 9607a7f..a3114d0 100644 --- a/respo/src/alerts/prompt.rs +++ b/respo/src/dialog/prompt.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::Closure; use wasm_bindgen::{JsCast, JsValue}; -use crate::alerts::{css_backdrop, css_button, css_card}; +use crate::dialog::{css_backdrop, css_button, css_card}; use crate::ui::{ui_button, ui_center, ui_column, ui_fullscreen, ui_global, ui_input, ui_row_parted, ui_textarea}; use crate::{ @@ -17,22 +17,32 @@ use crate::{ RespoAction, RespoEvent, RespoNode, RespoStyle, StatesTree, }; -use crate::alerts::{effect_fade, BUTTON_NAME}; +use crate::dialog::{effect_fade, BUTTON_NAME}; const NEXT_TASK_NAME: &str = "_RESPO_PROMPT_NEXT_TASK"; +/// options for prompt dialog #[derive(Debug, Clone, Default)] pub struct PromptOptions { + /// inline style for backdrop pub backdrop_style: RespoStyle, + /// inline style for card pub card_style: RespoStyle, + /// hint to display, defaults `input message` pub text: Option, + /// text on button pub button_text: Option, + /// initial value of input pub initial_value: Option, + /// textarea or input pub multilines: bool, + /// inline style for input bix pub input_style: RespoStyle, + /// a validation function to check input pub validator: Option, } +/// wraps validator function #[derive(Clone)] pub struct PromptValidator(Rc Result<(), String>>); @@ -60,7 +70,7 @@ struct InputState { error: Option, } -pub fn comp_prompt_modal( +fn comp_prompt_modal( states: StatesTree, options: PromptOptions, show: bool, @@ -218,7 +228,7 @@ where ) } -/// provides the interfaces to component of alert +/// provides the interfaces to component of prompt dialog pub trait PromptPluginInterface where T: Debug + Clone + RespoAction, @@ -228,16 +238,21 @@ where fn render(&self) -> Result, String> where T: Clone + Debug; - /// to show alert + /// to show prompt dialog, second parameter is the callback task when the dialog is read, + /// the callback is stored in a dirty to provide syntax sugar fn show(&self, dispatch: DispatchFn, next_task: V) -> Result<(), String> where V: Fn(String) -> Result<(), String> + 'static; - /// to close alert + /// to close prompt dialog fn close(&self, dispatch: DispatchFn) -> Result<(), String>; + /// initialize the plugin, second parameter is the callback task when submitted, fn new(states: StatesTree, options: PromptOptions, on_submit: U) -> Result where Self: std::marker::Sized; + + /// shared it in `Rc` + fn share_with_ref(&self) -> Rc; } #[derive(Debug, Clone, Default, Serialize, Deserialize)] @@ -246,7 +261,7 @@ struct PromptPluginState { text: Option, } -/// struct for PromptPlugin +/// a dialog for prompt, request for some input, and submit #[derive(Debug, Clone)] pub struct PromptPlugin where @@ -283,7 +298,7 @@ where let d2 = dispatch.clone(); on_submit(content.to_owned(), dispatch)?; let window = web_sys::window().expect("window"); - // dirty global variable + // TODO dirty global variable let task = Reflect::get(&window, &JsValue::from_str(NEXT_TASK_NAME)); if let Ok(f) = task { if f.is_function() { @@ -363,6 +378,10 @@ where Ok(instance) } + + fn share_with_ref(&self) -> Rc { + Rc::new(self.clone()) + } } static_styles!(css_error, ("$0".to_owned(), RespoStyle::default().color(CssColor::Red))); diff --git a/respo/src/lib.rs b/respo/src/lib.rs index b82d241..61a8d28 100644 --- a/respo/src/lib.rs +++ b/respo/src/lib.rs @@ -23,7 +23,7 @@ mod memof1; mod respo; -pub mod alerts; +pub mod dialog; pub mod ui; pub use crate::respo::*;