use std::process::ExitCode;
use bytes::Bytes;
use exoware_sdk::{Key, StoreClient, StoreKeyPrefix};
const NAMESPACE: &[u8] = b"example/remote/";
#[tokio::main]
async fn main() -> ExitCode {
let url = std::env::var("EXOWARE_URL").unwrap_or_else(|_| "http://localhost:8080".to_string());
let write_url = std::env::var("EXOWARE_WRITE_URL").unwrap_or_else(|_| url.clone());
println!("read {url}");
println!("write {write_url}");
match round_trip(&url, &write_url).await {
Ok(()) => ExitCode::SUCCESS,
Err(err) => {
eprintln!("\nfailed: {err}");
ExitCode::FAILURE
}
}
}
async fn round_trip(url: &str, write_url: &str) -> Result<(), Box<dyn std::error::Error>> {
let client = StoreClient::builder()
.query_url(url)
.stream_url(url)
.health_url(url)
.ingest_url(write_url)
.prune_url(write_url)
.retention_url(write_url)
.build()?;
let store = client.prefixed(StoreKeyPrefix::new(Bytes::from_static(NAMESPACE))?);
let entries: Vec<(Key, &[u8])> = vec![
(key(b"answer"), b"42".as_slice()),
(key(b"greeting"), b"hello".as_slice()),
(key(b"unit"), b"bytes".as_slice()),
];
let batch: Vec<(&Key, &[u8])> = entries.iter().map(|(k, v)| (k, *v)).collect();
let sequence = store.ingest().put(&batch).await?;
println!("\ncommitted {} entries at sequence {sequence}", batch.len());
let greeting = key(b"greeting");
let value = store
.query()
.get_with_min_sequence_number(&greeting, sequence)
.await?
.ok_or_else(|| format!("greeting is absent at sequence {sequence}, which committed it"))?;
println!("greeting = {}", String::from_utf8_lossy(&value));
if value != b"hello".as_slice() {
return Err(
format!("greeting reads back as {value:?} rather than the \"hello\" written").into(),
);
}
let start = Key::new();
let end = Key::from(vec![0xFF; store.key_prefix().max_logical_key_len()]);
let scanned = store
.query()
.range_with_min_sequence_number(&start, &end, 16, sequence)
.await?;
println!("\nrange over {} keys:", scanned.len());
for (k, value) in &scanned {
println!(
" {} = {}",
String::from_utf8_lossy(k),
String::from_utf8_lossy(value)
);
}
for (expected_key, expected_value) in &entries {
let name = String::from_utf8_lossy(expected_key);
match scanned.iter().find(|(k, _)| k == expected_key) {
Some((_, value)) if value == expected_value => {}
Some((_, value)) => {
return Err(format!(
"range returns {name} as {value:?} rather than {expected_value:?}"
)
.into())
}
None => return Err(format!("range over the namespace omits {name}").into()),
}
}
Ok(())
}
fn key(bytes: &[u8]) -> Key {
Bytes::copy_from_slice(bytes)
}