Expand description
A service-shaped Grab (food delivery) client over grabcli’s library crates, for a server or a bot that keeps many people’s Grab accounts.
grabcli’s grab binary keeps one account in files; a service keeps many,
in its own storage. This crate is the part such a service needs that is
not specific to it:
api: plain, serde-able types (Merchant,Menu,Cart,Quote,Placed,Tracked), the per-accountGrabClient, theLoginDriverand theGrabBackendthat makes both, andmoney. A session is opaque here (grabcli’s serdeSessionas JSON), read and written only through aSessionSlotthe service implements over its storage.real: the backend overgrabcli-coreandgrabfood.fake: a backend with no network, for tests and local development.login: many phone logins at once, each on its own thread, with a per-account send budget; keyed by plain strings (apersonwho started it, anorg: the account it connects).text: the answers as plain text (for a chat reply or a model).
Nothing here reads files, environment variables or the network on its
own, and nothing is printed: diagnostics go to an optional Log hook.
Phone numbers and codes are held in Secrets and never logged.
use std::sync::Arc;
use grabcli_service::{CartOp, GrabBackend, MemorySlot, fake::Fake, text};
let rt = tokio::runtime::Runtime::new()?;
let backend = Fake::default();
let slot = Arc::new(MemorySlot::new("acct-1", Some(Fake::session("Sample"))));
let client = backend.client(slot);
rt.block_on(async {
let found = client.search("ramen", 5).await?;
println!("{}", text::merchants(&found));
let menu = client.menu(&found[0].id).await?;
client
.cart(&CartOp::Add {
merchant_id: menu.merchant.id.clone(),
item_id: menu.items[0].id.clone(),
quantity: 1,
note: None,
})
.await?;
println!("{}", text::quote(&client.quote().await?));
Ok::<_, grabcli_service::GrabError>(())
})?;Placing a real order still needs grabcli’s own gate: allow_ordering in
the grabcli_core::Config given to real::Real::new, and a quote id
that matches a fresh re-quote. Never retry GrabClient::place_order.
Re-exports§
pub use self::api::Cart;pub use self::api::CartOp;pub use self::api::Fee;pub use self::api::GrabBackend;pub use self::api::GrabClient;pub use self::api::GrabError;pub use self::api::GrabResult;pub use self::api::Health;pub use self::api::Line;pub use self::api::LoginDriver;pub use self::api::Menu;pub use self::api::MenuItem;pub use self::api::Merchant;pub use self::api::Op;pub use self::api::Placed;pub use self::api::Quote;pub use self::api::SessionSlot;pub use self::api::Step;pub use self::api::Tracked;pub use self::api::money;pub use self::login::LoggedIn;pub use self::login::LoginError;pub use self::login::Logins;pub use self::login::OnDone;pub use self::login::Snapshot;pub use self::login::Timing;pub use self::login::View;
Modules§
- api
- What a service asks of Grab: the per-account client (
GrabClient: search, menu, cart, quote, order, tracking, the refresh and the liveness check), the login driver (LoginDriver) and the backend that makes both (GrabBackend: grabcli’s library, or the fake). The session itself is opaque here (grabcli’s serdeSessionas JSON), read and written only through aSessionSlot: wherever the service keeps it. - fake
- A Grab with no network, for tests and local development. Nothing here reaches Grab; every merchant, item and order is made up.
- login
- Logins in progress: one driver thread per login, one browser step at a time (start with the phone → a code or
the app’s approval → done). The browser polls
Logins::get; a login nobody polled forTiming::abandon_msis cancelled, as is one the person cancels. A login’s id is minted here and bound to the person and the org that started it: another’s is “not found”. - real
- grabcli’s library as the
GrabBackend:grabfoodfor the client,grabcli_core::login::LoginDriverfor the login. - text
- The client’s answers as plain text (Markdown-ish: ids in backticks), for a chat reply or a language model.
Structs§
- Memory
Slot - A
SessionSlotin memory (tests, examples, a single-process tool). ItsDebugshows only the account id and whether a session and a cart are kept. - Secret
- A secret string (a phone number, a login code):
Debugnever shows it.
Type Aliases§
- Log
- A diagnostics hook (a login abandoned, a cart that couldn’t be emptied): lines without secrets, for the embedder’s log. None: dropped.