1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
use TokenStream;
use ;
use crateExtendArgs;
/// # `derive(Store)`
///
/// The `Store` macro is used to create an extension trait for stores that makes it possible to access the fields or variants
/// of an item as stores.
///
/// ## Expansion
///
/// The macro expands to two different items:
/// - An extension trait which is implemented for `Store<YourType, W>` with methods to access fields and variants for your type.
/// - A transposed version of your type which contains the fields or variants as stores.
///
/// ### Structs
///
/// For structs, the store macro generates methods for each field that returns a store scoped to that field and a `transpose` method that returns a struct with all fields as stores:
///
/// ```rust, no_run
/// use dioxus::prelude::*;
/// use dioxus_stores::*;
///
/// #[derive(Store)]
/// struct TodoItem {
/// checked: bool,
/// contents: String,
/// }
///
/// let store = use_store(|| TodoItem {
/// checked: false,
/// contents: "Learn about stores".to_string(),
/// });
///
/// // The store macro creates an extension trait with methods for each field
/// // that returns a store scoped to that field.
/// let checked: Store<bool, _> = store.checked();
/// let contents: Store<String, _> = store.contents();
///
/// // It also generates a `transpose` method returns a variant of your structure
/// // with stores wrapping each of your data types. This can be very useful when destructuring
/// // or matching your type
/// let TodoItemStoreTransposed { checked, contents } = store.transpose();
/// let checked: bool = checked();
/// let contents: String = contents();
/// ```
///
///
/// ### Enums
///
/// For enums, the store macro generates methods for each variant that checks if the store is that variant. It also generates a `transpose` method that returns an enum with all fields as stores.
///
/// ```rust, no_run
/// use dioxus::prelude::*;
/// use dioxus_stores::*;
///
/// #[derive(Store, PartialEq, Clone, Debug)]
/// enum Enum {
/// Foo(String),
/// Bar { foo: i32, bar: String },
/// }
///
/// let store = use_store(|| Enum::Foo("Hello".to_string()));
/// // The store macro creates an extension trait with methods for each variant to check
/// // if the store is that variant.
/// let foo: bool = store.is_foo();
/// let bar: bool = store.is_bar();
///
/// // If there is only one field in the variant, it also generates a method to try
/// // to downcast the store to that variant.
/// let foo: Option<Store<String, _>> = store.foo();
/// if let Some(foo) = foo {
/// println!("Foo: {foo}");
/// }
///
/// // It also generates a `transpose` method that returns a variant of your enum where all
/// // the fields are stores. You can use this to match your enum
/// let transposed = store.transpose();
/// use EnumStoreTransposed::*;
/// match transposed {
/// EnumStoreTransposed::Foo(foo) => println!("Foo: {foo}"),
/// EnumStoreTransposed::Bar { foo, bar } => {
/// let foo: i32 = foo();
/// let bar: String = bar();
/// println!("Bar: foo = {foo}, bar = {bar}");
/// }
/// }
/// ```
/// # `#[store]`
///
/// The `store` attribute macro is used to create an extension trait for store implementations. The extension traits lets you add
/// methods to the store even though the type is not defined in your crate.
///
/// ## Arguments
///
/// - `pub`: Makes the generated extension trait public. If not provided, the trait will be private.
/// - `name = YourExtensionName`: The name of the extension trait. If not provided, it will be generated based on the type name.
///
/// ## Bounds
///
/// The generated extension trait will have bounds on the lens generic parameter to ensure it implements `Readable` or `Writable` as needed.
///
/// - If a method accepts `&self`, the lens will require `Readable` which lets you read the value of the store.
/// - If a method accepts `&mut self`, the lens will require `Writable` which lets you change the value of the store.
///
/// ## Example
///
/// ```rust, no_run
/// use dioxus::prelude::*;
/// use dioxus_stores::*;
///
/// #[derive(Store)]
/// struct TodoItem {
/// checked: bool,
/// contents: String,
/// }
///
/// // You can use the store attribute macro to add methods to your stores
/// #[store]
/// impl<Lens> Store<TodoItem, Lens> {
/// // Since this method takes &mut self, the lens will require Writable automatically. It cannot be used
/// // with ReadStore<TodoItem>
/// fn toggle_checked(&mut self) {
/// self.checked().toggle();
/// }
///
/// // Since this method takes &self, the lens will require Readable automatically
/// fn checked_contents(&self) -> Option<String> {
/// self.checked().cloned().then(|| self.contents().to_string())
/// }
/// }
///
/// let mut store = use_store(|| TodoItem {
/// checked: false,
/// contents: "Learn about stores".to_string(),
/// });
///
/// // You can use the methods defined in the extension trait
/// store.toggle_checked();
/// let contents: Option<String> = store.checked_contents();
/// ```