diff --git a/rust/perspective-client/src/rust/config/view_config.rs b/rust/perspective-client/src/rust/config/view_config.rs index fa4f36e..360b816 100644 --- a/rust/perspective-client/src/rust/config/view_config.rs +++ b/rust/perspective-client/src/rust/config/view_config.rs @@ -526,6 +526,23 @@ impl ViewConfig { } } + /// `_apply` for a field which is itself `Option`, where `None` in the update + /// means "not mentioned" rather than "clear it". `Option>` would be + /// needed to express both, and the wire format cannot carry the difference: + /// these fields are `skip_serializing_if = "Option::is_none"`, so an absent + /// field and an explicit null arrive identically. To lift a depth, send the + /// number of levels on that axis rather than clearing it. + fn _apply_optional(field: &mut Option, update: Option) -> bool { + match update { + None => false, + Some(_) if *field == update => false, + Some(_) => { + *field = update; + true + }, + } + } + pub fn reset(&mut self, reset_expressions: bool) { let mut config = Self::default(); if !reset_expressions { @@ -568,6 +585,16 @@ impl ViewConfig { changed = Self::_apply(&mut self.windows, update.windows) || changed; changed = Self::_apply(&mut self.group_rollup_mode, update.group_rollup_mode) || changed; changed = Self::_apply(&mut self.split_rollup_mode, update.split_rollup_mode) || changed; + + // Without these two, a depth can be set when a view is created -- + // `table.view({ group_by_depth })` -- but never through `restore()`, which + // merges a `ViewConfigUpdate` onto the live config. The field arrives, + // deserializes, and is then dropped here, so the viewer's config and the + // engine never see it and nothing happens. That makes expand/collapse + // unreachable for anything driven by `restore()`, which is how a viewer + // changes its own configuration. + changed = Self::_apply_optional(&mut self.group_by_depth, update.group_by_depth) || changed; + changed = Self::_apply_optional(&mut self.split_by_depth, update.split_by_depth) || changed; if self.group_rollup_mode == GroupRollupMode::Total && !self.group_by.is_empty() { tracing::info!("`total` incompatible with `group_by`"); changed = true;