use std::path::PathBuf;
use std::sync::Arc;
use offline_protocol::{
account_storage_namespace, DataValue, EnvStoreKey, FileProtocolStateStorage, OfflineProtocol,
ProtocolConfig, SealedFileMlsStorage, StaticStoreKey, StoreKeyProvider,
};
use rand_core::{OsRng, RngCore};
const APP_ID: &str = "com.example.notes";
const PROFILE: &str = "default";
fn open_stores(
root: &std::path::Path,
key: &dyn StoreKeyProvider,
) -> Result<(Arc<SealedFileMlsStorage>, Arc<FileProtocolStateStorage>), Box<dyn std::error::Error>>
{
let namespace = account_storage_namespace(APP_ID, PROFILE);
let secure = SealedFileMlsStorage::open(root.join("keys"), &namespace, key)?;
let records = FileProtocolStateStorage::open(root.join("state"), &namespace)?;
Ok((Arc::new(secure), Arc::new(records)))
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let root = match std::env::args_os().nth(1) {
Some(root) => PathBuf::from(root),
None => {
let mut suffix = [0u8; 8];
OsRng.fill_bytes(&mut suffix);
std::env::temp_dir().join(format!("replicated-notes-{}", hex::encode(suffix)))
}
};
let key: Box<dyn StoreKeyProvider> =
if std::env::var_os(EnvStoreKey::DEFAULT_VARIABLE).is_some() {
Box::new(EnvStoreKey::default())
} else {
let mut key = [0u8; 32];
OsRng.fill_bytes(&mut key);
Box::new(StaticStoreKey::new(key))
};
println!("stores: {}", root.display());
let (secure, records) = open_stores(&root, key.as_ref())?;
let config = ProtocolConfig::builder(APP_ID, PROFILE).build()?;
let mut protocol = OfflineProtocol::new(config)?;
protocol.initialize_mls(secure.clone(), records.clone())?;
let space = "demo-space";
protocol.data_create_doc(space, "trip")?;
protocol.data_map_set(
space,
"trip",
"meta",
"title",
DataValue::Text {
value: "Coast road".to_string(),
},
)?;
for item in ["water", "map", "torch"] {
protocol.data_list_push(
space,
"trip",
"packing",
DataValue::Text {
value: item.to_string(),
},
)?;
}
protocol.data_text_insert(space, "trip", "notes", 0, "Meet at the bridge")?;
protocol.data_counter_increment(space, "trip", "opened", 1.0)?;
protocol.data_flush(space, "trip")?;
println!("spaces: {:?}", protocol.data_list_spaces()?);
println!("documents: {:?}", protocol.data_list_docs(space)?);
println!(
"compacted: {} bytes",
protocol.data_doc_size(space, "trip")?
);
println!(
"history: {} bytes",
protocol.data_export_raw(space, "trip")?.len()
);
println!("json: {}", protocol.data_doc_json(space, "trip")?);
drop(protocol);
drop((secure, records));
let (secure, records) = open_stores(&root, key.as_ref())?;
let config = ProtocolConfig::builder(APP_ID, PROFILE).build()?;
let mut reopened = OfflineProtocol::new(config)?;
reopened.initialize_mls(secure, records)?;
println!("after reopen");
println!(
" title: {:?}",
reopened.data_map_get(space, "trip", "meta", "title")?
);
println!(
" packing: {}",
reopened.data_list_len(space, "trip", "packing")?
);
println!(
" notes: {:?}",
reopened.data_text_value(space, "trip", "notes")?
);
println!(
" opened: {}",
reopened.data_counter_value(space, "trip", "opened")?
);
Ok(())
}