use std::num::{NonZeroU64, NonZeroUsize};
use anyhow::Result;
use bytes::Bytes;
use iroh_base::hash::Hash;
use rand_core::CryptoRngCore;
use serde::{Deserialize, Serialize};
use crate::{
heads::AuthorHeads,
keys::{Author, NamespaceSecret},
ranger,
sync::{Replica, SignedEntry},
AuthorId, Capability, CapabilityKind, Entry, NamespaceId, PeerIdBytes,
};
#[cfg(feature = "fs-store")]
pub mod fs;
pub mod memory;
mod pubkeys;
mod util;
pub use pubkeys::*;
pub(crate) const PEERS_PER_DOC_CACHE_SIZE: NonZeroUsize = match NonZeroUsize::new(5) {
Some(val) => val,
None => panic!("this is clearly non zero"),
};
#[derive(Debug, thiserror::Error)]
pub enum OpenError {
#[error("Replica is already open")]
AlreadyOpen,
#[error("Replica not found")]
NotFound,
#[error("{0}")]
Other(#[from] anyhow::Error),
}
pub trait Store: std::fmt::Debug + Clone + Send + Sync + 'static {
type Instance: ranger::Store<SignedEntry>
+ PublicKeyStore
+ DownloadPolicyStore
+ Send
+ Sync
+ 'static
+ Clone;
type GetIter<'a>: Iterator<Item = Result<SignedEntry>>
where
Self: 'a;
type ContentHashesIter<'a>: Iterator<Item = Result<Hash>>
where
Self: 'a;
type NamespaceIter<'a>: Iterator<Item = Result<(NamespaceId, CapabilityKind)>>
where
Self: 'a;
type AuthorsIter<'a>: Iterator<Item = Result<Author>>
where
Self: 'a;
type LatestIter<'a>: Iterator<Item = Result<(AuthorId, u64, Vec<u8>)>>
where
Self: 'a;
type PeersIter<'a>: Iterator<Item = PeerIdBytes>
where
Self: 'a;
fn new_replica(&self, namespace: NamespaceSecret) -> Result<Replica<Self::Instance>> {
let id = namespace.id();
self.import_namespace(namespace.into())?;
self.open_replica(&id).map_err(Into::into)
}
fn import_namespace(&self, capability: Capability) -> Result<ImportNamespaceOutcome>;
fn list_namespaces(&self) -> Result<Self::NamespaceIter<'_>>;
fn open_replica(&self, namespace: &NamespaceId) -> Result<Replica<Self::Instance>, OpenError>;
fn close_replica(&self, replica: Replica<Self::Instance>);
fn remove_replica(&self, namespace: &NamespaceId) -> Result<()>;
fn new_author<R: CryptoRngCore + ?Sized>(&self, rng: &mut R) -> Result<Author> {
let author = Author::new(rng);
self.import_author(author.clone())?;
Ok(author)
}
fn import_author(&self, author: Author) -> Result<()>;
fn list_authors(&self) -> Result<Self::AuthorsIter<'_>>;
fn get_author(&self, author: &AuthorId) -> Result<Option<Author>>;
fn get_many(
&self,
namespace: NamespaceId,
query: impl Into<Query>,
) -> Result<Self::GetIter<'_>>;
fn get_exact(
&self,
namespace: NamespaceId,
author: AuthorId,
key: impl AsRef<[u8]>,
include_empty: bool,
) -> Result<Option<SignedEntry>>;
fn content_hashes(&self) -> Result<Self::ContentHashesIter<'_>>;
fn get_latest_for_each_author(&self, namespace: NamespaceId) -> Result<Self::LatestIter<'_>>;
fn has_news_for_us(
&self,
namespace: NamespaceId,
heads: &AuthorHeads,
) -> Result<Option<NonZeroU64>> {
let our_heads = {
let latest = self.get_latest_for_each_author(namespace)?;
let mut heads = AuthorHeads::default();
for e in latest {
let (author, timestamp, _key) = e?;
heads.insert(author, timestamp);
}
heads
};
let has_news_for_us = heads.has_news_for(&our_heads);
Ok(has_news_for_us)
}
fn register_useful_peer(&self, namespace: NamespaceId, peer: PeerIdBytes) -> Result<()>;
fn get_sync_peers(&self, namespace: &NamespaceId) -> Result<Option<Self::PeersIter<'_>>>;
fn set_download_policy(&self, namespace: &NamespaceId, policy: DownloadPolicy) -> Result<()>;
fn get_download_policy(&self, namespace: &NamespaceId) -> Result<DownloadPolicy>;
}
pub trait DownloadPolicyStore {
fn get_download_policy(&self, namespace: &NamespaceId) -> Result<DownloadPolicy>;
}
impl<T: Store> DownloadPolicyStore for T {
fn get_download_policy(&self, namespace: &NamespaceId) -> Result<DownloadPolicy> {
<T as Store>::get_download_policy(self, namespace)
}
}
#[derive(Debug, Clone, Copy)]
pub enum ImportNamespaceOutcome {
Inserted,
Upgraded,
NoChange,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DownloadPolicy {
NothingExcept(Vec<FilterKind>),
EverythingExcept(Vec<FilterKind>),
}
impl Default for DownloadPolicy {
fn default() -> Self {
DownloadPolicy::EverythingExcept(Vec::default())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum FilterKind {
Prefix(Bytes),
Exact(Bytes),
}
impl std::fmt::Display for FilterKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (kind, bytes) = match self {
FilterKind::Prefix(bytes) => ("prefix", bytes),
FilterKind::Exact(bytes) => ("exact", bytes),
};
let (encoding, repr) = match String::from_utf8(bytes.to_vec()) {
Ok(repr) => ("utf8", repr),
Err(_) => ("hex", hex::encode(bytes)),
};
write!(f, "{kind}:{encoding}:{repr}")
}
}
impl std::str::FromStr for FilterKind {
type Err = anyhow::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let Some((kind, rest)) = s.split_once(':') else {
anyhow::bail!("missing filter kind, either \"prefix:\" or \"exact:\"")
};
let Some((encoding, rest)) = rest.split_once(':') else {
anyhow::bail!("missing encoding: either \"hex:\" or \"utf8:\"")
};
let is_exact = match kind {
"exact" => true,
"prefix" => false,
other => {
anyhow::bail!("expected filter kind \"prefix:\" or \"exact:\", found {other}")
}
};
let decoded = match encoding {
"utf8" => Bytes::from(rest.to_owned()),
"hex" => match hex::decode(rest) {
Ok(bytes) => Bytes::from(bytes),
Err(_) => anyhow::bail!("failed to decode hex"),
},
other => {
anyhow::bail!("expected encoding: either \"hex:\" or \"utf8:\", found {other}")
}
};
if is_exact {
Ok(FilterKind::Exact(decoded))
} else {
Ok(FilterKind::Prefix(decoded))
}
}
}
impl FilterKind {
pub fn matches(&self, key: impl AsRef<[u8]>) -> bool {
match self {
FilterKind::Prefix(prefix) => key.as_ref().starts_with(prefix),
FilterKind::Exact(expected) => expected == key.as_ref(),
}
}
}
impl DownloadPolicy {
pub fn matches(&self, entry: &Entry) -> bool {
let key = entry.key();
match self {
DownloadPolicy::NothingExcept(patterns) => {
patterns.iter().any(|pattern| pattern.matches(key))
}
DownloadPolicy::EverythingExcept(patterns) => {
patterns.iter().all(|pattern| !pattern.matches(key))
}
}
}
}
#[derive(Debug, Default)]
pub struct QueryBuilder<K> {
kind: K,
filter_author: AuthorFilter,
filter_key: KeyFilter,
limit: Option<u64>,
offset: u64,
include_empty: bool,
sort_direction: SortDirection,
}
impl<K> QueryBuilder<K> {
pub fn include_empty(mut self) -> Self {
self.include_empty = true;
self
}
pub fn key_exact(mut self, key: impl AsRef<[u8]>) -> Self {
self.filter_key = KeyFilter::Exact(key.as_ref().to_vec().into());
self
}
pub fn key_prefix(mut self, key: impl AsRef<[u8]>) -> Self {
self.filter_key = KeyFilter::Prefix(key.as_ref().to_vec().into());
self
}
pub fn author(mut self, author: AuthorId) -> Self {
self.filter_author = AuthorFilter::Exact(author);
self
}
pub fn limit(mut self, limit: u64) -> Self {
self.limit = Some(limit);
self
}
pub fn offset(mut self, offset: u64) -> Self {
self.offset = offset;
self
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct FlatQuery {
sort_by: SortBy,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SingleLatestPerKeyQuery {}
impl QueryBuilder<FlatQuery> {
pub fn sort_by(mut self, sort_by: SortBy, direction: SortDirection) -> Self {
self.kind.sort_by = sort_by;
self.sort_direction = direction;
self
}
pub fn build(self) -> Query {
Query::from(self)
}
}
impl QueryBuilder<SingleLatestPerKeyQuery> {
pub fn sort_direction(mut self, direction: SortDirection) -> Self {
self.sort_direction = direction;
self
}
pub fn build(self) -> Query {
Query::from(self)
}
}
impl From<QueryBuilder<SingleLatestPerKeyQuery>> for Query {
fn from(builder: QueryBuilder<SingleLatestPerKeyQuery>) -> Query {
Query {
kind: QueryKind::SingleLatestPerKey(builder.kind),
filter_author: builder.filter_author,
filter_key: builder.filter_key,
limit: builder.limit,
offset: builder.offset,
include_empty: builder.include_empty,
sort_direction: builder.sort_direction,
}
}
}
impl From<QueryBuilder<FlatQuery>> for Query {
fn from(builder: QueryBuilder<FlatQuery>) -> Query {
Query {
kind: QueryKind::Flat(builder.kind),
filter_author: builder.filter_author,
filter_key: builder.filter_key,
limit: builder.limit,
offset: builder.offset,
include_empty: builder.include_empty,
sort_direction: builder.sort_direction,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Query {
kind: QueryKind,
filter_author: AuthorFilter,
filter_key: KeyFilter,
limit: Option<u64>,
offset: u64,
include_empty: bool,
sort_direction: SortDirection,
}
impl Query {
pub fn all() -> QueryBuilder<FlatQuery> {
Default::default()
}
pub fn single_latest_per_key() -> QueryBuilder<SingleLatestPerKeyQuery> {
Default::default()
}
pub fn author(author: AuthorId) -> QueryBuilder<FlatQuery> {
Self::all().author(author)
}
pub fn key_exact(key: impl AsRef<[u8]>) -> QueryBuilder<FlatQuery> {
Self::all().key_exact(key)
}
pub fn key_prefix(prefix: impl AsRef<[u8]>) -> QueryBuilder<FlatQuery> {
Self::all().key_prefix(prefix)
}
pub fn limit(&self) -> Option<u64> {
self.limit
}
pub fn offset(&self) -> u64 {
self.offset
}
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
pub enum SortDirection {
#[default]
Asc,
Desc,
}
#[derive(derive_more::Debug, Clone, Serialize, Deserialize)]
enum QueryKind {
#[debug("Flat {{ sort_by: {:?}}}", _0)]
Flat(FlatQuery),
#[debug("SingleLatestPerKey")]
SingleLatestPerKey(SingleLatestPerKeyQuery),
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
pub enum SortBy {
KeyAuthor,
#[default]
AuthorKey,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default, Eq, PartialEq)]
pub enum KeyFilter {
#[default]
Any,
Exact(Bytes),
Prefix(Bytes),
}
impl<T: AsRef<[u8]>> From<T> for KeyFilter {
fn from(value: T) -> Self {
KeyFilter::Exact(Bytes::copy_from_slice(value.as_ref()))
}
}
impl KeyFilter {
pub fn matches(&self, key: &[u8]) -> bool {
match self {
Self::Any => true,
Self::Exact(k) => &k[..] == key,
Self::Prefix(p) => key.starts_with(p),
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Default, Eq, PartialEq)]
pub enum AuthorFilter {
#[default]
Any,
Exact(AuthorId),
}
impl AuthorFilter {
pub fn matches(&self, author: &AuthorId) -> bool {
match self {
Self::Any => true,
Self::Exact(a) => a == author,
}
}
}
impl From<AuthorId> for AuthorFilter {
fn from(value: AuthorId) -> Self {
AuthorFilter::Exact(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_filter_kind_encode_decode() {
const REPR: &str = "prefix:utf8:memes/futurama";
let filter: FilterKind = REPR.parse().expect("should decode");
assert_eq!(
filter,
FilterKind::Prefix(Bytes::from(String::from("memes/futurama")))
);
assert_eq!(filter.to_string(), REPR)
}
}