Skip to content

Commit

Permalink
chore(candid-utils): Factor out Candid pretty printing function (#3572)
Browse files Browse the repository at this point in the history
This PR moves Candid pretty printing into a separate function
`printing::pretty`, adding a few unit tests to specify its expected
behavior.

This function will be used in a new crate that will be added in a
follow-up PR.

< [Previous PR](#3488) | [Next
PR](#3439) >
  • Loading branch information
aterga authored Jan 23, 2025
1 parent 44d54e5 commit 850e959
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 5 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion rs/nervous_system/candid_utils/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ DEPENDENCIES = [

MACRO_DEPENDENCIES = []

DEV_DEPENDENCIES = []
DEV_DEPENDENCIES = [
"@crate_index//:pretty_assertions",
]

MACRO_DEV_DEPENDENCIES = []

Expand Down
3 changes: 3 additions & 0 deletions rs/nervous_system/candid_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ path = "src/lib.rs"
[dependencies]
candid = { workspace = true }
candid_parser = { workspace = true }

[dev-dependencies]
pretty_assertions = { workspace = true }
1 change: 1 addition & 0 deletions rs/nervous_system/candid_utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod printing;
pub mod validation;
44 changes: 44 additions & 0 deletions rs/nervous_system/candid_utils/src/printing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use candid::{CandidType, IDLValue};

/// Pretty print `value` into a Candid string.
///
/// Example:
///
/// ```rust
/// #[derive(CandidType)]
/// struct DummyCandidStruct {
/// pub status: Option<i32>,
/// pub module_hash: Vec<u8>,
/// pub controllers: String,
/// pub memory_size: Option<u64>,
/// pub cycles: Option<u64>,
/// }
///
/// let dummy = DummyCandidStruct {
/// status: Some(42),
/// module_hash: vec![1, 2, 3, 4],
/// controllers: "foo".to_string(),
/// memory_size: Some(100),
/// cycles: Some(123),
/// };
/// println!("{}", pretty(&dummy));
/// ```
///
/// Output:
/// ```candid
/// record {
/// status = opt (42 : int32);
/// controllers = "foo";
/// memory_size = opt (100 : nat64);
/// cycles = opt (123 : nat64);
/// module_hash = blob "\01\02\03\04";
/// }
/// ```
pub fn pretty<T: CandidType>(value: &T) -> Result<String, String> {
let value = IDLValue::try_from_candid_type(value).map_err(|err| err.to_string())?;

Ok(value.to_string())
}

#[cfg(test)]
mod tests;
76 changes: 76 additions & 0 deletions rs/nervous_system/candid_utils/src/printing/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use super::*;
use pretty_assertions::{assert_eq, assert_str_eq};

#[derive(CandidType)]
struct DummyCandidStruct {
pub status: Option<i32>,
pub module_hash: Vec<u8>,
pub controllers: String,
pub memory_size: Option<u64>,
pub cycles: Option<u64>,
}

#[derive(CandidType)]
enum DummyCandidVariant {
Foo(String),
Bar { abc: String, xyz: String },
}

#[derive(CandidType)]
struct DummyCandidContainer {
foo: DummyCandidVariant,
bar: Result<DummyCandidVariant, String>,
}

#[track_caller]
fn assert_expectation<T: CandidType>(value: &T, expected_result: Result<String, String>) {
let observed_result = pretty(value);

match (observed_result, expected_result) {
(Ok(observed), Ok(expected)) => {
assert_str_eq!(observed, expected);
}
(observed, expected) => {
assert_eq!(observed, expected);
}
}
}

#[test]
fn test_pretty_printing_simple_struct() {
assert_expectation(
&DummyCandidStruct {
status: Some(42),
module_hash: vec![1, 2, 3, 4],
controllers: "foo".to_string(),
memory_size: Some(100),
cycles: Some(123),
},
Ok(r#"record {
status = opt (42 : int32);
controllers = "foo";
memory_size = opt (100 : nat64);
cycles = opt (123 : nat64);
module_hash = blob "\01\02\03\04";
}"#
.to_string()),
);
}

#[test]
fn test_pretty_printing_complex_struct() {
assert_expectation(
&DummyCandidContainer {
foo: DummyCandidVariant::Foo("hello".to_string()),
bar: Ok(DummyCandidVariant::Bar {
abc: "abc".to_string(),
xyz: "xyz".to_string(),
}),
},
Ok(r#"record {
bar = variant { Ok = variant { Bar = record { abc = "abc"; xyz = "xyz" } } };
foo = variant { Foo = "hello" };
}"#
.to_string()),
);
}
1 change: 1 addition & 0 deletions rs/sns/cli/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ DEPENDENCIES = [
# Keep sorted.
"//rs/crypto/sha2",
"//rs/nervous_system/agent",
"//rs/nervous_system/candid_utils",
"//rs/nervous_system/common",
"//rs/nervous_system/common/test_keys",
"//rs/nervous_system/humanize",
Expand Down
1 change: 1 addition & 0 deletions rs/sns/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ path = "src/lib.rs"
anyhow = { workspace = true }
base64 = { workspace = true }
candid = { workspace = true }
candid-utils = { path = "../../nervous_system/candid_utils" }
clap = { workspace = true }
futures = { workspace = true }
hex = { workspace = true }
Expand Down
6 changes: 2 additions & 4 deletions rs/sns/cli/src/neuron_id_to_candid_subaccount.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::str::FromStr;

use anyhow::{anyhow, Result};
use candid::IDLValue;
use candid_utils::printing;
use clap::Parser;
use ic_sns_governance::pb::v1::NeuronId;

Expand Down Expand Up @@ -37,9 +37,7 @@ pub fn neuron_id_to_subaccount(args: NeuronIdToCandidSubaccountArgs) -> Result<S
.to_vec();

// We'll convert it to a candid string.
let idl = IDLValue::try_from_candid_type(&subaccount)
.unwrap()
.to_string();
let idl = printing::pretty(&subaccount).unwrap();

if args.escaped {
Ok(idl.replace('\\', "\\\\").replace('\"', "\\\""))
Expand Down

0 comments on commit 850e959

Please sign in to comment.