Skip to main content

honk_me/
lib.rs

1//! The official Rust client for [Honk](https://honk-me.app): send events from your apps, jobs,
2//! scripts and CI to your phone, as calm, grouped push notifications.
3//!
4//! - Async-first on Tokio and `reqwest` with rustls (no OpenSSL); a synchronous
5//!   `blocking::Honk` with the feature `blocking`.
6//! - Every send carries an `Idempotency-Key` (yours, or a UUIDv7) that is reused on every
7//!   retry, so a retry never creates a duplicate.
8//! - Retries only network errors, timeouts, 429 and 5xx, with exponential backoff and full
9//!   jitter, never sooner than `Retry-After`, all under a total deadline (30 s). Redirects are
10//!   reported, never followed.
11//! - Local validation of everything cheap (lengths, URLs, actions, metadata, the 16 KiB body),
12//!   with every invalid field reported at once. [`HonkBuilder::validate`]`(false)` leaves it to
13//!   the server.
14//!
15//! Keep the ingestion key (`honk_…`) on the server: never ship it inside a client app.
16//!
17//! # Quick start
18//!
19//! ```no_run
20//! use honk_me::{Honk, Message, Severity};
21//!
22//! #[tokio::main]
23//! async fn main() -> honk_me::Result<()> {
24//!     let honk = Honk::from_env()?; // HONK_URL, HONK_KEY
25//!
26//!     // The Honk scale: light, beep, loud, long, blast.
27//!     honk.beep("Backup finished", "nightly pg_dump took 42 s").await?;
28//!
29//!     // Every field, and your own idempotency key.
30//!     let msg = Message::new("Ana asked for a quote: 3 rooms, 2 bathrooms")
31//!         .title("New quote request")
32//!         .severity(Severity::LOUD)
33//!         .group_key("requests/4812")
34//!         .url("https://shop.example.com/admin/requests/4812")
35//!         .metadata("rooms", 3);
36//!     let accepted = honk.send(&msg, "request-4812").await?;
37//!     println!("stored as {}", accepted.id);
38//!
39//!     // Incidents: a problem opens one for its group, a recovery closes it.
40//!     honk.problem("db/backup", "Backup failed", "pg_dump exited with 1").await?;
41//!     honk.recovery("db/backup", "Backup OK", "pg_dump finished").await?;
42//!     Ok(())
43//! }
44//! ```
45//!
46//! # Errors
47//!
48//! Every failure is an [`Error`]: `Validation` (fix the message), `Auth`, `Quota`, `Conflict`,
49//! `Network`, `Timeout`, `Server` or `Http`, each with a [`Failure`] holding the details.
50//! [`Error::is_retryable`] tells whether sending again later (with the same idempotency key)
51//! may succeed.
52//!
53//! # Features
54//!
55//! - `blocking`: `blocking::Honk`, a synchronous client on a private Tokio runtime.
56
57#![forbid(unsafe_code)]
58#![warn(missing_docs, rust_2018_idioms, unreachable_pub)]
59#![cfg_attr(docsrs, feature(doc_cfg))]
60
61#[cfg(feature = "blocking")]
62#[cfg_attr(docsrs, doc(cfg(feature = "blocking")))]
63pub mod blocking;
64mod client;
65mod error;
66mod message;
67mod time;
68mod uuid;
69mod validate;
70
71pub use client::{Honk, HonkBuilder, PendingSend, VERSION};
72pub use error::{Error, Failure, FieldError, Result};
73pub use message::{
74    Accepted, Action, Category, Defaults, EventType, Message, MetadataValue, ParseEnumError,
75    Priority, Severity,
76};
77pub use uuid::new_idempotency_key;
78pub use validate::{
79    MAX_ACTION_TITLE, MAX_ACTIONS, MAX_BODY_BYTES, MAX_CHANNEL, MAX_ENVIRONMENT, MAX_GROUP_KEY,
80    MAX_MESSAGE_BYTES, MAX_METADATA_KEYS, MAX_METADATA_STRING, MAX_SOURCE, MAX_TITLE,
81    MAX_TTL_SECONDS, MAX_URL_BYTES, MIN_TTL_SECONDS, encode_message,
82};