@@ -275,10 +275,21 @@ fn convert_self_describing_action(
275275
276276 let type_name = Some ( type_name. clone ( ) ) ;
277277 let type_description = Some ( type_description. clone ( ) ) ;
278+
279+ let paths_to_omit = if omit_create_service_nervous_system_logos {
280+ vec ! [
281+ RecordPath { fields_names: vec![ "logo" ] } ,
282+ RecordPath {
283+ fields_names: vec![ "ledger_parameters" , "token_logo" ] ,
284+ } ,
285+ ]
286+ } else {
287+ vec ! [ ]
288+ } ;
278289 let value = value. as_ref ( ) . map ( |value| {
279290 convert_self_describing_value (
280291 value,
281- & PathToOmit :: default_paths_to_omit ( omit_create_service_nervous_system_logos ) ,
292+ paths_to_omit ,
282293 )
283294 } ) ;
284295
@@ -289,33 +300,44 @@ fn convert_self_describing_action(
289300 }
290301}
291302
292- struct PathToOmit {
303+
304+ /// For example, suppose we have
305+ ///
306+ /// ```
307+ /// struct Plane {
308+ /// wing: Wing,
309+ /// landing_gear: LandingGear,
310+ /// }
311+ ///
312+ /// struct LandingGear {
313+ /// wheel: Wheel,
314+ /// }
315+ ///
316+ /// struct Wheel {
317+ /// tire: Tire,
318+ /// }
319+ ///
320+ /// let plane: Plane = ...;
321+ /// ```
322+ ///
323+ /// To reach the `Tire` from `plane`, we can use the path
324+ /// `vec!["landing_gear", "wheel", "tire"]`, because `plane.landing_gear.wheel.tire`
325+ /// evaluates to the `Tire`.
326+ #[ derive( Clone ) ]
327+ struct RecordPath {
293328 // Must have at least one element.
294- path : Vec < & ' static str > ,
329+ fields_names : Vec < & ' static str > ,
295330}
296331
297332enum OmitAction {
298333 DoNothing ,
299334 OmitCurrent ,
300- OmitDescendant ( PathToOmit ) ,
335+ OmitDescendant ( RecordPath ) ,
301336}
302337
303- impl PathToOmit {
304- fn default_paths_to_omit ( omit_create_service_nervous_system_logos : bool ) -> Vec < Self > {
305- if omit_create_service_nervous_system_logos {
306- vec ! [
307- Self { path: vec![ "logo" ] } ,
308- Self {
309- path: vec![ "ledger_parameters" , "token_logo" ] ,
310- } ,
311- ]
312- } else {
313- vec ! [ ]
314- }
315- }
316-
338+ impl RecordPath {
317339 fn matches ( & self , field_name : & str ) -> OmitAction {
318- let Some ( ( & first, rest) ) = self . path . split_first ( ) else {
340+ let Some ( ( & first, rest) ) = self . fields_names . split_first ( ) else {
319341 // This should never happen, but we handle it anyway.
320342 return OmitAction :: DoNothing ;
321343 } ;
@@ -325,15 +347,15 @@ impl PathToOmit {
325347 if rest. is_empty ( ) {
326348 return OmitAction :: OmitCurrent ;
327349 }
328- OmitAction :: OmitDescendant ( PathToOmit {
329- path : rest. to_vec ( ) ,
350+ OmitAction :: OmitDescendant ( RecordPath {
351+ fields_names : rest. to_vec ( ) ,
330352 } )
331353 }
332354}
333355
334356fn convert_self_describing_field (
335357 field_name : & str ,
336- paths_to_omit : & [ PathToOmit ] ,
358+ paths_to_omit : Vec < RecordPath > ,
337359 original_value : & pb:: SelfDescribingValue ,
338360) -> pb_api:: SelfDescribingValue {
339361 let match_results = paths_to_omit
@@ -353,16 +375,22 @@ fn convert_self_describing_field(
353375 _ => None ,
354376 } )
355377 . collect :: < Vec < _ > > ( ) ;
356- convert_self_describing_value ( original_value, & descendant_paths_to_omit)
378+ convert_self_describing_value ( original_value, descendant_paths_to_omit)
357379}
358380
359381fn convert_self_describing_value (
360382 item : & pb:: SelfDescribingValue ,
361- paths_to_omit : & [ PathToOmit ] ,
383+ paths_to_omit : Vec < RecordPath > ,
362384) -> pb_api:: SelfDescribingValue {
363- let pb:: SelfDescribingValue { value : Some ( value) } = item else {
385+ let pb:: SelfDescribingValue { value } = item;
386+
387+ // This should be unreacheable, because we always construct a SelfDescribingValue with a value.
388+ // Ideally the type should be non-optional, but prost always generates an optional field for
389+ // messages.
390+ let Some ( value) = value else {
364391 return pb_api:: SelfDescribingValue :: Map ( HashMap :: new ( ) ) ;
365392 } ;
393+
366394 match value {
367395 pb:: self_describing_value:: Value :: Blob ( v) => pb_api:: SelfDescribingValue :: Blob ( v. clone ( ) ) ,
368396 pb:: self_describing_value:: Value :: Text ( v) => pb_api:: SelfDescribingValue :: Text ( v. clone ( ) ) ,
@@ -378,32 +406,33 @@ fn convert_self_describing_value(
378406 pb:: self_describing_value:: Value :: Array ( v) => pb_api:: SelfDescribingValue :: Array (
379407 v. values
380408 . iter ( )
381- . map ( |value| convert_self_describing_value ( value, & [ ] ) )
409+ . map ( |value| convert_self_describing_value ( value, vec ! [ ] ) )
382410 . collect ( ) ,
383411 ) ,
412+
413+ // This is where `paths_to_omit` takes effect - the resursion (calling
414+ // `convert_self_describing_value` happens indirectly through
415+ // `convert_self_describing_field`, which calls `convert_self_describing_value` if the field
416+ // should not be omitted.
384417 pb:: self_describing_value:: Value :: Map ( v) => pb_api:: SelfDescribingValue :: Map (
385418 v. values
386419 . iter ( )
387420 . map ( |( k, v) | {
388421 (
389422 k. clone ( ) ,
390- convert_self_describing_field ( k, paths_to_omit, v) ,
423+ convert_self_describing_field ( k, paths_to_omit. clone ( ) , v) ,
391424 )
392425 } )
393426 . collect ( ) ,
394427 ) ,
395428 }
396429}
397430
398- // This is only used for tests, because the `pb::SelfDescribingValue` can be large, and a direct
399- // conversion would most likely involve cloning the source value. Previously, we had an issue where
400- // it caused too many instructions due to the cloning, even when the large fields were omitted after
401- // cloning. The `cfg(test)` can be removed, however, if we decide to use SelfDescribingValue in
402- // other scenarios where there is less concern about cloning large fields. Ideally, we would
431+ // To avoid cloning large values in production, this is only available in tests.
403432#[ cfg( test) ]
404433impl From < pb:: SelfDescribingValue > for pb_api:: SelfDescribingValue {
405434 fn from ( value : pb:: SelfDescribingValue ) -> Self {
406- convert_self_describing_value ( & value, & [ ] )
435+ convert_self_describing_value ( & value, vec ! [ ] )
407436 }
408437}
409438
@@ -674,7 +703,6 @@ mod tests {
674703
675704 #[ test]
676705 fn test_self_describing_value_omit_logos ( ) {
677- // Not all fields are included in the test, for simplicity.
678706 let create_service_nervous_system = CreateServiceNervousSystem {
679707 name : Some ( "some name" . to_string ( ) ) ,
680708 logo : Some ( Image {
0 commit comments