-
Notifications
You must be signed in to change notification settings - Fork 2
Design philosophy
We, the two people behind tbot
, want to create a crate which helps to develop
Telegram bots with Rust quickly and easily. Here's our vision of what tbot
should be:
- Easy-to-use:
tbot
should abstract away the underlying parts of the API, so users oftbot
can focus on the logic of their bot without knowing what's under the hood. - Type-safe:
tbot
's API must be designed in such a way that prevents invalid type states and method arguments. - Future-proof:
tbot
's design must easily adapt for the future. - Fast:
tbot
must do as little work as possible, and do it as quickly as possible.
Given this, tbot
's design implements several patterns and design decisions.
One part of tbot
must have only one purpose. For example,
methods::SendChatAction
only does what the sendChatAction
can do:
sending one chat action. It won't send the chat action repeatedly, because
the purpose of the methods
module is to provide plain methods.
In many cases, there are optional arguments to methods. In these cases,
a Builder API must be provided. For example, the sendMessage
methods has
several optional arguments. It's much easier to set optional arguments with
Builder API:
use tbot::{types::parameters::Text, prelude::*};
let bot = tbot::from_env!("BOT_TOKEN");
bot.send_message(CHAT_ID, Text::markdown("`tbot` is amazing!")).call().await;
than if you passed everything to the constructor:
use tbot::{
methods::SendMessage,
types::parameters::ParseMode::Markdown,
prelude::*,
};
let bot = tbot::from_env!("BOT_TOKEN");
bot.send_message(
TOKEN,
CHAT_ID,
"`tbot` is amazing!",
Some(Markdown),
None,
None,
None,
None,
)
.call()
.await;
tbot
must take ownership only when it is essential. For example, Bot
takes ownership of the token because it needs to put the token in an Arc
,
and then passes it around when needed.
tbot
's types must prevent invalid states. For example, consider
the InlineKeyboardButton
type. The docs list all (but one) fields
as optional but they state:
You must use exactly one of the optional fields.
With a bad library, constructing such a type would look like this:
let button = InlineKeyboardButton::new("Rust good")
.callback_data("rust_good");
What if one doesn't call callback_data
or a similar method? What if one calls
callback_data
and login_url
? Most likely, you won't even notice it before
you make a request. How would a good library solve this? One way is this:
let button = inline::Button::new(
"Rust good",
inline::ButtonKind::Callback("rust_good"),
);
You can see that this way it's impossible to pass several types; if you pass none, you get a clear compile-time error.