Skip to content

Commit 9bed600

Browse files
committed
ser: sequences: Rework, fixing handling of nested arrays
Change the representation of our "current location". Previously it was a list of increasingly-long full paths, but excepting the putative final array index. Now we just record the individual elements. and assemble the whole path just before calling .set(). This saves a little memory on the whole since the entries in keys are now a bit shorter. It is much less confusing. (There are perhaps still further opportunities to use Rust's type system to better advantage to eliminuate opportunities for bugs.) Arrays that appear other than at the top level are now handled correctly. This includes nested arrays, and arrays containing structs, etc. Signed-off-by: Ian Jackson <[email protected]> (cherry picked from commit 831102f) Signed-off-by: Ian Jackson <[email protected]>
1 parent 43ac661 commit 9bed600

File tree

1 file changed

+55
-49
lines changed

1 file changed

+55
-49
lines changed

src/ser.rs

+55-49
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::fmt::Display;
2+
use std::fmt::Write as _;
23

34
use serde::ser;
45

@@ -8,79 +9,73 @@ use crate::Config;
89

910
#[derive(Default, Debug)]
1011
pub struct ConfigSerializer {
11-
keys: Vec<(String, Option<usize>)>,
12+
keys: Vec<SerKey>,
1213
pub output: Config,
1314
}
1415

16+
#[derive(Debug)]
17+
enum SerKey {
18+
Named(String),
19+
Seq(usize),
20+
}
21+
22+
/// Serializer for numbered sequences
23+
///
24+
/// This wrapper is present when we are outputting a sequence (numbered indices).
25+
/// Making this a separate type centralises the handling of sequences
26+
/// and ensures we don't have any call sites for `ser::SerializeSeq::serialize_element`
27+
/// that don't do the necessary work of `SeqSerializer::new`.
28+
///
29+
/// Existence of this wrapper implies that `.0.keys.last()` is
30+
/// `Some(SerKey::Seq(next_index))`.
1531
pub struct SeqSerializer<'a>(&'a mut ConfigSerializer);
1632

1733
impl ConfigSerializer {
1834
fn serialize_primitive<T>(&mut self, value: T) -> Result<()>
1935
where
2036
T: Into<Value> + Display,
2137
{
22-
let key = match self.last_key_index_pair() {
23-
Some((key, Some(index))) => format!("{}[{}]", key, index),
24-
Some((key, None)) => key.to_string(),
25-
None => {
26-
return Err(ConfigError::Message(format!(
27-
"key is not found for value {}",
28-
value
29-
)))
30-
}
31-
};
38+
// At some future point we could perhaps retain a cursor into the output `Config`,
39+
// rather than reifying the whole thing into a single string with `make_full_key`
40+
// and passing that whole path to the `set` method.
41+
//
42+
// That would be marginally more performant, but more fiddly.
43+
let key = self.make_full_key()?;
3244

3345
#[allow(deprecated)]
3446
self.output.set(&key, value.into())?;
3547
Ok(())
3648
}
3749

38-
fn last_key_index_pair(&self) -> Option<(&str, Option<usize>)> {
39-
let len = self.keys.len();
40-
if len > 0 {
41-
self.keys
42-
.get(len - 1)
43-
.map(|&(ref key, opt)| (key.as_str(), opt))
44-
} else {
45-
None
46-
}
47-
}
50+
fn make_full_key(&self) -> Result<String> {
51+
let mut keys = self.keys.iter();
4852

49-
fn inc_last_key_index(&mut self) -> Result<()> {
50-
let len = self.keys.len();
51-
if len > 0 {
52-
self.keys
53-
.get_mut(len - 1)
54-
.map(|pair| pair.1 = pair.1.map(|i| i + 1).or(Some(0)))
55-
.ok_or_else(|| {
56-
ConfigError::Message(format!("last key is not found in {} keys", len))
57-
})
58-
} else {
59-
Err(ConfigError::Message("keys is empty".to_string()))
60-
}
61-
}
53+
let mut whole = match keys.next() {
54+
Some(SerKey::Named(s)) => s.clone(),
55+
_ => {
56+
return Err(ConfigError::Message(
57+
"top level is not a struct".to_string(),
58+
))
59+
}
60+
};
6261

63-
fn make_full_key(&self, key: &str) -> String {
64-
let len = self.keys.len();
65-
if len > 0 {
66-
if let Some(&(ref prev_key, index)) = self.keys.get(len - 1) {
67-
return if let Some(index) = index {
68-
format!("{}[{}].{}", prev_key, index, key)
69-
} else {
70-
format!("{}.{}", prev_key, key)
71-
};
62+
for k in keys {
63+
match k {
64+
SerKey::Named(s) => write!(whole, ".{}", s),
65+
SerKey::Seq(i) => write!(whole, "[{}]", i),
7266
}
67+
.expect("write! to a string failed");
7368
}
74-
key.to_string()
69+
70+
Ok(whole)
7571
}
7672

7773
fn push_key(&mut self, key: &str) {
78-
let full_key = self.make_full_key(key);
79-
self.keys.push((full_key, None));
74+
self.keys.push(SerKey::Named(key.to_string()));
8075
}
8176

82-
fn pop_key(&mut self) -> Option<(String, Option<usize>)> {
83-
self.keys.pop()
77+
fn pop_key(&mut self) {
78+
self.keys.pop();
8479
}
8580
}
8681

@@ -265,10 +260,14 @@ impl<'a> ser::Serializer for &'a mut ConfigSerializer {
265260

266261
impl<'a> SeqSerializer<'a> {
267262
fn new(inner: &'a mut ConfigSerializer) -> Result<Self> {
263+
inner.keys.push(SerKey::Seq(0));
264+
268265
Ok(SeqSerializer(inner))
269266
}
270267

271268
fn end(self) -> &'a mut ConfigSerializer {
269+
// This ought to be Some(SerKey::Seq(..)) but we don't want to panic if we are buggy
270+
let _: Option<SerKey> = self.0.keys.pop();
272271
self.0
273272
}
274273
}
@@ -281,8 +280,15 @@ impl<'a> ser::SerializeSeq for SeqSerializer<'a> {
281280
where
282281
T: ?Sized + ser::Serialize,
283282
{
284-
self.0.inc_last_key_index()?;
285283
value.serialize(&mut *(self.0))?;
284+
match self.0.keys.last_mut() {
285+
Some(SerKey::Seq(i)) => *i += 1,
286+
_ => {
287+
return Err(ConfigError::Message(
288+
"config-rs internal error (ser._element but last not Seq!".to_string(),
289+
))
290+
}
291+
};
286292
Ok(())
287293
}
288294

0 commit comments

Comments
 (0)