Skip to content

Commit 72d8a67

Browse files
Require auto_index attribute to enable automatic index assigment
Automatically assigning indices based on the order of the fields is error prone, for example when used with feature flags, when fields are skipped or when the fields of the struct are changed or reordered. With this patch, we require an explicit opt-in to this potentially problematic behavior. See also: #17
1 parent 031c0b2 commit 72d8a67

File tree

4 files changed

+26
-10
lines changed

4 files changed

+26
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
[Unreleased]: https://github.com/trussed-dev/serde-indexed/compare/0.1.1...HEAD
66

7-
-
7+
- Prefer explicit indexing over automatically assigned indices:
8+
- Require `auto_index` attribute to enable automatic index assignment
89

910
## [v0.1.1][] (2024-04-03)
1011

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ and a custom `offset` container attribute.
88
use serde_indexed::{DeserializeIndexed, SerializeIndexed};
99
1010
#[derive(Clone, Debug, PartialEq, SerializeIndexed, DeserializeIndexed)]
11-
#[serde_indexed(offset = 1)]
11+
#[serde_indexed(auto_index, offset = 1)]
1212
pub struct SomeKeys {
1313
pub number: i32,
1414
#[serde(skip_serializing_if = "Option::is_none")]

src/parse.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub struct Input {
1212

1313
#[derive(Default)]
1414
pub struct StructAttrs {
15+
pub auto_index: bool,
1516
pub offset: usize,
1617
// pub skip_nones: bool,
1718
}
@@ -24,7 +25,10 @@ pub struct Field {
2425
}
2526

2627
fn parse_meta(attrs: &mut StructAttrs, meta: ParseNestedMeta) -> Result<()> {
27-
if meta.path.is_ident("offset") {
28+
if meta.path.is_ident("auto_index") {
29+
attrs.auto_index = true;
30+
Ok(())
31+
} else if meta.path.is_ident("offset") {
2832
let value = meta.value()?;
2933
let offset: LitInt = value.parse()?;
3034
attrs.offset = offset.base10_parse()?;
@@ -75,7 +79,7 @@ impl Parse for Input {
7579
}
7680
};
7781

78-
let fields = fields_from_ast(&syn_fields.named)?;
82+
let fields = fields_from_ast(&attrs, &syn_fields.named)?;
7983

8084
//serde::internals::ast calls `fields_from_ast(cx, &fields.named, attrs, container_default)`
8185

@@ -89,8 +93,16 @@ impl Parse for Input {
8993
}
9094

9195
fn fields_from_ast(
96+
attrs: &StructAttrs,
9297
fields: &syn::punctuated::Punctuated<syn::Field, Token![,]>,
9398
) -> Result<Vec<Field>> {
99+
if !attrs.auto_index {
100+
return Err(Error::new_spanned(
101+
fields,
102+
"auto_index attribute must be set",
103+
));
104+
}
105+
94106
// serde::internals::ast.rs:L183
95107
fields
96108
.iter()

tests/basics.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ mod some_keys {
4343
use serde_bytes::Bytes;
4444

4545
#[derive(Clone, Debug, PartialEq, SerializeIndexed, DeserializeIndexed)]
46-
#[serde_indexed(offset = 1)]
46+
#[serde_indexed(auto_index, offset = 1)]
4747
pub struct SomeKeys {
4848
pub number: i32,
4949
pub bytes: [u8; 7],
@@ -54,7 +54,7 @@ mod some_keys {
5454
}
5555

5656
#[derive(Clone, Debug, PartialEq, SerializeIndexed, DeserializeIndexed)]
57-
#[serde_indexed(offset = 1)]
57+
#[serde_indexed(auto_index, offset = 1)]
5858
pub struct SomeRefKeys<'a, 'b, 'c> {
5959
pub number: i32,
6060
pub bytes: &'a ByteArray<7>,
@@ -66,6 +66,7 @@ mod some_keys {
6666

6767
#[derive(Clone, Debug, PartialEq, SerializeIndexed, DeserializeIndexed)]
6868
// #[serde_indexed(offset = 1)]
69+
#[serde_indexed(auto_index)]
6970
pub struct NakedOption {
7071
pub option: Option<SomeKeys>,
7172
pub num: usize,
@@ -74,6 +75,7 @@ mod some_keys {
7475

7576
#[derive(Clone, Debug, PartialEq, SerializeIndexed, DeserializeIndexed)]
7677
// #[serde_indexed(offset = 1)]
78+
#[serde_indexed(auto_index)]
7779
pub struct NakedRefOption<'a, 'b, 'c> {
7880
pub option: Option<SomeRefKeys<'a, 'b, 'c>>,
7981
pub num: usize,
@@ -82,6 +84,7 @@ mod some_keys {
8284

8385
#[derive(Clone, Debug, PartialEq, SerializeIndexed, DeserializeIndexed)]
8486
// #[serde_indexed(offset = 1)]
87+
#[serde_indexed(auto_index)]
8588
pub struct EmptyStruct {}
8689

8790
fn an_example() -> (&'static [u8], SomeKeys) {
@@ -317,7 +320,7 @@ mod cow {
317320
use std::borrow::Cow;
318321

319322
#[derive(PartialEq, Debug, SerializeIndexed, DeserializeIndexed)]
320-
#[serde_indexed(offset = 1)]
323+
#[serde_indexed(auto_index, offset = 1)]
321324
struct WithLifetimes<'a> {
322325
data: Cow<'a, [u8]>,
323326
#[serde(skip_serializing_if = "Option::is_none")]
@@ -381,7 +384,7 @@ mod generics {
381384
use serde_bytes::Bytes;
382385

383386
#[derive(PartialEq, Debug, SerializeIndexed, DeserializeIndexed)]
384-
#[serde_indexed(offset = 1)]
387+
#[serde_indexed(auto_index, offset = 1)]
385388
struct WithGeneric<T> {
386389
data: T,
387390
#[serde(skip_serializing_if = "Option::is_none")]
@@ -396,7 +399,7 @@ mod generics {
396399
}
397400

398401
#[derive(PartialEq, Debug, SerializeIndexed, DeserializeIndexed)]
399-
#[serde_indexed(offset = 1)]
402+
#[serde_indexed(auto_index, offset = 1)]
400403
struct WithConstGeneric<const N: usize> {
401404
data: ByteArray<N>,
402405
#[serde(skip_serializing_if = "Option::is_none")]
@@ -445,7 +448,7 @@ mod generics {
445448
}
446449

447450
#[derive(PartialEq, Debug, SerializeIndexed, DeserializeIndexed)]
448-
#[serde_indexed(offset = 1)]
451+
#[serde_indexed(auto_index, offset = 1)]
449452
struct WithAllGenerics<'a, 'b, T, I, const N: usize, const Z: usize> {
450453
data1: heapless::Vec<T, N>,
451454
data2: heapless::Vec<I, Z>,

0 commit comments

Comments
 (0)