//! Mattermost's REST calls, one connection per request, with the token in the `Authorization` //! header. A 429 waits as asked; a 5xx is tried twice more; a 401 or 403 is `Auth` at once. use std::time::{Duration, SystemTime}; use serde::Deserialize; use crate::config::valid_id; use crate::http::{self, Response, rate_limit_wait}; use crate::mm::{Me, MmError, Post, Since, json, since_list}; use crate::net::Connector; use crate::secrets::Secret; use crate::ws::conn::host_header; /// The pause before trying a call again after a 5xx. pub const RETRY_5XX: Duration = Duration::from_millis(500); /// How many times a call is tried again after a 5xx, and waits after a 429. pub const RETRIES: u32 = 2; /// How much of an error body is kept for the message. const BODY_KEPT: usize = 200; pub struct Client { connector: Connector, token: Secret, timeout: Duration, } #[derive(Deserialize)] struct Channel { id: String, } impl Client { /// `timeout` bounds connecting and each read: no bytes for that long is an error. pub fn new(connector: Connector, token: Secret, timeout: Duration) -> Client { // Store the three. todo!() } pub fn connector(&self) -> &Connector { // The Connector. todo!() } pub fn token(&self) -> &Secret { // The Secret. todo!() } fn once(&self, method: &str, path: &str, body: Option<&[u8]>) -> Result { // 1. `self.connector.connect(self.timeout)`, then `set_read_timeout(Some(self.timeout))`. // 2. Headers: Authorization "Bearer " (the only use of `expose`), Accept // "application/json", and Content-Type "application/json" when there is a body. Host is // `host_header(self.connector.server())`. // 3. `http::request`. Every error on the way is Net(" : "). todo!() } /// One call, tried again as the module comment says; the body of a 2xx answer. fn call(&self, method: &str, path: &str, body: Option<&[u8]>) -> Result, MmError> { // Loop over `once`: 2xx -> the body. 401 or 403 -> Auth(status) at once. 429 -> after // RETRIES waits already, RateLimited(wait); else sleep `rate_limit_wait(&head, // SystemTime::now())` and try again. 5xx -> up to RETRIES more tries, sleeping RETRY_5XX // before each. Anything else -> Status(status, the first BODY_KEPT characters of the body, // lossy UTF-8). todo!() } /// `GET /api/v4/users/me`: who this token is. pub fn me(&self) -> Result { // GET /api/v4/users/me into Me; an id that is not `valid_id` or an empty username is Json. todo!() } /// `POST /api/v4/posts`: a post in `channel`, in the thread of `root` (empty: top level). pub fn create_post(&self, channel: &str, root: &str, message: &str) -> Result { // POST /api/v4/posts with {"channel_id", "root_id", "message"}; the answer is a Post, // `check`ed. todo!() } /// `GET /api/v4/channels/{channel}/posts?since=`: the posts created after `since`. pub fn posts_since(&self, channel: &str, since: i64) -> Result { // A channel that is not `valid_id` is Json, and nothing is sent. Otherwise GET // /api/v4/channels//posts?since=, through `since_list`. todo!() } /// `POST /api/v4/channels/direct`: the id of the direct channel between two users. pub fn direct_channel(&self, a: &str, b: &str) -> Result { // POST /api/v4/channels/direct with the JSON array [a, b]; the answer has an "id", which // must be `valid_id` (else Json). todo!() } }