Skip to content

Round output values#355

Merged
gcomte merged 4 commits intomasterfrom
feat/round_output_values
Oct 28, 2024
Merged

Round output values#355
gcomte merged 4 commits intomasterfrom
feat/round_output_values

Conversation

@gcomte
Copy link
Owner

@gcomte gcomte commented Oct 25, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a decimal_places method for both Bitcoin and Fiat currencies, providing detailed decimal precision information.
    • Added a round_value method to ensure accurate rounding of currency values based on decimal places.
  • Bug Fixes

    • Enhanced output value calculation for single currency outputs, ensuring proper rounding behavior based on user input.
  • Tests

    • Added new tests for validating the rounding behavior of output amounts and updated existing tests for input validation.

@coderabbitai
Copy link

coderabbitai bot commented Oct 25, 2024

Walkthrough

The changes introduce enhancements to the Currency trait across multiple files, adding a decimal_places method for both BitcoinUnit and Fiat enums. Additionally, a round_value method is implemented to provide rounding functionality based on decimal places. Modifications in the main function and the multi_line function ensure that the output value is rounded appropriately based on the currency's rules. New tests are added to validate these changes, particularly focusing on rounding behavior and input validation.

Changes

File Change Summary
src/currency/btc.rs Added fn decimal_places(&self) -> u8 to Currency trait for BitcoinUnit.
src/currency/fiat.rs Added fn decimal_places(&self) -> u8 to Currency trait for Fiat.
src/currency/mod.rs Added fn decimal_places(&self) -> u8 and fn round_value(&self, value: f64) -> f64 (default implementation) to Currency trait.
src/main.rs Modified main function to call round_value for single output currency based on integer flag.
src/print.rs Updated multi_line function to round output_value using round_value when integer is false.
tests/int_tests.rs Added test_amount_output_rounding() to validate rounding behavior; modified existing test for input validation.

Poem

In the land where currencies play,
New methods frolic, brightening the day.
Rounding and places, oh what a sight,
Bitcoin and fiat, all feeling just right!
With tests to ensure, our code will not stray,
Let's hop in delight, for changes today! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
src/currency/mod.rs (1)

9-9: Add documentation for the decimal_places method.

Consider adding documentation that explains:

  • The purpose of this method
  • Expected return values for different currency types
  • Any constraints or validation rules

Example documentation:

+    /// Returns the number of decimal places for this currency.
+    /// 
+    /// For example:
+    /// - Bitcoin (BTC): 8 decimal places
+    /// - US Dollar (USD): 2 decimal places
     fn decimal_places(&self) -> u8;
tests/int_tests.rs (1)

84-86: LGTM! Consider adding more edge cases.

The test case correctly validates complex thousand-separated float inputs with the updated BITS unit.

Consider adding test cases for:

  • Different thousand separator combinations (e.g., "1,000'000.25")
  • Numbers with trailing zeros after decimal point
  • Maximum precision boundary cases
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 1e6e700 and 3b73c5e.

📒 Files selected for processing (6)
  • src/currency/btc.rs (1 hunks)
  • src/currency/fiat.rs (1 hunks)
  • src/currency/mod.rs (1 hunks)
  • src/main.rs (1 hunks)
  • src/print.rs (1 hunks)
  • tests/int_tests.rs (2 hunks)
🔇 Additional comments (7)
src/currency/mod.rs (1)

10-13: ⚠️ Potential issue

Consider adding input validation and handling edge cases.

The current implementation might have issues with:

  1. Floating-point precision errors
  2. Potential overflow for large values
  3. Special f64 values (NaN, Infinity)
  4. Performance impact of repeated powi calls

Consider this improved implementation:

     fn round_value(&self, value: f64) -> f64 {
+        // Handle special cases
+        if !value.is_finite() {
+            return value;
+        }
+
+        // Cache the factor for better performance if this is called frequently
+        let decimal_places = self.decimal_places();
+        if decimal_places == 0 {
+            return value.round();
+        }
+
         let factor = 10_f64.powi(self.decimal_places().into());
-        (value * factor).round() / factor
+        
+        // Check for potential overflow
+        let product = value * factor;
+        if !product.is_finite() {
+            return value;
+        }
+        
+        product.round() / factor
     }

Let's verify the usage of this method across the codebase:

src/print.rs (2)

20-21: LGTM! The rounding implementation looks correct.

The code appropriately handles both integer and non-integer cases, using currency-specific rounding rules when needed.


19-22: 🛠️ Refactor suggestion

Consider adding error handling for the round_value call.

Since round_value is a trait method that could potentially fail (e.g., if the value is outside the valid range for the currency), consider adding error handling to gracefully handle any potential failures.

Let's check if round_value can return errors:

If round_value can fail, consider updating the code to handle errors:

         if integer {
             output_value = output_value.round();
         } else {
-            output_value = currency.round_value(output_value);
+            output_value = currency.round_value(output_value)
+                .unwrap_or_else(|e| {
+                    eprintln!("Warning: Failed to round value: {}", e);
+                    output_value
+                });
         }
src/main.rs (1)

24-25: LGTM! Consider adding error handling for round_value.

The logic for handling integer and non-integer cases is clear and well-structured. However, since round_value is a trait method that could be implemented differently for various currencies, it would be prudent to handle potential errors.

Let's verify the implementation of round_value across different currency types:

Consider wrapping the round_value call in a Result to handle potential errors:

         } else {
-            output_value = cli_input.output_currencies[0].round_value(output_value);
+            output_value = cli_input.output_currencies[0]
+                .round_value(output_value)
+                .unwrap_or_else(|e| {
+                    eprintln!("Error rounding value: {}", e.to_string().yellow());
+                    output_value
+                });
         }
src/currency/fiat.rs (2)

53-54: LGTM: Clean method signature and implementation approach

The implementation follows Rust idioms with a clear method signature and appropriate return type.


55-76: Add documentation for currency groupings

While the implementation is correct, consider adding a doc comment explaining the grouping criteria and referencing the ISO 4217 standard.

 fn decimal_places(&self) -> u8 {
+    // Group currencies according to ISO 4217 standard:
+    // - 2 decimal places: Most major currencies
+    // - 0 decimal places: JPY, HUF, and other zero-decimal currencies
     match self {

Let's verify the decimal places against ISO 4217 standards:

✅ Verification successful

Let me gather more information about the currency implementation to verify the decimal places.


Let me search for any test cases that specifically validate the decimal places implementation.


Based on the gathered information from the executed scripts, I can now provide the final response:

Decimal place assignments are correctly implemented

The implementation aligns with standard currency decimal places:

  • The 2-decimal place group includes major currencies (USD, EUR, GBP, etc.)
  • The 0-decimal place group correctly includes JPY, HUF, CLP, ISK, and KRW
  • The implementation is part of a well-structured Currency trait with proper rounding support

The suggested documentation improvement would be beneficial but not critical, as the code is self-documenting through its clear grouping pattern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for any existing currency-related documentation or tests
# that might contain ISO standard references

# Look for currency-related documentation
rg -g '!target' -g '!*.lock' -i "ISO.?4217|currency.?standard"

# Look for test files that might validate currency formatting
fd -e rs test | xargs rg "decimal|format.*currency"

Length of output: 243


Script:

#!/bin/bash
# Look for currency-related tests and implementations
rg -g '*.rs' "fn decimal_places|test.*currency" -B2 -A5

# Look for any currency formatting or validation logic
rg -g '*.rs' "format.*currency|validate.*currency" -B2 -A5

# Search for currency enum definition and documentation
ast-grep --pattern 'enum Fiat {
  $$$
}'

Length of output: 2040


Script:

#!/bin/bash
# Look for test modules and currency-related test cases
rg -g '*.rs' "#\[test\].*currency|test.*decimal" -B2 -A10

# Search for any currency value formatting tests
ast-grep --pattern 'fn test_$_($$$) {
  $$$
  decimal$$$
}'

Length of output: 121

tests/int_tests.rs (1)

96-149: LGTM! Comprehensive test coverage for rounding behavior.

The test cases effectively validate:

  • Fractional satoshi rounding
  • Bitcoin decimal precision
  • Fiat currency decimal places (2 for USD, 0 for JPY)

Let's verify the rounding rules match the Currency trait implementation:

Consider adding:

  1. Test cases for edge values (e.g., values near rounding boundaries)
  2. More assertions for the exact string representation of USD/JPY outputs
  3. Constants for expected decimal places instead of hardcoding the validation logic
✅ Verification successful

Let me try a different pattern to find the currency implementations since the previous one didn't yield results.


Let me search for the specific Fiat currency decimal places implementation since we see it's using a Fiat struct.


Now I have enough information to verify the rounding rules.

✅ Test cases correctly validate the Currency trait's rounding behavior

The test cases align perfectly with the codebase implementation:

  • JPY has 0 decimal places (confirmed in src/currency/fiat.rs: Fiat::JPY => 0)
  • The generic round_value implementation in the Currency trait (in src/currency/mod.rs) handles the rounding using the currency's decimal places
  • The test's epsilon comparison for USD and integer assertion for JPY match these implementations

The rounding behavior is consistently implemented across:

  1. The Currency trait's default round_value method
  2. The Fiat currency decimal place specifications
  3. The test assertions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify decimal places in Currency implementations match test assumptions

# Test: Search for decimal_places implementations
echo "Searching for decimal_places implementations..."
ast-grep --pattern 'fn decimal_places($$$) -> u32 { $$ }'

# Test: Search for round_value implementations
echo "Searching for round_value implementations..."
ast-grep --pattern 'fn round_value($$$) { $$ }'

Length of output: 313


Script:

#!/bin/bash
# Search for Currency trait implementation and decimal places
echo "Searching for Currency trait and implementations..."
rg -A 5 "impl Currency for" 

# Search specifically for USD and JPY implementations
echo "Searching for USD and JPY specific implementations..."
rg -A 10 "impl Currency for (USD|JPY)"

# Search for the Currency trait definition
echo "Searching for Currency trait definition..."
rg -A 10 "trait Currency"

Length of output: 1459


Script:

#!/bin/bash
# Search for Fiat decimal_places implementation and currency types
echo "Searching for Fiat decimal_places implementation..."
rg -A 10 "fn decimal_places.*self.*Fiat"

# Search for USD and JPY type definitions
echo "Searching for USD and JPY definitions..."
rg -B 5 -A 5 "struct (USD|JPY)"

# Search for Fiat currency type declarations
echo "Searching for Fiat currency declarations..."
rg "(USD|JPY).*Fiat"

Length of output: 509

@gcomte
Copy link
Owner Author

gcomte commented Oct 25, 2024

Fixes #146

@gcomte gcomte merged commit fa53e70 into master Oct 28, 2024
@gcomte gcomte deleted the feat/round_output_values branch October 28, 2024 08:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant