anystore/
lib.rs

1#![feature(async_fn_in_trait)]
2#![feature(never_type)]
3#![feature(associated_type_defaults)]
4#![feature(try_trait_v2)]
5#![feature(try_blocks)]
6// #![feature(return_position_impl_trait_in_trait)]
7#![feature(error_generic_member_access)]
8// #![feature(provide_any)]
9#![feature(doc_cfg)]
10#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
11
12//! # anystore
13//!
14//! `anystore` is a polymorphic, type-safe, composable async framework for specifying API for arbitrary stores
15//! (including databases and configuration systems). It supports addressing arbitrary type-safe hierarchies of objects.
16//!
17//! It is best used for prototyping and configuration. It is especially good for situations when a storage system is needed,
18//! but it's not very important, and you want to be able to change the storage provider quickly, in case the requirements change.
19//!
20//! It is good for when you don't want to learn *yet another API*. (It also might be good if you don't want to invent *yet another API*,
21//! as it gives enough structure, primitives, and utils to help you build a decent client.)
22//!
23//! It is not intended to be used when you need high performance or reliability.
24//!
25//! **This crate is nightly-only**. It heavily depends on the `async_fn_in_trait` feature, which is not stable yet.
26//!
27//! Goals:
28//!
29//! * Provide many reasonable defaults and demand very few inconsequential choices.
30//! * Provide a variety of wrappers to compose or augment particular stores.
31//! * Type safety: as long as the type is statically determinable, it should be inferred.
32//! * Work nicely with autocompletion and type inference.
33//! * Make it easy and safe to switch the store used by a program.
34//! * Try to capture that blessed feeling of wonder when you implement a few small pieces and suddenly
35//!     you have a lot of cool emerging features.
36//! * The values most passed around should be thread-safe, `Sync`, `Send` -- for ergonomic reasons.
37//!
38//! Hopes and dreams:
39//! * Generic dynamic configuration for any store.
40//! * Become a reasonable framework for developing API interfaces for stores.
41//! * Provide an extensive test framework to make testing of new store implementations easier.
42//! * Support CLI/TUI tools for navigating and editing arbitrary stores.
43//!
44//! Non-goals:
45//!
46//! * Serious performance considerations beyond simple hygiene. This will never be a goal.
47//! * Supporting all the database features for a particular database. We'd rather support the most generally
48//!     useful common subset.
49//! * Complete concurrency safety. We're constrained here by the particulars of the stores used.
50//! * For now, avoiding unsafe/unstable features. This crate already uses `async_fn_in_trait`,
51//!     and while it's not stable, there's no point in trying to keep everything `stable`.
52//!     Hopefully one day it'll stabilize and we can rethink this non-goal.
53
54//! # Quick examples
55//!
56//! Check those out:
57//! * Recursive traversing of the trees: [`Location::walk_tree_recursively`](location::Location::walk_tree_recursively)
58//! * Turn any store of Strings into JSON store: [`LocatedJsonStore`](stores::located::json::LocatedJsonStore)
59//! * [`FilterAddressesWrapperStore`](wrappers::filter_addresses::FilterAddressesWrapperStore) -- dynamically filter out addresses in any store
60//!
61//! # Main concepts
62//!
63//! ## Location
64//!
65//! [`location::Location`] is the object you'll use the most, and it contains most of the important client API.
66//! It is simply a pair of an address and a store. This is the value you'd most typically pass around,
67//! and it has a bunch of helper methods. You can traverse it further:
68//!
69#![cfg_attr(not(feature = "json"), doc = "```ignore")]
70#![cfg_attr(feature = "json", doc = "```no_run")]
71//! # use anystore::stores::json::*;
72//! # use anystore::location::Location;
73//! # fn testloc(jsonlocation: Location<JsonPath, JsonValueStore>) {
74//! let location = jsonlocation
75//!                     .sub(JsonPathPart::Key("subkey".to_owned()))
76//!                     .sub(JsonPathPart::Index(2));
77//! # }
78//! ```
79//!
80//! At every point, `Location` tracks the type and is typically able to statically tell you what kind of
81//! values you can expect there. `Store`s can define a [`DefaultValue`](address::Addressable::DefaultValue) for the addresses,
82//! which allows you to get immediately the correct default type:
83//!
84#![cfg_attr(not(feature = "json"), doc = "```ignore")]
85#![cfg_attr(feature = "json", doc = "```no_run")]
86//! # use anystore::location::Location;
87//! # use anystore::stores::json::*;
88//! # use anystore::store::*;
89//! # use anystore::address::traits::*;
90//! # async fn test<V, S: Store + Addressable<JsonPath, DefaultValue=V> + AddressableGet<V, JsonPath>>(location: Location<JsonPath, S>) -> StoreResult<(), S> {
91//! let val = location.getv().await?;
92//! # Ok(()) };
93//! ```
94//!
95//! In cases when you need a more refined type than the default, you can use e.g. [`Location::get::<Type>`](location::Location::get), as long as
96//! the address supports that type.
97//!
98#![cfg_attr(not(feature = "json"), doc = "```ignore")]
99#![cfg_attr(feature = "json", doc = "```no_run")]
100//! # use anystore::store::StoreResult;
101//! # use anystore::location::Location;
102//! # use anystore::store::Store;
103//! # use anystore::address::traits::*;
104//! # use anystore::stores::json::*;
105//! # type Value = ();
106//! # async fn test<S: Store + AddressableGet<Value, JsonPath>>(location: Location<JsonPath, S>) -> StoreResult<(), S> {
107//! let val = location.get::<Value>().await?;
108//! # Ok(()) };
109//! ```
110//!
111//! In many cases, you can also use strings to traverse it, which is sometimes convenient but less typesafe.
112//!
113#![cfg_attr(not(feature = "json"), doc = "```ignore")]
114#![cfg_attr(feature = "json", doc = "```no_run")]
115//! # use anystore::stores::json::*;
116//! # use anystore::store::StoreResult;
117//! # use anystore::location::Location;
118//! # fn testloc(jsonlocation: Location<JsonPath, JsonValueStore>) -> StoreResult<(), JsonValueStore> {
119//! let location = jsonlocation.path("subkey[2]")?.path("deeper.anotherone[12]")?;
120//! # Ok(()) };
121//! ```
122//!
123//! ## Address
124//!
125//! A storage system is defined around the concept of [`address::Address`]. An address uniquely identifies
126//! a piece of content. A storage system can support several types as [Addresses][`address::Address`]: e.g.
127//! pointers to specific databases, tables, rows, cells, or even sub-values inside cells.
128//!
129//! If a system understands a particular address, it implements [traits][`address::traits`] like [`address::traits::AddressableGet`].
130//! `address::traits::AddressableGet<SomeType, SomeAddr>` means that `SomeAddr` can be used to read a value of `SomeType`.
131//! Often there's a bunch of `SomeType`s that is useable with an address, including special values
132//! like `address::primitive::Existence` which is simply used to check whether something exists at that address.
133//!
134//! In some cases, "address" and "value" are more or less the same thing.
135//! E.g., if you list your Airtable bases, you get the data about them, but then you can reuse it as an address.
136//!
137//! ## Wrappers
138//!
139//! The traits in this crate are designed to be easily composable without too much boilerplate. That allows
140//! the creation of abstract wrappers that add functionality to the existing stores or compose stores together.
141//!
142//! # Table of contents
143//!
144//! Be aware that most of the things in this crate are hidden behind specific features.
145//!
146//! Basic stores:
147//! - [`stores::fs::FileSystemStore`](stores::fs::FileSystemStore) file system as a store
148//!
149//! Wrappers:
150//! - [`stores::located::json::LocatedJsonStore`] -- use this over any `Location` to store JSON in it
151//! - [`wrappers::filter_addresses::FilterAddressesWrapperStore`] -- wrap this over a store to dynamically filter out addresses
152//!
153//! Cloud services:
154//! - [`stores::cloud::airtable::AirtableStore`](stores::cloud::airtable::AirtableStore) -- Airtable
155//!
156//! Memory:
157//! - [`stores::cell::MemoryCellStore`] arbitrary memory cell as a readable/writable location (use it with wrappers)
158//! - [`stores::json::json_value_store`] `serde_json::Value` as a store (simply a [`MemoryCellStore`](stores::cell::MemoryCellStore) wrapped in [`LocatedJsonStore`](stores::located::json::LocatedJsonStore))
159//! - [`stores::indexed_vec::IndexedVecStore`] Vector indexed by Id derived from the value
160//!
161pub mod store;
162
163pub mod address;
164pub mod location;
165pub mod stores;
166pub mod util;
167pub mod wrappers;