spreadsheet_kit/
response_prune.rs1use anyhow::Result;
2use schemars::{JsonSchema, Schema, SchemaGenerator};
3use serde::Serialize;
4use serde::ser::Error as _;
5use serde_json::Value;
6use std::borrow::Cow;
7
8pub fn to_pruned_value<T: Serialize>(value: &T) -> Result<Value> {
9 let mut json = serde_json::to_value(value)?;
10 prune_non_structural_empties(&mut json);
11 Ok(json)
12}
13
14#[derive(Debug, Clone)]
15pub struct Pruned<T>(pub T);
16
17impl<T: Serialize> Serialize for Pruned<T> {
18 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
19 where
20 S: serde::Serializer,
21 {
22 let mut value = serde_json::to_value(&self.0).map_err(S::Error::custom)?;
23 prune_non_structural_empties(&mut value);
24 value.serialize(serializer)
25 }
26}
27
28impl<T: JsonSchema> JsonSchema for Pruned<T> {
29 fn schema_name() -> Cow<'static, str> {
30 T::schema_name()
31 }
32
33 fn json_schema(generator: &mut SchemaGenerator) -> Schema {
34 T::json_schema(generator)
35 }
36}
37
38fn preserve_empty_array_for_key(key: &str) -> bool {
39 matches!(
40 key,
41 "warnings"
42 | "matches"
43 | "target_deltas"
44 | "changed_targets"
45 | "new_errors"
46 | "resolved_errors"
47 | "preexisting_errors"
48 | "named_range_deltas"
49 | "formula_targets"
50 | "likely_patch_targets"
51 | "adjacent_sum_targets"
52 | "merged_ranges_fully_contained"
53 | "merged_ranges_crossing_boundary"
54 )
55}
56
57pub fn prune_non_structural_empties(value: &mut Value) {
58 match value {
59 Value::Object(map) => {
60 let mut remove_keys = Vec::new();
61 for (key, child) in map.iter_mut() {
62 prune_non_structural_empties(child);
63 if child.is_null() {
64 remove_keys.push(key.clone());
65 continue;
66 }
67 if let Value::Object(obj) = child
68 && obj.is_empty()
69 {
70 remove_keys.push(key.clone());
71 continue;
72 }
73 if let Value::Array(arr) = child
74 && arr.is_empty()
75 && !preserve_empty_array_for_key(key)
76 {
77 remove_keys.push(key.clone());
78 }
79 }
80 for key in remove_keys {
81 map.remove(&key);
82 }
83 }
84 Value::Array(items) => {
85 for child in items.iter_mut() {
86 prune_non_structural_empties(child);
87 }
88 }
89 _ => {}
90 }
91}