Skip to main content

forest/lotus_json/
vec.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use super::*;
5use get_size2::GetSize;
6
7impl<T> HasLotusJson for Vec<T>
8// TODO(forest): https://github.com/ChainSafe/forest/issues/4032
9//               This shouldn't recurse - LotusJson<Vec<T>> should only handle
10//               the OUTER issue of serializing an empty Vec as null, and
11//               shouldn't be interested in the inner representation.
12where
13    T: HasLotusJson + Clone,
14{
15    type LotusJson = Option<Vec<T::LotusJson>>;
16
17    #[cfg(test)]
18    fn snapshots() -> Vec<(serde_json::Value, Self)> {
19        unimplemented!("only Vec<Cid> is tested, below")
20    }
21
22    fn into_lotus_json(self) -> Self::LotusJson {
23        match self.is_empty() {
24            true => None,
25            false => Some(self.into_iter().map(T::into_lotus_json).collect()),
26        }
27    }
28
29    fn from_lotus_json(it: Self::LotusJson) -> Self {
30        match it {
31            Some(it) => it.into_iter().map(T::from_lotus_json).collect(),
32            None => vec![],
33        }
34    }
35}
36
37// an empty `Vec<T>` serializes into `null` lotus json by default,
38// while an empty `NotNullVec<T>` serializes into `[]`
39// this is a temporary workaround and will likely be deprecated once
40// other issues on serde of `Vec<T>` are resolved.
41#[derive(Debug, Clone, Default, PartialEq, JsonSchema, GetSize)]
42pub struct NotNullVec<T>(pub Vec<T>);
43
44impl<T> HasLotusJson for NotNullVec<T>
45where
46    T: HasLotusJson + Clone,
47{
48    type LotusJson = Vec<T::LotusJson>;
49
50    #[cfg(test)]
51    fn snapshots() -> Vec<(serde_json::Value, Self)> {
52        unimplemented!("only Vec<Cid> is tested, below")
53    }
54
55    fn into_lotus_json(self) -> Self::LotusJson {
56        self.0.into_iter().map(T::into_lotus_json).collect()
57    }
58
59    fn from_lotus_json(it: Self::LotusJson) -> Self {
60        Self(it.into_iter().map(T::from_lotus_json).collect())
61    }
62}
63
64/// Deserialize `Option<NotNullVec<T>>`, mapping `null` and empty `[]` to `None`.
65///
66/// Generic `HasLotusJson for Option<T>` keeps `Some(NotNullVec([]))` on `[]`;
67/// Lotus sends `null` for empty access lists and we treat both as absent.
68pub fn deserialize_empty_not_null_opt<'de, T, D>(
69    deserializer: D,
70) -> Result<Option<NotNullVec<T>>, D::Error>
71where
72    T: Deserialize<'de>,
73    D: Deserializer<'de>,
74{
75    Ok(Option::<Vec<T>>::deserialize(deserializer)?
76        .filter(|l| !l.is_empty())
77        .map(NotNullVec))
78}
79
80#[test]
81fn snapshots() {
82    assert_one_snapshot(json!([{"/": "baeaaaaa"}]), vec![::cid::Cid::default()]);
83}
84
85#[cfg(test)]
86#[quickcheck_macros::quickcheck]
87fn quickcheck(val: Vec<::cid::Cid>) {
88    assert_unchanged_via_json(val)
89}