Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/thepacketgeek/tote

A lightweight data cache for CLI libraries
https://github.com/thepacketgeek/tote

Last synced: about 10 hours ago
JSON representation

A lightweight data cache for CLI libraries

Awesome Lists containing this project

README

        

# Tote [![Build](https://img.shields.io/github/workflow/status/thepacketgeek/tote/Rust?logo=github)](https://github.com/thepacketgeek/tote/actions/workflows/rust.yml) [![Docs](https://img.shields.io/docsrs/tote?logo=rust)](https://docs.rs/crate/tote/latest)

A lightweight data file cache for CLI libraries

For CLIs that query a default set of information with each invocation,
`Tote` offers a convenient way to cache data to a file for quicker
subsequent CLI runs.

When `Tote` is used for a cache (examples below), the `tote.get()` call will:
- Check that the `Tote` filepath exists and has been modified within the specified expiry time
- Deserialize and return the data

If the cached data is not present or expired, `Tote` will:
- Use the `Fetch::fetch` methods to retrieve the data
- Serialize the data (using `serde_json`) and write to the `Tote` filepath
- Return the newly fetched data

## Features
### Default
The default feature uses a Synchronous `Fetch` trait:

```rust
use std::time::Duration;

use serde_derive::{Serialize, Deserialize};
use tote::{Fetch, Tote};

// Implement `serde`'s `Serialize`/`Deserialize` for you own data
// or make a NewType and `derive` so `Tote` can read and write the cached data
#[derive(Debug, Deserialize, Serialize)]
struct NearbyCities(Vec);

impl Fetch for NearbyCities {
type Cached = NearbyCities;

fn fetch() -> Result> {
let resp = reqwest::blocking::get("http://getnearbycities.geobytes.com/GetNearbyCities?radius=10")?
.json::>>()?;
let cities = resp.into_iter().map(|city| format!("{}, {}", city[1], city[2])).collect();
Ok(NearbyCities(cities))
}
}

fn main () -> Result<(), Box> {
// Create a Tote at the given path, with data expiry of 1 day
let cache: Tote = Tote::new(".my_tool.cache", Duration::from_secs(86400));

// This `.get()` call will use data cached in ".my_tool.cache" if:
// - The file exists & has valid data
// - The file has been modified in the past 1 day
// Otherwise `NearbyCities::fetch` is called to get the data and populate the cache file
let nearby_cities = cache.get()?;
println!("Cities near you are: {:?}", nearby_cities);
# std::fs::remove_file(".my_tool.cache")?; // Cleanup for doctest
Ok(())
}
```

### Async
The `"async"` feature adds the `AsyncFetch` trait if you want to use async I/O for fetching data. Call `Tote::get_async().await` to get the `Tote` contents.

#### Cargo.toml
```toml
tote = { version = "*", features = ["async"] }
```

```rust
use std::collections::HashMap;
use std::net::IpAddr;
use std::time::Duration;

use async_trait::async_trait;
use serde_derive::{Serialize, Deserialize};
use tote::{AsyncFetch, Tote};

// Implement `serde`'s `Serialize`/`Deserialize` for you own data
// or make a NewType and `derive` so `Tote` can read and write the cached data
#[derive(Debug, Deserialize, Serialize)]
struct MyPublicIp(IpAddr);

#[async_trait]
impl AsyncFetch for MyPublicIp {
type Cached = MyPublicIp;

async fn fetch_async() -> Result> {
let resp = reqwest::get("https://httpbin.org/ip")
.await?
.json::>()
.await?;
let origin_ip = resp["origin"].parse()?;
Ok(MyPublicIp(origin_ip))
}
}

#[tokio::main]
async fn main () -> Result<(), Box> {
// Create a Tote at the given path, with data expiry of 1 day
let cache: Tote = Tote::new(".my_tool.cache", Duration::from_secs(86400));
// This `.get_async().await` call will use data cached in ".my_tool.cache" if:
// - The file exists & has valid data
// - The file has been modified in the past 1 day
// Otherwise `MyPublicIp::fetch_async` is called to get the data and populate the cache file
let public_ip = cache.get_async().await?;
println!("Your public IP address is {}", public_ip.0);
# std::fs::remove_file(".my_tool.cache")?; // Cleanup for doctest
Ok(())
}
```

# License

`tote` is both MIT and Apache License, Version 2.0 licensed, as found in the LICENSE-MIT and LICENSE-APACHE files.