Write the docs

This commit is contained in:
Temirkhan Myrzamadi 2020-01-08 17:59:30 +06:00
parent 1547034741
commit 3351838a36
7 changed files with 87 additions and 24 deletions

View file

@ -1,37 +1,47 @@
use crate::types::Update;
use std::{future::Future, pin::Pin};
/// Continue or terminate a user session.
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub enum SessionState<S> {
Continue(S),
pub enum SessionState<Session> {
Continue(Session),
Terminate,
}
pub trait Handler<S> {
/// A handler of a user session and an update.
///
/// ## Returns
/// Returns [`SessionState::Continue(session)`] if it wants to be called again
/// after a new update, or [`SessionState::Terminate`] if not.
///
/// [`SessionState::Continue(session)`]:
/// crate::dispatching::SessionState::Continue
/// [`SessionState::Terminate`]: crate::dispatching::SessionState::Terminate
pub trait Handler<Session> {
#[must_use]
fn handle<'a>(
&'a self,
session: S,
session: Session,
update: Update,
) -> Pin<Box<dyn Future<Output = SessionState<S>> + 'a>>
) -> Pin<Box<dyn Future<Output = SessionState<Session>> + 'a>>
where
S: 'a;
Session: 'a;
}
/// The implementation of `Handler` for `Fn(S, Update) -> Future<Output =
/// SessionState<S>>`.
impl<S, F, Fut> Handler<S> for F
/// The implementation of `Handler` for `Fn(Session, Update) -> Future<Output =
/// SessionState<Session>>`.
impl<Session, F, Fut> Handler<Session> for F
where
F: Fn(S, Update) -> Fut,
Fut: Future<Output = SessionState<S>>,
F: Fn(Session, Update) -> Fut,
Fut: Future<Output = SessionState<Session>>,
{
fn handle<'a>(
&'a self,
session: S,
session: Session,
update: Update,
) -> Pin<Box<dyn Future<Output = Fut::Output> + 'a>>
where
S: 'a,
Session: 'a,
{
Box::pin(async move { self(session, update).await })
}

View file

@ -1,5 +1,12 @@
//! Update dispatching.
/// If an update was handled by a dispatcher or not.
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub enum DispatchResult {
Handled,
Unhandled,
}
pub mod filters;
mod handler;
pub mod private;

View file

@ -1,20 +1,18 @@
use super::storage::{InMemStorage, Storage};
use super::{
super::DispatchResult,
storage::{InMemStorage, Storage},
};
use crate::{
dispatching::{Handler, SessionState},
types::{ChatKind, Update, UpdateKind},
};
/// A dispatcher that dispatches updates from 1-to-1 chats.
pub struct Dispatcher<'a, Session, H> {
storage: Box<dyn Storage<Session> + 'a>,
handler: H,
}
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub enum DispatchResult {
Handled,
Unhandled,
}
#[macro_use]
mod macros {
#[macro_export]
@ -33,6 +31,10 @@ where
Session: Default + 'a,
H: Handler<Session>,
{
/// Creates a dispatcher with the specified `handler` and [`InMemStorage`]
/// (a default storage).
///
/// [`InMemStorage`]: crate::dispatching::private::InMemStorage
pub fn new(handler: H) -> Self {
Self {
storage: Box::new(InMemStorage::default()),
@ -40,6 +42,7 @@ where
}
}
/// Creates a dispatcher with the specified `handler` and `storage`.
pub fn with_storage<Stg>(handler: H, storage: Stg) -> Self
where
Stg: Storage<Session> + 'a,
@ -50,6 +53,16 @@ where
}
}
/// Dispatches a single `update`.
///
/// ## Returns
/// Returns [`DispatchResult::Handled`] if `update` was supplied to a
/// handler, and [`DispatchResult::Unhandled`] if it was an update not
/// from a 1-to-1 chat.
///
/// [`DispatchResult::Handled`]: crate::dispatching::DispatchResult::Handled
/// [`DispatchResult::Unhandled`]:
/// crate::dispatching::DispatchResult::Unhandled
pub async fn dispatch(&mut self, update: Update) -> DispatchResult {
let chat_id = match &update.kind {
UpdateKind::Message(msg) => private_chat_id!(msg),

View file

@ -1,3 +1,13 @@
//! Dispatching updates from 1-to-1 chats.
//!
//! It's fairly simple: you have a session type and a handler that accepts
//! (session, update) and turns a session into the next state. When a new
//! user sends a message to your bot, the dispatcher creates a default session
//! and supplies it to your handler, but when an old user sends a message, your
//! handler gets the saved session with him.
// TODO: examples
mod dispatcher;
mod storage;

View file

@ -3,6 +3,13 @@ use async_trait::async_trait;
use super::Storage;
use std::collections::HashMap;
/// A memory storage based on a hash map. Stores all the sessions directly in
/// RAM.
///
/// ## Note
/// All the sessions will be lost after you restart your bot. If you need to
/// store them somewhere on a drive, you need to implement a storage
/// communicating with a DB.
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct InMemStorage<Session> {
map: HashMap<i64, Session>,

View file

@ -3,13 +3,30 @@ mod in_mem_storage;
use async_trait::async_trait;
pub use in_mem_storage::InMemStorage;
/// A storage of user sessions.
///
/// You can implement this trait for a structure that communicates with a DB and
/// be sure that after you restart your bot, all the sessions won't be lost.
///
/// For a storage based on a simple hash map, see [`InMemStorage`].
///
/// [`InMemStorage`]: crate::dispatching::private::InMemStorage
#[async_trait(?Send)]
#[async_trait]
pub trait Storage<Session> {
/// Removes a session with the specified `chat_id`.
///
/// Returns `None` if there wasn't such a session, `Some(session)` if a
/// `session` was deleted.
async fn remove_session(&mut self, chat_id: i64) -> Option<Session>;
/// Updates a session with the specified `chat_id`.
///
/// Returns `None` if there wasn't such a session, `Some(session)` if a
/// `session` was updated.
async fn update_session(
&mut self,
chat_id: i64,
state: Session,
session: Session,
) -> Option<Session>;
}

View file

@ -6,8 +6,8 @@
//! - [`polling`], which returns a long/short polling listener with your
//! configuration.
//!
//! And then you can extract updates from it and pass them directly to
//! [`Dispatcher::dispatch`].
//! And then you can extract updates from it and pass them directly to a
//! dispatcher.
//!
//! Telegram supports two ways of [getting updates]: [long]/[short] polling and
//! [webhook].
@ -94,7 +94,6 @@
//! [`UpdateListener`]: UpdateListener
//! [`polling_default`]: polling_default
//! [`polling`]: polling
//! [`Dispatcher::dispatch`]: crate::dispatching::Dispatcher::dispatch
//! [`Box::get_updates`]: crate::Bot::get_updates
//! [getting updates]: https://core.telegram.org/bots/api#getting-updates
//! [long]: https://en.wikipedia.org/wiki/Push_technology#Long_polling