specta_util/array.rs
1use std::marker::PhantomData;
2
3use specta::{
4 Type, Types,
5 datatype::{DataType, List},
6};
7
8/// Declares a fixed-length array type for Specta exporters.
9///
10/// This is primarily useful with `#[specta(type = ...)]` when you want an array
11/// field to keep its length information in exported schemas.
12///
13/// A plain Rust array like `[u8; 2]` may be exported as `number[]` if Specta
14/// deems it's unable to safely export it as `[number, number]`. This limitation
15/// is due to the fact that Specta can't track the inference of const generics and
16/// hence can't fully support them. Using `FixedArray<N, T>` will always encode the
17/// length so it can be used to force override Specta's conservative behaviour when you know what your doing.
18///
19/// ```ignore
20/// use specta::Type;
21///
22/// /// #[derive(Type)]
23/// struct DemoA {
24/// a: [u8; 2], // becomes `[number, number]`
25///
26/// #[specta(type = specta_util::FixedArray<2, u8>)]
27/// d: [u8; 2], // becomes `[number, number]`
28/// }
29///
30/// #[derive(Type)]
31/// struct DemoB<const N: usize = 1> {
32/// // These are generalised by Specta as we can't know if a specific type is using `N` or a constant, and we don't know what `N` is.
33/// // If you `#[specta(inline)]` or `#[serde(flatten)]` the `[number, number]` will be restored as we are able to track it properly.
34/// data: [u32; N], // becomes `number[]`
35/// a: [u8; 2], // becomes `number[]`
36///
37/// #[specta(type = specta_util::FixedArray<2, u8>)]
38/// d: [u8; 2], // becomes `[number, number]`
39/// }
40/// ```
41pub struct FixedArray<const N: usize, T: Type>(PhantomData<[T; N]>);
42
43impl<const N: usize, T: Type> Type for FixedArray<N, T> {
44 fn definition(types: &mut Types) -> DataType {
45 let mut l = List::new(T::definition(types));
46 // Refer to the type documentation for the safety around this.
47 l.length = Some(N);
48 DataType::List(l)
49 }
50}