anystore 0.2.1

Polymorphic, type-safe, composable async API for arbitrary stores
Documentation
use derive_more::Display;

use crate::store::StoreResult;

use super::{AddressableList, SubAddress};

#[derive(Debug, Clone, Display, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum BranchOrLeaf<B, L> {
    Branch(B),
    Leaf(L),
}

impl<B, L> BranchOrLeaf<B, L> {
    pub fn unit(&self) -> BranchOrLeaf<(), ()> {
        match self {
            BranchOrLeaf::Branch(_) => BranchOrLeaf::Branch(()),
            BranchOrLeaf::Leaf(_) => BranchOrLeaf::Leaf(()),
        }
    }
}

pub trait AddressableTree<'a, TreeAddr, ItemAddr>:
    AddressableList<'a, TreeAddr, ItemAddress = TreeAddr>
where
    TreeAddr: SubAddress<Self::AddedAddress, Output = TreeAddr>,
{
    async fn branch_or_leaf(
        &self,
        addr: TreeAddr,
    ) -> StoreResult<BranchOrLeaf<TreeAddr, ItemAddr>, Self>;
}

#[cfg(test)]
#[cfg(feature = "json")]
mod test {
    use std::collections::HashSet;

    use futures::{StreamExt, TryStreamExt};
    use serde_json::json;

    use crate::{
        store::*,
        stores::json::{paths::JsonPath, *},
        wrappers::filter_addresses::FilterAddressesWrapperStore,
    };

    #[tokio::test]
    async fn test() -> Result<(), anyhow::Error> {
        let val = json!({
            "wow": {"hello": "yes"},
            "another": {"seriously": {"throrougly": 7}, "basic": [1, 2, 3, {"hello": "_why"}, {"_why": "ya"}]},
            "_ignore": {"haha": {"_yes": 3}}
        });
        let store = FilterAddressesWrapperStore::new(json_value_store(val)?, |s: JsonPath| {
            s.last()
                .map(|s| !s.to_key().starts_with('_'))
                .unwrap_or(true)
        });
        let root = store.root();

        let all_paths = root
            .walk_tree_recursively()
            .map_ok(|v| v.to_string())
            .try_collect::<HashSet<_>>()
            .await?;

        println!("{all_paths:?}");

        assert!(all_paths.contains("another.basic[2]"));
        assert!(all_paths.contains("another"));
        assert!(!all_paths.contains("_ignore"));
        assert!(!all_paths.contains("_ignore.haha"));
        assert!(!all_paths.contains("another.basic[4]._why"));
        assert!(all_paths.contains("wow.hello"));
        assert!(!all_paths.contains("wow.hello.nonexistent"));
        assert!(all_paths.contains("another.basic[3].hello"));

        Ok(())
    }
}