valuable/structable.rs
1use crate::field::*;
2use crate::*;
3
4use core::fmt;
5
6/// A struct-like [`Valuable`] sub-type.
7///
8/// Implemented by [`Valuable`] types that have a struct-like shape. Fields may
9/// be named or unnamed (tuple). Values that implement `Structable` must return
10/// [`Value::Structable`] from their [`Valuable::as_value`] implementation.
11///
12/// # Inspecting
13///
14/// Inspecting fields contained by a `Structable` instance is done by visiting
15/// the struct. When visiting a `Structable`, either the `visit_named_fields()`
16/// or the `visit_unnamed_fields()` methods of `Visit` are called. Each method
17/// may be called multiple times per `Structable`, but the two methods are never
18/// mixed.
19///
20/// ```
21/// use valuable::{NamedValues, Valuable, Value, Visit};
22///
23/// #[derive(Valuable)]
24/// struct MyStruct {
25/// foo: u32,
26/// bar: u32,
27/// }
28///
29/// struct PrintFields;
30///
31/// impl Visit for PrintFields {
32/// fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) {
33/// for (field, value) in named_values.iter() {
34/// println!("{}: {:?}", field.name(), value);
35/// }
36/// }
37///
38/// fn visit_value(&mut self, value: Value<'_>) {
39/// match value {
40/// Value::Structable(v) => v.visit(self),
41/// _ => {} // do nothing for other types
42/// }
43/// }
44/// }
45///
46/// let my_struct = MyStruct {
47/// foo: 123,
48/// bar: 456,
49/// };
50///
51/// valuable::visit(&my_struct, &mut PrintFields);
52/// ```
53///
54/// If the struct is **statically** defined, then all fields are known ahead of
55/// time and may be accessed via the [`StructDef`] instance returned by
56/// [`definition()`]. [`NamedField`] instances returned by [`definition()`]
57/// maybe used to efficiently extract specific field values.
58///
59/// # Implementing
60///
61/// Implementing `Structable` is usually done by adding `#[derive(Valuable)]` to
62/// a Rust `struct` definition.
63///
64/// ```
65/// use valuable::{Fields, Valuable, Structable, StructDef};
66///
67/// #[derive(Valuable)]
68/// struct MyStruct {
69/// foo: &'static str,
70/// }
71///
72/// let my_struct = MyStruct { foo: "Hello" };
73/// let fields = match my_struct.definition() {
74/// StructDef::Static { name, fields, .. } => {
75/// assert_eq!("MyStruct", name);
76/// fields
77/// }
78/// _ => unreachable!(),
79/// };
80///
81/// match fields {
82/// Fields::Named(named_fields) => {
83/// assert_eq!(1, named_fields.len());
84/// assert_eq!("foo", named_fields[0].name());
85/// }
86/// _ => unreachable!(),
87/// }
88/// ```
89///
90/// [`definition()`]: Structable::definition()
91pub trait Structable: Valuable {
92 /// Returns the struct's definition.
93 ///
94 /// See [`StructDef`] documentation for more details.
95 ///
96 /// # Examples
97 ///
98 /// ```
99 /// use valuable::{Structable, Valuable};
100 ///
101 /// #[derive(Valuable)]
102 /// struct MyStruct {
103 /// foo: u32,
104 /// }
105 ///
106 /// let my_struct = MyStruct {
107 /// foo: 123,
108 /// };
109 ///
110 /// assert_eq!("MyStruct", my_struct.definition().name());
111 fn definition(&self) -> StructDef<'_>;
112}
113
114/// A struct's name, fields, and other struct-level information.
115///
116/// Returned by [`Structable::definition()`], `StructDef` provides the caller
117/// with information about the struct's definition.
118///
119/// [`Structable::definition()`]: Structable::definition
120#[derive(Debug)]
121#[non_exhaustive]
122pub enum StructDef<'a> {
123 /// The struct is statically-defined, all fields are known ahead of time.
124 ///
125 /// Most `Structable` definitions for Rust struct types will be
126 /// `StructDef::Static`.
127 ///
128 /// # Examples
129 ///
130 /// A statically defined struct
131 ///
132 /// ```
133 /// use valuable::{Fields, Valuable, Structable, StructDef};
134 ///
135 /// #[derive(Valuable)]
136 /// struct MyStruct {
137 /// foo: &'static str,
138 /// }
139 ///
140 /// let my_struct = MyStruct { foo: "Hello" };
141 /// let fields = match my_struct.definition() {
142 /// StructDef::Static { name, fields, ..} => {
143 /// assert_eq!("MyStruct", name);
144 /// fields
145 /// }
146 /// _ => unreachable!(),
147 /// };
148 ///
149 /// match fields {
150 /// Fields::Named(named_fields) => {
151 /// assert_eq!(1, named_fields.len());
152 /// assert_eq!("foo", named_fields[0].name());
153 /// }
154 /// _ => unreachable!(),
155 /// }
156 /// ```
157 #[non_exhaustive]
158 Static {
159 /// The struct's name.
160 name: &'static str,
161
162 /// The struct's fields.
163 fields: Fields<'static>,
164 },
165
166 /// The struct is dynamically-defined, not all fields are known ahead of
167 /// time.
168 ///
169 /// A dynamically-defined struct **could** be represented using
170 /// [`Mappable`], though, using `Structable` offers benefits in a couple of
171 /// cases. For example, when serializing a `Value`, some formats will
172 /// serialize maps and structs differently. In this case, differentiating
173 /// the two is required. There also are times when **some** struct fields
174 /// are known statically, but not all of them (see second example).
175 ///
176 /// # Examples
177 ///
178 /// The struct stores field values in a `HashMap`.
179 ///
180 /// ```
181 /// use valuable::{Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit};
182 /// use std::collections::HashMap;
183 ///
184 /// /// A dynamic struct
185 /// struct Dyn {
186 /// // The struct name
187 /// name: String,
188 ///
189 /// // Named values.
190 /// values: HashMap<String, Box<dyn Valuable>>,
191 /// }
192 ///
193 /// impl Valuable for Dyn {
194 /// fn as_value(&self) -> Value<'_> {
195 /// Value::Structable(self)
196 /// }
197 ///
198 /// fn visit(&self, visit: &mut dyn Visit) {
199 /// // This could be optimized to batch some.
200 /// for (field, value) in self.values.iter() {
201 /// visit.visit_named_fields(&NamedValues::new(
202 /// &[NamedField::new(field)],
203 /// &[value.as_value()],
204 /// ));
205 /// }
206 /// }
207 /// }
208 ///
209 /// impl Structable for Dyn {
210 /// fn definition(&self) -> StructDef<'_> {
211 /// StructDef::new_dynamic(&self.name, Fields::Named(&[]))
212 /// }
213 /// }
214 /// ```
215 ///
216 /// Some fields are known statically.
217 ///
218 /// ```
219 /// use valuable::{Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit};
220 /// use std::collections::HashMap;
221 ///
222 /// struct HalfStatic {
223 /// foo: u32,
224 /// bar: u32,
225 /// extra_values: HashMap<String, Box<dyn Valuable>>,
226 /// }
227 ///
228 /// impl Valuable for HalfStatic {
229 /// fn as_value(&self) -> Value<'_> {
230 /// Value::Structable(self)
231 /// }
232 ///
233 /// fn visit(&self, visit: &mut dyn Visit) {
234 /// // First, visit static fields
235 /// visit.visit_named_fields(&NamedValues::new(
236 /// FIELDS,
237 /// &[self.foo.as_value(), self.bar.as_value()],
238 /// ));
239 ///
240 /// // This could be optimized to batch some.
241 /// for (field, value) in self.extra_values.iter() {
242 /// visit.visit_named_fields(&NamedValues::new(
243 /// &[NamedField::new(field)],
244 /// &[value.as_value()],
245 /// ));
246 /// }
247 /// }
248 /// }
249 ///
250 /// static FIELDS: &[NamedField<'static>] = &[
251 /// NamedField::new("foo"),
252 /// NamedField::new("bar"),
253 /// ];
254 ///
255 /// impl Structable for HalfStatic {
256 /// fn definition(&self) -> StructDef<'_> {
257 /// // Include known fields.
258 /// StructDef::new_dynamic(
259 /// "HalfStatic",
260 /// Fields::Named(FIELDS))
261 /// }
262 /// }
263 /// ```
264 #[non_exhaustive]
265 Dynamic {
266 /// The struct's name
267 name: &'a str,
268
269 /// The struct's fields.
270 fields: Fields<'a>,
271 },
272}
273
274impl fmt::Debug for dyn Structable + '_ {
275 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
276 let def = self.definition();
277
278 if def.fields().is_named() {
279 struct DebugStruct<'a, 'b> {
280 fmt: fmt::DebugStruct<'a, 'b>,
281 }
282
283 let mut debug = DebugStruct {
284 fmt: fmt.debug_struct(def.name()),
285 };
286
287 impl Visit for DebugStruct<'_, '_> {
288 fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) {
289 for (field, value) in named_values {
290 self.fmt.field(field.name(), value);
291 }
292 }
293
294 fn visit_value(&mut self, _: Value<'_>) {
295 unreachable!()
296 }
297 }
298
299 self.visit(&mut debug);
300
301 debug.fmt.finish()
302 } else {
303 struct DebugStruct<'a, 'b> {
304 fmt: fmt::DebugTuple<'a, 'b>,
305 }
306
307 let mut debug = DebugStruct {
308 fmt: fmt.debug_tuple(def.name()),
309 };
310
311 impl Visit for DebugStruct<'_, '_> {
312 fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) {
313 for value in values {
314 self.fmt.field(value);
315 }
316 }
317
318 fn visit_value(&mut self, _: Value<'_>) {
319 unreachable!();
320 }
321 }
322
323 self.visit(&mut debug);
324
325 debug.fmt.finish()
326 }
327 }
328}
329
330impl<'a> StructDef<'a> {
331 /// Create a new [`StructDef::Static`] instance.
332 ///
333 /// This should be used when a struct's fields are fixed and known ahead of time.
334 ///
335 /// # Examples
336 ///
337 /// ```
338 /// use valuable::{StructDef, Fields};
339 ///
340 /// let def = StructDef::new_static("Foo", Fields::Unnamed(2));
341 /// ```
342 pub const fn new_static(name: &'static str, fields: Fields<'static>) -> StructDef<'a> {
343 StructDef::Static { name, fields }
344 }
345
346 /// Create a new [`StructDef::Dynamic`] instance.
347 ///
348 /// This is used when the struct's fields may vary at runtime.
349 ///
350 /// # Examples
351 ///
352 /// ```
353 /// use valuable::{StructDef, Fields};
354 ///
355 /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(3));
356 /// ```
357 pub const fn new_dynamic(name: &'a str, fields: Fields<'a>) -> StructDef<'a> {
358 StructDef::Dynamic { name, fields }
359 }
360
361 /// Returns the struct's name
362 ///
363 /// # Examples
364 ///
365 /// With a static struct
366 ///
367 /// ```
368 /// use valuable::{StructDef, Fields};
369 ///
370 /// let def = StructDef::new_static("Foo", Fields::Unnamed(1));
371 /// assert_eq!("Foo", def.name());
372 /// ```
373 ///
374 /// With a dynamic struct
375 ///
376 /// ```
377 /// use valuable::{StructDef, Fields};
378 ///
379 /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(2));
380 /// assert_eq!("Foo", def.name());
381 /// ```
382 pub const fn name(&self) -> &'a str {
383 match self {
384 StructDef::Static { name, .. } => name,
385 StructDef::Dynamic { name, .. } => name,
386 }
387 }
388
389 /// Returns the struct's fields
390 ///
391 /// # Examples
392 ///
393 /// With a static struct
394 ///
395 /// ```
396 /// use valuable::{StructDef, Fields};
397 ///
398 /// let def = StructDef::new_static("Foo", Fields::Unnamed(3));
399 /// assert!(matches!(def.fields(), Fields::Unnamed(_)));
400 /// ```
401 ///
402 /// With a dynamic struct
403 ///
404 /// ```
405 /// use valuable::{StructDef, Fields};
406 ///
407 /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(1));
408 /// assert!(matches!(def.fields(), Fields::Unnamed(_)));
409 /// ```
410 pub const fn fields(&self) -> &Fields<'a> {
411 match self {
412 StructDef::Static { fields, .. } => fields,
413 StructDef::Dynamic { fields, .. } => fields,
414 }
415 }
416
417 /// Returns `true` if the struct is [statically defined](StructDef::Static).
418 ///
419 /// # Examples
420 ///
421 /// With a static struct
422 ///
423 /// ```
424 /// use valuable::{StructDef, Fields};
425 ///
426 /// let def = StructDef::new_static("Foo", Fields::Unnamed(2));
427 /// assert!(def.is_static());
428 /// ```
429 ///
430 /// With a dynamic struct
431 ///
432 /// ```
433 /// use valuable::{StructDef, Fields};
434 ///
435 /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(4));
436 /// assert!(!def.is_static());
437 /// ```
438 pub const fn is_static(&self) -> bool {
439 matches!(self, StructDef::Static { .. })
440 }
441
442 /// Returns `true` if the struct is [dynamically defined](StructDef::Dynamic).
443 ///
444 /// # Examples
445 ///
446 /// With a static struct
447 ///
448 /// ```
449 /// use valuable::{StructDef, Fields};
450 ///
451 /// let def = StructDef::new_static("Foo", Fields::Unnamed(1));
452 /// assert!(!def.is_dynamic());
453 /// ```
454 ///
455 /// With a dynamic struct
456 ///
457 /// ```
458 /// use valuable::{StructDef, Fields};
459 ///
460 /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(1));
461 /// assert!(def.is_dynamic());
462 /// ```
463 pub const fn is_dynamic(&self) -> bool {
464 matches!(self, StructDef::Dynamic { .. })
465 }
466}
467
468macro_rules! deref {
469 (
470 $(
471 $(#[$attrs:meta])*
472 $ty:ty,
473 )*
474 ) => {
475 $(
476 $(#[$attrs])*
477 impl<T: ?Sized + Structable> Structable for $ty {
478 fn definition(&self) -> StructDef<'_> {
479 T::definition(&**self)
480 }
481 }
482 )*
483 };
484}
485
486deref! {
487 &T,
488 &mut T,
489 #[cfg(feature = "alloc")]
490 alloc::boxed::Box<T>,
491 #[cfg(feature = "alloc")]
492 alloc::rc::Rc<T>,
493 #[cfg(not(valuable_no_atomic_cas))]
494 #[cfg(feature = "alloc")]
495 alloc::sync::Arc<T>,
496}