Skip to content

Commit 6512c6c

Browse files
Address feedback
1 parent f91e6f9 commit 6512c6c

File tree

2 files changed

+65
-34
lines changed

2 files changed

+65
-34
lines changed

rs/nns/governance/src/pb/proposal_conversions.rs

Lines changed: 62 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -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

297332
enum 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

334356
fn 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

359381
fn 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)]
404433
impl 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 {

rs/nns/governance/src/proposals/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ impl ValidProposalAction {
234234
ValidProposalAction::FulfillSubnetRentalRequest(fulfill_subnet_rental_request) => {
235235
Ok(fulfill_subnet_rental_request.to_self_describing_action())
236236
}
237+
ValidProposalAction::CreateServiceNervousSystem(create_service_nervous_system) => {
238+
Ok(create_service_nervous_system.to_self_describing_action())
239+
}
237240
_ => Err(GovernanceError::new_with_message(
238241
ErrorType::InvalidProposal,
239242
"Self describing proposal actions are not supported for this proposal action yet.",

0 commit comments

Comments
 (0)