Skip to content

Commit

Permalink
feat(cast): add flag equivalents of parseUnits, formatUnits (#9165)
Browse files Browse the repository at this point in the history
* feat: base func for parseunints and formatuints

* test: update doc and tests

* fix: function alias

* test: cast test

* refacctor: helper fucnction

* Update crates/cast/tests/cli/main.rs

* revert: format uints function cattest func name

---------

Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
  • Loading branch information
kien6034 and grandizzy authored Oct 24, 2024
1 parent 6b4ad0d commit bcef905
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 3 deletions.
32 changes: 32 additions & 0 deletions crates/cast/bin/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,38 @@ pub enum CastSubcommand {
unit: String,
},

/// Convert a number from decimal to smallest unit with arbitrary decimals.
///
/// Examples:
/// - 1.0 6 (for USDC, result: 1000000)
/// - 2.5 12 (for 12 decimals token, result: 2500000000000)
/// - 1.23 3 (for 3 decimals token, result: 1230)
#[command(visible_aliases = &["--parse-units", "pun"])]
ParseUnits {
/// The value to convert.
value: Option<String>,

/// The unit to convert to.
#[arg(default_value = "18")]
unit: u8,
},

/// Format a number from smallest unit to decimal with arbitrary decimals.
///
/// Examples:
/// - 1000000 6 (for USDC, result: 1.0)
/// - 2500000000000 12 (for 12 decimals, result: 2.5)
/// - 1230 3 (for 3 decimals, result: 1.23)
#[command(visible_aliases = &["--format-units", "fun"])]
FormatUnits {
/// The value to format.
value: Option<String>,

/// The unit to format to.
#[arg(default_value = "18")]
unit: u8,
},

/// Convert an ETH amount to wei.
///
/// Consider using --to-unit.
Expand Down
8 changes: 8 additions & 0 deletions crates/cast/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ async fn main_args(args: CastArgs) -> Result<()> {
let value = stdin::unwrap_line(value)?;
sh_println!("{}", SimpleCast::to_unit(&value, &unit)?)?
}
CastSubcommand::ParseUnits { value, unit } => {
let value = stdin::unwrap_line(value)?;
println!("{}", SimpleCast::parse_units(&value, unit)?);
}
CastSubcommand::FormatUnits { value, unit } => {
let value = stdin::unwrap_line(value)?;
println!("{}", SimpleCast::format_units(&value, unit)?);
}
CastSubcommand::FromWei { value, unit } => {
let value = stdin::unwrap_line(value)?;
sh_println!("{}", SimpleCast::from_wei(&value, &unit)?)?
Expand Down
51 changes: 48 additions & 3 deletions crates/cast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1354,17 +1354,62 @@ impl SimpleCast {
.wrap_err("Could not convert to uint")?
.0;
let unit = unit.parse().wrap_err("could not parse units")?;
let mut formatted = ParseUnits::U256(value).format_units(unit);
Ok(Self::format_unit_as_string(value, unit))
}

/// Convert a number into a uint with arbitrary decimals.
///
/// # Example
///
/// ```
/// use cast::SimpleCast as Cast;
///
/// # fn main() -> eyre::Result<()> {
/// assert_eq!(Cast::parse_units("1.0", 6)?, "1000000"); // USDC (6 decimals)
/// assert_eq!(Cast::parse_units("2.5", 6)?, "2500000");
/// assert_eq!(Cast::parse_units("1.0", 12)?, "1000000000000"); // 12 decimals
/// assert_eq!(Cast::parse_units("1.23", 3)?, "1230"); // 3 decimals
/// # Ok(())
/// # }
/// ```
pub fn parse_units(value: &str, unit: u8) -> Result<String> {
let unit = Unit::new(unit).ok_or_else(|| eyre::eyre!("invalid unit"))?;

Ok(ParseUnits::parse_units(value, unit)?.to_string())
}

/// Format a number from smallest unit to decimal with arbitrary decimals.
///
/// # Example
///
/// ```
/// use cast::SimpleCast as Cast;
///
/// # fn main() -> eyre::Result<()> {
/// assert_eq!(Cast::format_units("1000000", 6)?, "1"); // USDC (6 decimals)
/// assert_eq!(Cast::format_units("2500000", 6)?, "2.500000");
/// assert_eq!(Cast::format_units("1000000000000", 12)?, "1"); // 12 decimals
/// assert_eq!(Cast::format_units("1230", 3)?, "1.230"); // 3 decimals
/// # Ok(())
/// # }
/// ```
pub fn format_units(value: &str, unit: u8) -> Result<String> {
let value = NumberWithBase::parse_int(value, None)?.number();
let unit = Unit::new(unit).ok_or_else(|| eyre::eyre!("invalid unit"))?;
Ok(Self::format_unit_as_string(value, unit))
}

// Helper function to format units as a string
fn format_unit_as_string(value: U256, unit: Unit) -> String {
let mut formatted = ParseUnits::U256(value).format_units(unit);
// Trim empty fractional part.
if let Some(dot) = formatted.find('.') {
let fractional = &formatted[dot + 1..];
if fractional.chars().all(|c: char| c == '0') {
formatted = formatted[..dot].to_string();
}
}

Ok(formatted)
formatted
}

/// Converts wei into an eth amount
Expand Down
36 changes: 36 additions & 0 deletions crates/cast/tests/cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1396,3 +1396,39 @@ casttest!(hash_message, |_prj, cmd| {
"#]]);
});

casttest!(parse_units, |_prj, cmd| {
cmd.args(["parse-units", "1.5", "6"]).assert_success().stdout_eq(str![[r#"
1500000
"#]]);

cmd.cast_fuse().args(["pun", "1.23", "18"]).assert_success().stdout_eq(str![[r#"
1230000000000000000
"#]]);

cmd.cast_fuse().args(["--parse-units", "1.23", "3"]).assert_success().stdout_eq(str![[r#"
1230
"#]]);
});

casttest!(format_units, |_prj, cmd| {
cmd.args(["format-units", "1000000", "6"]).assert_success().stdout_eq(str![[r#"
1
"#]]);

cmd.cast_fuse().args(["--format-units", "2500000", "6"]).assert_success().stdout_eq(str![[
r#"
2.500000
"#
]]);

cmd.cast_fuse().args(["fun", "1230", "3"]).assert_success().stdout_eq(str![[r#"
1.230
"#]]);
});

0 comments on commit bcef905

Please sign in to comment.