improve documentation

This commit is contained in:
Brent Schroeter 2026-01-11 20:39:21 +00:00
parent 7c27482d08
commit 4a76f13d58
3 changed files with 72 additions and 8 deletions

View file

@ -7,16 +7,16 @@ that implement the `Clone`, `serde::Deserialize`, and `serde::Serialize` traits.
This crate follows in the footsteps of the This crate follows in the footsteps of the
[airtable-api](https://crates.io/crates/airtable-api) crate from Oxide Computer [airtable-api](https://crates.io/crates/airtable-api) crate from Oxide Computer
Company, which appears to have been archived and unmaintained since 2022. Company, which appears to have been archived and unmaintained since 2022. By
By comparison, Ferrtable aims to provide a more flexible and expressive client comparison, Ferrtable aims to provide a more flexible and expressive client
interface as well as greater control over paginated responses with the help of 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). async [streams](https://doc.rust-lang.org/book/ch17-04-streams.html).
## Status: Work in Progress ## Status: Work in Progress
Only a limited set of operations (e.g., creating and listing records) are Only a limited set of operations (e.g., creating and listing records) are
currently supported. The goal is to implement coverage for at least the full currently supported. The goal is to implement coverage for at least the full set
set of non-enterprise API endpoints, but my initial emphasis is on getting a of non-enterprise API endpoints, but my initial emphasis is on getting a
relatively small subset built and tested well. relatively small subset built and tested well.
## Usage ## Usage
@ -89,3 +89,57 @@ async fn main() -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
``` ```
## Crate Release Process
Maintainers: Eventually the release process should be automated, but until CI
runners are available for the repo, follow this checklist.
1. Lint:
```sh
cd ferrtable
cargo fmt --check
cargo clippy
cd ../ferrtable-test
cargo fmt --check
cargo clippy
```
2. Run integration tests:
```sh
cd ferrtable-test
cat <<EOF
access_token = "<AIRTABLE ACCESS TOKEN>"
base_id = "<BASE ID>"
table_id = "<TABLE ID>"
EOF > ferrtable-test.config.toml
cargo run
```
3. Run unit tests:
```sh
cd ferrtable
# At the time of this writing, nextest doesn't actually have anything to run,
# but it may in the future as tests are added outside of doc comments.
cargo nextest run --all-features --no-tests=warn
cargo test --doc
```
4. Bump `version` in `Cargo.toml`.
5. Update main package version in `ferrtable-test` lockfile:
```sh
cd ferrtable-test
cargo build
```
6. Commit and push changes.
7. Publish:
```sh
cargo publish
```

View file

@ -1,5 +1,6 @@
use thiserror::Error; use thiserror::Error;
/// Errors that may occur when making an Airtable API request.
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum ExecutionError { pub enum ExecutionError {
#[error("error making http request to airtable api: {0}")] #[error("error making http request to airtable api: {0}")]

View file

@ -35,8 +35,17 @@ where
Q: Clone, Q: Clone,
T: Clone, T: Clone,
{ {
/// Records remaining from the most recently fetched page but not yet
/// yielded to the stream consumer. Subsequent page should not be fetched
/// until this collection has been fully consumed.
buffered: VecDeque<T>, buffered: VecDeque<T>,
/// Owned copy of the paginated query.
query: Q, query: Q,
/// When `query.offset` is `None`, this flag indicates whether that is
/// because the first page still needs to be fetched (`started` = `false`)
/// or because there are no pages remaining (`started` = `true`).
started: bool, started: bool,
} }
@ -61,9 +70,9 @@ macro_rules! handle_stream_err {
}; };
} }
// This could be brought into PaginatedQuery as a default implementation, but // This could be folded into the `PaginatedQuery` trait as a default
// that forces that the traits in this module be exposed outside of the crate // implementation, but that forces that the traits in this module be exposed
// and additionally results in worse client ergonomics overall. // outside of the crate and results in worse caller ergonomics overall.
pub(crate) fn execute_paginated<T, R>( pub(crate) fn execute_paginated<T, R>(
query: impl PaginatedQuery<T, R>, query: impl PaginatedQuery<T, R>,
) -> Pin<Box<impl Stream<Item = Result<T, ExecutionError>>>> ) -> Pin<Box<impl Stream<Item = Result<T, ExecutionError>>>>
@ -72,7 +81,7 @@ where
R: PaginatedResponse<T>, R: PaginatedResponse<T>,
{ {
// Stream has to be pinned to the heap so that the closure inside // Stream has to be pinned to the heap so that the closure inside
// doesn't need to implement Unpin (which I don't think it can). // doesn't need to implement Unpin.
Box::pin(futures::stream::unfold( Box::pin(futures::stream::unfold(
StreamState { StreamState {
buffered: VecDeque::new(), buffered: VecDeque::new(),