Skip to main content

Crate grabcli_service

Crate grabcli_service 

Source
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-account GrabClient, the LoginDriver and the GrabBackend that makes both, and money. A session is opaque here (grabcli’s serde Session as JSON), read and written only through a SessionSlot the service implements over its storage.
  • real: the backend over grabcli-core and grabfood.
  • 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 (a person who started it, an org: 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 serde Session as JSON), read and written only through a SessionSlot: 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 for Timing::abandon_ms is 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: grabfood for the client, grabcli_core::login::LoginDriver for the login.
text
The client’s answers as plain text (Markdown-ish: ids in backticks), for a chat reply or a language model.

Structs§

MemorySlot
A SessionSlot in memory (tests, examples, a single-process tool). Its Debug shows only the account id and whether a session and a cart are kept.
Secret
A secret string (a phone number, a login code): Debug never 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.