Work with a Store
In this tutorial we will instantiate a StoreSimpleSled and use it to store and retrieve an Entry and its Payload.
Prerequisites
A basic knowledge of the Rust programming language and executing commands in the terminal will be helpful for completing this tutorial. Some of the steps below also require cargo to be installed.
Additionally, knowledge of the Capability API would be helpful. If you're not yet familiar, please see our dedicated tutorial for capabilities.
Setup
- Create a new directory on your filesystem and name it something like
store. - Using your terminal, run
cargo initwithin the newly created directory. - After than, run
cargo add willow_25 willow-store-simple-sled ufotofu smol.
Instantiate a store
Firstly we'll instantiate a StoreSimpleSled in the directory ./my_db.
Open src/main.rs, delete its contents, and enter the following:
use willow_25::{AuthorisationToken, NamespaceId25, PayloadDigest25, SubspaceId25};
use willow_store_simple_sled::StoreSimpleSled;
fn main() {
// Instantiate a store
let namespace_id = NamespaceId25::new_communal();
let db = sled::open("my_db").unwrap();
let store = StoreSimpleSled::<
1024,
1024,
1024,
NamespaceId25,
SubspaceId25,
PayloadDigest25,
AuthorisationToken,
>::new(&namespace_id, db)
.unwrap();
println!("Instantiated the store!");
std::fs::remove_dir_all("my_db").unwrap();
}
In your terminal, run cargo run, and you should see the following output:
Instantiated the store!Ingest an entry
Next, we'll create a new Entry, use that to create an AuthorisedEntry, and ingest it into theStoreSimpleSled we instantiated.
Make the following changes tosrc/main.rs:
use willow_25::{
AccessMode, AuthorisationToken, AuthorisedEntry, Capability, Entry, NamespaceId25, Path,
PayloadDigest25, SubspaceId25,
data_model::{EntryIngestionSuccess, EntryOrigin, Store},
};
use willow_store_simple_sled::StoreSimpleSled;
fn main() {
// Instantiate a store
let namespace_id = NamespaceId25::new_communal();
let db = sled::open("my_db").unwrap();
let store = StoreSimpleSled::<
1024,
1024,
1024,
NamespaceId25,
SubspaceId25,
PayloadDigest25,
AuthorisationToken,
>::new(&namespace_id, db)
.unwrap();
println!("Instantiated the store!");
// Create an entry and authorise it
let (alfie_id, alfie_secret) = SubspaceId25::new();
let write_cap =
Capability::new_communal(namespace_id.clone(), alfie_id.clone(), AccessMode::Write)
.unwrap();
let path = Path::from_slices(&["ideas", "clock"]).unwrap();
let payload = b"An emoji clock";
let digest = PayloadDigest25::new_from_slice(payload);
let entry = Entry::new(
namespace_id.clone(),
alfie_id.clone(),
path.clone(),
100,
payload.len() as u64,
digest.clone(),
);
let token = write_cap.authorisation_token(&entry, alfie_secret).unwrap();
let authed_entry = AuthorisedEntry::new(entry, token).unwrap();
smol::block_on(async {
// Ingest an entry...
if let Ok(EntryIngestionSuccess::Success) = store
.ingest_entry(authed_entry, false, EntryOrigin::Local)
.await
{
println!("We ingested the entry!")
}
// ... and retrieve it.
if let Some(_entry) = store
.entry(&alfie_id, &path, QueryIgnoreParams::default())
.await
.unwrap()
{
println!("We got our entry back out!")
}
});
std::fs::remove_dir_all("my_db").unwrap();
}
In your terminal, run cargo run, and you should see the following output:
Instantiated the store!
We ingested the entry!
We got our entry back out!Append a payload
Next, we'll try and retrieve the payload of the Entry we just ingested, append some data to its payload, and then try to retrieve it again.
Make the following changes tosrc/main.rs:
use ufotofu::producer::FromSlice;
use willow_25::{
AccessMode, AuthorisationToken, AuthorisedEntry, Capability, Entry, NamespaceId25, Path,
PayloadDigest25, SubspaceId25,
data_model::{EntryIngestionSuccess, EntryOrigin, QueryIgnoreParams, Store},
};
use willow_store_simple_sled::StoreSimpleSled;
fn main() {
// Instantiate a store
let namespace_id = NamespaceId25::new_communal();
let db = sled::open("my_db").unwrap();
let store = StoreSimpleSled::<
1024,
1024,
1024,
NamespaceId25,
SubspaceId25,
PayloadDigest25,
AuthorisationToken,
>::new(&namespace_id, db)
.unwrap();
println!("Instantiated the store!");
// Create an entry and authorise it
let (alfie_id, alfie_secret) = SubspaceId25::new();
let write_cap =
Capability::new_communal(namespace_id.clone(), alfie_id.clone(), AccessMode::Write)
.unwrap();
let path = Path::from_slices(&["ideas", "clock"]).unwrap();
let payload = b"An emoji clock";
let digest = PayloadDigest25::new_from_slice(payload);
let entry = Entry::new(
namespace_id.clone(),
alfie_id.clone(),
path.clone(),
100,
payload.len() as u64,
digest.clone(),
);
let token = write_cap.authorisation_token(&entry, alfie_secret).unwrap();
let authed_entry = AuthorisedEntry::new(entry, token).unwrap();
smol::block_on(async {
// Ingest an entry...
if let Ok(EntryIngestionSuccess::Success) = store
.ingest_entry(authed_entry, false, EntryOrigin::Local)
.await
{
println!("We ingested the entry!")
}
// ... and retrieve it.
if let Some(_entry) = store
.entry(&alfie_id, &path, QueryIgnoreParams::default())
.await
.unwrap()
{
println!("We got our entry back out!")
}
// Try to retrieve its payload...
match store.payload(&alfie_id, &path, 0, None).await {
Ok(_payload) => println!("We have the payload!"),
Err(_) => println!("We haven't ingested the payload yet!"),
}
// ... append some data to the payload...
if let Ok(_success) = store
.append_payload(&alfie_id, &path, Some(digest), &mut FromSlice::new(payload))
.await
{
println!("Appended the payload!")
}
// ... and try to retrieve it again.
match store.payload(&alfie_id, &path, 0, None).await {
Ok(_payload) => println!("We have the payload!"),
Err(_) => println!("We haven't ingested the payload yet!"),
}
});
std::fs::remove_dir_all("my_db").unwrap();
}
In your terminal, run cargo run, and you should see the following output:
Instantiated the store!
We ingested the entry!
We got our entry back out!
We have the payload!
Appended the payload!
We have the payload!Query an area
Next, we'll query an Area within the store and iterate through the results we get.
Make the following changes tosrc/main.rs:
use ufotofu::{Producer, producer::FromSlice};
use willow_25::{
AccessMode, Area, AreaSubspace, AuthorisationToken, AuthorisedEntry, Capability, Entry,
NamespaceId25, Path, PayloadDigest25, Range, SubspaceId25,
data_model::{EntryIngestionSuccess, EntryOrigin, QueryIgnoreParams, Store},
};
use willow_store_simple_sled::StoreSimpleSled;
fn main() {
// Instantiate a store
let namespace_id = NamespaceId25::new_communal();
let db = sled::open("my_db").unwrap();
let store = StoreSimpleSled::<
1024,
1024,
1024,
NamespaceId25,
SubspaceId25,
PayloadDigest25,
AuthorisationToken,
>::new(&namespace_id, db)
.unwrap();
println!("Instantiated the store!");
// Create an entry and authorise it
let (alfie_id, alfie_secret) = SubspaceId25::new();
let write_cap =
Capability::new_communal(namespace_id.clone(), alfie_id.clone(), AccessMode::Write)
.unwrap();
let path = Path::from_slices(&["ideas", "clock"]).unwrap();
let payload = b"An emoji clock";
let digest = PayloadDigest25::new_from_slice(payload);
let entry = Entry::new(
namespace_id.clone(),
alfie_id.clone(),
path.clone(),
100,
payload.len() as u64,
digest.clone(),
);
let token = write_cap.authorisation_token(&entry, alfie_secret).unwrap();
let authed_entry = AuthorisedEntry::new(entry, token).unwrap();
smol::block_on(async {
// Ingest an entry...
if let Ok(EntryIngestionSuccess::Success) = store
.ingest_entry(authed_entry, false, EntryOrigin::Local)
.await
{
println!("We ingested the entry!")
}
// ... and retrieve it.
if let Some(_entry) = store
.entry(&alfie_id, &path, QueryIgnoreParams::default())
.await
.unwrap()
{
println!("We got our entry back out!")
}
// Try to retrieve its payload...
match store.payload(&alfie_id, &path, 0, None).await {
Ok(_payload) => println!("We have the payload!"),
Err(_) => println!("We haven't ingested the payload yet!"),
}
// ... append some data to the payload...
if let Ok(_success) = store
.append_payload(&alfie_id, &path, Some(digest), &mut FromSlice::new(payload))
.await
{
println!("Appended the payload!")
}
// ... and try to retrieve it again.
match store.payload(&alfie_id, &path, 0, None).await {
Ok(_payload) => println!("We have the payload!"),
Err(_) => println!("We haven't ingested the payload yet!"),
}
// Query by area
let ideas_area = Area::new(
AreaSubspace::Id(alfie_id.clone()),
path.clone(),
Range::new_open(0),
);
if let Ok(mut entry_producer) = store
.query_area(&ideas_area, QueryIgnoreParams::default())
.await
{
while let Ok(_lengthy_authed_entry) = entry_producer.produce_item().await {
println!("Found an entry in the area!")
}
}
});
std::fs::remove_dir_all("my_db").unwrap();
}
In your terminal, run cargo run, and you should see the following output:
Instantiated the store!
We ingested the entry!
We got our entry back out!
We have the payload!
Appended the payload!
We have the payload!
Found an entry in the area!Forget an entry
Finally, we'll forget the Entry we ingested earlier.
Make the following changes tosrc/main.rs:
use ufotofu::{Producer, producer::FromSlice};
use willow_25::{
AccessMode, Area, AreaSubspace, AuthorisationToken, AuthorisedEntry, Capability, Entry,
NamespaceId25, Path, PayloadDigest25, Range, SubspaceId25,
data_model::{EntryIngestionSuccess, EntryOrigin, QueryIgnoreParams, Store},
};
use willow_store_simple_sled::StoreSimpleSled;
fn main() {
// Instantiate a store
let namespace_id = NamespaceId25::new_communal();
let db = sled::open("my_db").unwrap();
let store = StoreSimpleSled::<
1024,
1024,
1024,
NamespaceId25,
SubspaceId25,
PayloadDigest25,
AuthorisationToken,
>::new(&namespace_id, db)
.unwrap();
println!("Instantiated the store!");
// Create an entry and authorise it
let (alfie_id, alfie_secret) = SubspaceId25::new();
let write_cap =
Capability::new_communal(namespace_id.clone(), alfie_id.clone(), AccessMode::Write)
.unwrap();
let path = Path::from_slices(&["ideas", "clock"]).unwrap();
let payload = b"An emoji clock";
let digest = PayloadDigest25::new_from_slice(payload);
let entry = Entry::new(
namespace_id.clone(),
alfie_id.clone(),
path.clone(),
100,
payload.len() as u64,
digest.clone(),
);
let token = write_cap.authorisation_token(&entry, alfie_secret).unwrap();
let authed_entry = AuthorisedEntry::new(entry, token).unwrap();
smol::block_on(async {
// Ingest an entry...
if let Ok(EntryIngestionSuccess::Success) = store
.ingest_entry(authed_entry, false, EntryOrigin::Local)
.await
{
println!("We ingested the entry!")
}
// ... and retrieve it.
if let Some(_entry) = store
.entry(&alfie_id, &path, QueryIgnoreParams::default())
.await
.unwrap()
{
println!("We got our entry back out!")
}
// Try to retrieve its payload...
match store.payload(&alfie_id, &path, 0, None).await {
Ok(_payload) => println!("We have the payload!"),
Err(_) => println!("We haven't ingested the payload yet!"),
}
// ... append some data to the payload...
if let Ok(_success) = store
.append_payload(&alfie_id, &path, Some(digest), &mut FromSlice::new(payload))
.await
{
println!("Appended the payload!")
}
// ... and try to retrieve it again.
match store.payload(&alfie_id, &path, 0, None).await {
Ok(_payload) => println!("We have the payload!"),
Err(_) => println!("We haven't ingested the payload yet!"),
}
// Query by area
let ideas_area = Area::new(
AreaSubspace::Id(alfie_id.clone()),
path.clone(),
Range::new_open(0),
);
if let Ok(mut entry_producer) = store
.query_area(&ideas_area, QueryIgnoreParams::default())
.await
{
while let Ok(_lengthy_authed_entry) = entry_producer.produce_item().await {
println!("Found an entry in the area!")
}
}
// Forget an entry
if let Ok(forgotten_count) = store.forget_area(&ideas_area, None).await {
println!("Forgot {forgotten_count} entry(s)!")
}
if store
.entry(&alfie_id, &path, QueryIgnoreParams::default())
.await
.unwrap()
.is_none()
{
println!("Our entry was forgotten!")
}
});
std::fs::remove_dir_all("my_db").unwrap();
}
In your terminal, run cargo run, and you should see the following output:
Instantiated the store!
We ingested the entry!
We got our entry back out!
We have the payload!
Appended the payload!
We have the payload!
Found an entry in the area!
Forgot 1 entry(s)!
Our entry was forgotten!Summary
In this tutorial, we explored Store's APIs via StoreSimpleSled:
- We created a StoreSimpleSled
- We created an Entry, authorised it, and ingested in the store with Store::ingest_entry.
- We appended data to a payload and retrieved that payload.
- We queried an Area using Store::query_area and iterated through the results.
- We used Store::forget_area the Entry we originally ingested.
We've now practiced everything we need to move on to the next tutorial: Create and ingest a sidedrop..