freya_query/lib.rs
1//! # Freya Query
2//!
3//! A powerful, async-focused data management library for Freya applications.
4//! Inspired by React Query and SWR, it provides intelligent caching, background
5//! updates, and automatic invalidation for async operations.
6//!
7//! ## Overview
8//!
9//! Freya Query manages two types of async operations:
10//!
11//! - **Queries**: Read operations that fetch and cache data
12//! - **Mutations**: Write operations that modify data and can invalidate queries
13//!
14//! ## Key Features
15//!
16//! - **Automatic Caching**: Query results are cached and reused across components
17//! - **Background Refetching**: Stale data is automatically refreshed in the background
18//! - **Invalidation**: Mutations can invalidate related queries to keep data fresh
19//! - **Deduplication**: Multiple identical queries are automatically deduplicated
20//! - **Error Handling**: Built-in error states
21//! - **Reactive**: Integrates seamlessly with Freya's reactive state system
22//!
23//! ## Basic Usage
24//!
25//! ### Queries
26//!
27//! ```rust,no_run
28//! use freya::{
29//! prelude::*,
30//! query::*,
31//! };
32//!
33//! # #[derive(Debug)]
34//! # struct User;
35//!
36//! # async fn fetch_user(_id: u32) -> Result<User, String> {
37//! # Ok(User)
38//! # }
39//!
40//! // Define a query capability
41//! #[derive(Clone, PartialEq, Hash, Eq)]
42//! struct FetchUser;
43//!
44//! impl QueryCapability for FetchUser {
45//! type Ok = User;
46//! type Err = String;
47//! type Keys = u32;
48//!
49//! async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
50//! // Fetch user from API
51//! fetch_user(*keys).await
52//! }
53//! }
54//!
55//! #[derive(PartialEq)]
56//! struct UserProfile(u32);
57//!
58//! impl Component for UserProfile {
59//! fn render(&self) -> impl IntoElement {
60//! let user_query = use_query(Query::new(self.0, FetchUser));
61//!
62//! format!("{:?}", user_query.read().state())
63//! }
64//! }
65//! ```
66//!
67//! ### Mutations
68//!
69//! ```rust,no_run
70//! use freya::{
71//! prelude::*,
72//! query::*,
73//! };
74//!
75//! # struct User;
76//!
77//! # async fn update_user(_id: u32, _name: &str) -> Result<User, String> {
78//! # Ok(User)
79//! # }
80//!
81//! #[derive(Clone, PartialEq, Hash, Eq)]
82//! struct UpdateUser {
83//! id: u32,
84//! }
85//!
86//! // Define a query capability
87//! # #[derive(Clone, PartialEq, Hash, Eq)]
88//! # struct FetchUser;
89//!
90//! # impl QueryCapability for FetchUser {
91//! # type Ok = User;
92//! # type Err = String;
93//! # type Keys = u32;
94//! #
95//! # async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
96//! # Ok(User)
97//! # }
98//! # }
99//!
100//! impl MutationCapability for UpdateUser {
101//! type Ok = ();
102//! type Err = String;
103//! type Keys = String;
104//!
105//! async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
106//! update_user(self.id, &keys).await?;
107//! Ok(())
108//! }
109//!
110//! async fn on_settled(&self, keys: &Self::Keys, result: &Result<Self::Ok, Self::Err>) {
111//! if result.is_ok() {
112//! QueriesStorage::<FetchUser>::invalidate_matching(self.id).await;
113//! }
114//! }
115//! }
116//!
117//! #[derive(PartialEq)]
118//! struct UserEditor {
119//! user_id: u32,
120//! }
121//!
122//! impl Component for UserEditor {
123//! fn render(&self) -> impl IntoElement {
124//! let mutation = use_mutation(Mutation::new(UpdateUser { id: self.user_id }));
125//!
126//! Button::new()
127//! .child("Update User")
128//! .on_press(move |_| mutation.mutate("New Name".to_string()))
129//! }
130//! }
131//! ```
132//!
133//! ## Advanced Patterns
134//!
135//! ### Query Invalidation
136//!
137//! Mutations can invalidate queries to ensure data consistency:
138//!
139//! ```rust, ignore
140//! # use freya::query::*;
141//! // Invalidate all user queries
142//! QueriesStorage::<FetchUser>::invalidate_all().await;
143//!
144//! // Invalidate specific user query
145//! QueriesStorage::<FetchUser>::invalidate_matching(1).await;
146//! ```
147//!
148//! ### Custom Query Matching
149//!
150//! Control which queries get invalidated by implementing custom matching logic:
151//!
152//! ```rust, no_run
153//! # use freya::query::*;
154//! # #[derive(Hash, Clone, Eq, PartialEq)]
155//! # struct FetchUser { id: u32 };
156//! impl QueryCapability for FetchUser {
157//! # type Ok = ();
158//! # type Err = String;
159//! # type Keys = u32;
160//! // ... other methods
161//!
162//! # async fn run(&self, keys: &Self::Keys) -> Result<Self::Ok, Self::Err> {
163//! # Ok(())
164//! # }
165//!
166//! fn matches(&self, keys: &Self::Keys) -> bool {
167//! // Only match queries with the same user ID
168//! &self.id == keys
169//! }
170//! }
171//! ```
172//!
173//! ### Background Refetching
174//!
175//! Queries automatically refetch data in the background when components remount
176//! or when explicitly invalidated by mutations.
177//!
178//! ## Architecture
179//!
180//! Freya Query uses a hierarchical caching system:
181//!
182//! - **Query Store**: Global cache of query results by capability type and keys
183//! - **Mutation Store**: Tracks running mutations and their invalidation logic
184//! - **Reactive Integration**: Seamlessly integrates with Freya's state management
185//!
186//! ## Error Handling
187//!
188//! Both queries and mutations return `Result<T, E>` types. Freya Query provides
189//! utilities for handling loading states, errors, and retries.
190//!
191//! ## Performance
192//!
193//! - **Queries Deduplication**: Identical concurrent queries are automatically deduplicated
194//! - **Smart Caching**: Results are cached until invalidated or expired
195//! - **Minimal Re-renders**: Only components reading changed data re-render
196
197pub mod captured;
198pub mod mutation;
199pub mod query;
200
201pub mod prelude {
202 pub use crate::{
203 captured::*,
204 mutation::*,
205 query::*,
206 };
207}