diff --git a/README.md b/README.md index 8e79602..673f9b4 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ While the exact configuration will depend on the backend used, usage is roughly If you only need some backends, then simply disable the default features, and enable any backends that you require. -2. Construct anud use your queue. +2. Construct and use your queue. The exact configuration type used will depend on your backend, but it's as simple as: @@ -95,7 +95,7 @@ let (p, mut c) = RabbitMqBackend::builder(cfg) }) .with_decoder(|v: &Vec| -> Result { Ok(ExampleType { - field: *v.first().unwrap_or(i&0), + field: *v.first().unwrap_or(&0), }) }) .build_pair() diff --git a/omniqueue/src/lib.rs b/omniqueue/src/lib.rs index b387eb2..309b6fc 100644 --- a/omniqueue/src/lib.rs +++ b/omniqueue/src/lib.rs @@ -1,3 +1,102 @@ +//! # Omniqueue +//! +//! Omniqueue provides a high-level interface for sending and receiving the following over a range +//! of queue backends: +//! +//! * Raw byte arrays in the way most compatible with the queue backend +//! +//! * JSON encoded byte arrays for types that implement [`serde::Deserialize`] and +//! [`serde::Serialize`] +//! +//! * Arbitrary types for which an encoder and/or decoder has been defined +//! +//! ## Cargo Features +//! +//! Each backend is enabled with its associated cargo feature. All backends are enabled by default. +//! As of present it supports: +//! +//! * In-memory queues +//! +//! * RabbitMQ +//! +//! * Redis streams +//! +//! * SQS +//! +//! ## How to Use Omniqueue +//! +//! Each queue backend has a unique configuration type. One of these configurations is taken +//! when constructing the [`queue::QueueBuilder`]. +//! +//! To create a simple producer and/or consumer: +//! +//! ```compile_fail +//! let cfg = SqsConfig { +//! queue_dsn: "http://localhost:9234/queue/queue_name".to_owned(), +//! override_endpoint: true, +//! }; +//! +//! // Either both producer and consumer +//! let (p, mut c) = SqsQueueBackend::builder(cfg.clone()).build_pair().await?; +//! +//! // Or one half +//! let p = SqsQueueBackend::builder(cfg.clone()).build_producer().await?; +//! let mut c = SqsQueueBackend::builder(cfg).build_consumer().await?; +//! +//! (p, c) +//! ``` +//! +//! Sending and receiving information from this queue is simple: +//! +//! ```compile_fail +//! p.send_serde_json(&ExampleType::default()).await?; +//! +//! let delivery = c.receive().await?; +//! let payload = delivery.payload_serde_json::().await?; +//! delivery.ack().await?; +//! ``` +//! +//! ## `DynProducer`s and `DynConsumer`s +//! +//! Dynamic-dispatch can be used easily for when you're not sure which backend to use at +//! compile-time. +//! +//! Making a `DynProducer` or `DynConsumer` is as simple as adding one line to the builder: +//! +//! ```compile_fail +//! let (p, mut c) = RabbitMqBackend::builder(cfg) +//! .make_dynamic() +//! .build_pair() +//! .await?; +//! ``` +//! +//! ## Encoders/Decoders +//! +//! The [`encoding::CustomEncoder`]s and [`decoding::CustomDecoder`]s given to the builder upon +//! producer/consumer creation will be used to convert from/to the queue's native representation +//! into/from a given type. This helps enforce a separation of responsibilities where only the +//! application setting up a concrete queue instance should ever have to think about the internal +//! data-representation of items within the queue while abstract uses of queues should be able to +//! work with simple Rust types. +//! +//! Any function or closure with the right signature may be used as an encoder or decoder. +//! +//! ```compile_fail +//! #[derive(Debug, PartialEq)] +//! struct ExampleType { +//! field: u8, +//! } +//! +//! let (p, mut c) = RabbitMqBackend::builder(cfg) +//! .with_encoder(|et: &ExampleType| -> Result, QueueError> { +//! Ok(vec![et.field]) +//! }) +//! .with_decoder(|v: &Vec| -> Result { +//! Ok(ExampleType { +//! field: *v.first().unwrap_or(&0), +//! }) +//! }) +//! ``` use std::fmt::Debug; use thiserror::Error;