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