diff --git a/README.md b/README.md index fa36464..24d8d38 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,16 @@ # Ferrtable: Ferris the Crab's Favorite Airtable Client -A power vacuum churns where the venerable -[airtable-api](https://crates.io/crates/airtable-api) (archived but not -forgotten) once stood tall. The world calls for a new leader to carry the -fallen torch. From the dust rises Ferrtable: to abase SQL supremacy, to turn -all the tables, to rise inexorably to the top of the field, and to set the -record straight. +Ferrtable provides async Rust bindings for a subset of the +[Airtable web API](https://airtable.com/developers/web/api/introduction). +Notably, it allows records to be read from and written to arbitrary Rust types +that implement the `Clone`, `serde::Deserialize`, and `serde::Serialize` traits. + +This crate follows in the footsteps of the +[airtable-api](https://crates.io/crates/airtable-api) crate from Oxide Computer +Company, which appears to have be archived and unmaintained since 2022. +By comparison, Ferrtable aims to provide a more flexible and expressive client +interface as well as greater control over paginated responses with the help of +async [streams](https://doc.rust-lang.org/book/ch17-04-streams.html). ## Status: Work in Progress @@ -23,64 +28,64 @@ use futures::prelude::*; // Deserialize, and Serialize. #[derive(Clone, Debug, Deserialize, Serialize)] struct MyRecord { - #[serde(rename = "Name")] - name: String, + #[serde(rename = "Name")] + name: String, - #[serde(rename = "Notes")] - notes: String, + #[serde(rename = "Notes")] + notes: String, - #[serde(rename = "Assignee")] - assignee: Option, + #[serde(rename = "Assignee")] + assignee: Option, - #[serde(rename = "Status")] - status: Status, + #[serde(rename = "Status")] + status: Status, - #[serde(rename = "Attachments")] - attachments: Vec, + #[serde(rename = "Attachments")] + attachments: Vec, } #[derive(Clone, Debug, Deserialize, Serialize)] enum Status { - Todo, + Todo, - #[serde(rename = "In progress")] - InProgress, + #[serde(rename = "In progress")] + InProgress, - Done, + Done, } #[tokio::main] async fn main() { - let client = ferrtable::Client::new_from_access_token("******").unwrap(); + let client = ferrtable::Client::new_from_access_token("******").unwrap(); - client - .create_records([MyRecord { - name: "Steal Improbability Drive".to_owned(), - notes: "Just for fun, no other reason.".to_owned(), - assignee: None, - status: Status::InProgress, - attachments: vec![], - }]) - .with_base_id("***".to_owned()) - .with_table_id("***".to_owned()) - .build() - .unwrap() - .execute() - .await - .unwrap(); + client + .create_records([MyRecord { + name: "Steal Improbability Drive".to_owned(), + notes: "Just for fun, no other reason.".to_owned(), + assignee: None, + status: Status::InProgress, + attachments: vec![], + }]) + .with_base_id("***".to_owned()) + .with_table_id("***".to_owned()) + .build() + .unwrap() + .execute() + .await + .unwrap(); - let mut rec_stream = client - .list_records() - .with_base_id("***".to_owned()) - .with_table_id("***".to_owned()) - .with_filter("{status} = 'Todo' || {status} = 'In Progress'".to_owned()) - .build() - .unwrap() - .stream_items::(); + let mut rec_stream = client + .list_records() + .with_base_id("***".to_owned()) + .with_table_id("***".to_owned()) + .with_filter("{status} = 'Todo' || {status} = 'In Progress'".to_owned()) + .build() + .unwrap() + .stream_items::(); - while let Some(result) = rec_stream.next().await { - let rec = result.unwrap(); - dbg!(rec.fields); - } + while let Some(result) = rec_stream.next().await { + let rec = result.unwrap(); + dbg!(rec.fields); + } } ``` diff --git a/ferrtable/src/lib.rs b/ferrtable/src/lib.rs index 2298062..c0d2af4 100644 --- a/ferrtable/src/lib.rs +++ b/ferrtable/src/lib.rs @@ -1,3 +1,88 @@ +//! # Ferrtable: Ferris the Crab's Favorite Airtable Client +//! +//! ## Status: Work in Progress +//! +//! Only a limited set of operations are currently supported. Any version bumps +//! before version 0.1 may include breaking changes to the crate API. +//! +//! ## Usage +//! +//! ```ignore +//! use futures::prelude::*; +//! +//! // Ferrtable allows us to use any record types that implement Clone, +//! // Deserialize, and Serialize. +//! #[derive(Clone, Debug, Deserialize, Serialize)] +//! struct MyRecord { +//! #[serde(rename = "Name")] +//! name: String, +//! +//! #[serde(rename = "Notes")] +//! notes: String, +//! +//! #[serde(rename = "Assignee")] +//! assignee: Option, +//! +//! #[serde(rename = "Status")] +//! status: Status, +//! +//! #[serde(rename = "Attachments")] +//! attachments: Vec, +//! } +//! +//! #[derive(Clone, Debug, Deserialize, Serialize)] +//! enum Status { +//! Todo, +//! +//! #[serde(rename = "In progress")] +//! InProgress, +//! +//! Done, +//! } +//! +//! #[tokio::main] +//! async fn main() { +//! let client = ferrtable::Client::new_from_access_token("******").unwrap(); +//! +//! client +//! .create_records([MyRecord { +//! name: "Steal Improbability Drive".to_owned(), +//! notes: "Just for fun, no other reason.".to_owned(), +//! assignee: None, +//! status: Status::InProgress, +//! attachments: vec![], +//! }]) +//! .with_base_id("***".to_owned()) +//! .with_table_id("***".to_owned()) +//! .build() +//! .unwrap() +//! .execute() +//! .await +//! .unwrap(); +//! +//! let mut rec_stream = client +//! .list_records() +//! .with_base_id("***".to_owned()) +//! .with_table_id("***".to_owned()) +//! .with_filter("{status} = 'Todo' || {status} = 'In Progress'".to_owned()) +//! .build() +//! .unwrap() +//! .stream_items::(); +//! +//! while let Some(result) = rec_stream.next().await { +//! let rec = result.unwrap(); +//! dbg!(rec.fields); +//! } +//! } +//! ``` +//! +//! ## Features +//! +//! ### `chrono` +//! +//! Deserializes certain Airtable timestamp fields as `chrono::DateTime` values +//! instead of [`String`]s. Disabled by default. + pub mod cell_values; pub mod client; pub mod errors;