{"id":24144969,"url":"https://github.com/fi3/merx","last_synced_at":"2026-05-13T17:31:48.316Z","repository":{"id":57638074,"uuid":"251654328","full_name":"Fi3/merx","owner":"Fi3","description":null,"archived":false,"fork":false,"pushed_at":"2020-11-15T16:20:58.000Z","size":35,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-23T00:13:29.318Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Fi3.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-03-31T15:55:19.000Z","updated_at":"2020-11-15T16:21:01.000Z","dependencies_parsed_at":"2022-09-26T20:21:08.962Z","dependency_job_id":null,"html_url":"https://github.com/Fi3/merx","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fi3%2Fmerx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fi3%2Fmerx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fi3%2Fmerx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Fi3%2Fmerx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Fi3","download_url":"https://codeload.github.com/Fi3/merx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241381645,"owners_count":19953751,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-12T06:13:33.806Z","updated_at":"2026-05-13T17:31:48.269Z","avatar_url":"https://github.com/Fi3.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Merx\n\n##\n\n**mèr | ce** s. f. [lat. merx mercis]:\n[*something meant to be divided, distributable portion.*](https://www.etimo.it/?term=merce\u0026find=Cerca)\n\n[Here](https://github.com/fi3/pinoedino) a test case for Merx.\n\n##\n\n**Pre-beta version** if you think that merx can be useful or you like it, [here](https://users.rust-lang.org/t/help-annuncment-merx-let-you-define-decimal-safe-types/40531) a\nthread about which direction this library should take.\n\n##\n\nrustc version \u003e= (1edd389cc 2020-03-23)\n\n##\n\nMerx is a library useful to talk about quantities in a safe way and with an eye on the\nperformance. It is inspired by this\n[article](https://tech.fpcomplete.com/blog/safe-decimal-right-on-the-money) from\n[fpcomplete](https://tech.fpcomplete.com/tech).\n\n## How it work:\nMerx let you defines assets. An asset is everything that has an amount and can be divided, for\nexample an asset could be a currency, a commodity, (a physical quantity?) ecc ecc\n\nAn asset is characterized by a unit (minimum quantity) and an optional upper bound.\nThe unit is the smallest part of the asset that the software can express.\n\nAddition between assets of the same type are supported out of the box with operator\noverloading. Multiplication and division are implemented between assets and numeric\ntypes with operator overloading. So **asset + asset**, **asset * number**,\n**asset / number** are valid operations.\n\nAn asset can be either a credit or a debit. A debit can only contain negative amounts. A credit can\nonly contains positive amounts.\n\nMerx expose `Asset` that is a wrapper around a `Debt` or a `Credit` that are wrapper around a\nnumeric value.\nThe wrapped numeric value is a dummy fixed value defined in [*/src/fixed.rs*](./src.fixed.rs)\n\n```rust\npub struct \u003cT: NUMERIC\u003eDebt(T);\npub struct \u003cT: NUMERIC\u003eCredit(T);\n\npub enum Asset\u003cT: NUMERIC\u003e {\n    Debt(Debt(T)),NUMERIC\n    Credit(Credit(T)),\n}\n```\n\nThe permitted operations are: \n* `Credit - Debt` that is positive_value + negative_value [TODO remove it it should be `Credit + Debt`]\n* `Credit + Credit` that is value + value\n* `Debt + Credit` that is negative_value + positive_value\n* `Debt + Debt` that is negative_value + negative_value\n* `Asset + Asset` that is value + value\n\n## Example\n```rust\n#[macro_use]\nextern crate merx;\nuse merx::{Asset, Debt, Credit, asset::CheckedOps};\n\nget_traits!();\n\n// Create a new asset called bitcoin with 8 decimal digits and a max value of 21 million of units\nnew_asset!(bitcoin, 8, 21_000_000);\n// Create a new asset called usd with 2 decimal digits and a max value of 14_000_000_000_000 units\nnew_asset!(usd, 2, 14_000_000_000_000);\n\ntype Bitcoin = Asset\u003cbitcoin::Value\u003e;\ntype Usd = Asset\u003cusd::Value\u003e;\n\nfn main() {\n    // A tuple that define a decimal value as (mantissa, decimal part)\n    let tot_amount = (679, 1); // -\u003e 67.9\n    let tot_amount = Bitcoin::try_from(tot_amount).unwrap();\n    let to_pay = Bitcoin::try_from(-29).unwrap();\n    let remain = (tot_amount + to_pay).unwrap();\n    println!(\"{:#?}\", remain);\n\n    // With a float also a rounding method must be provided, this because Merx must know what to do\n    // with floats with higher precision than the asset. Possible rounding methods are Trunc Floor\n    // Ceil Round and they behaved the same as rust's f64 methods with the same names.\n    let usd = Usd::try_from((10.87, FloatRounding::Trunc)).unwrap();\n    println!(\"{:#?}\", usd);\n\n    // When the source of the float is a text string the best thing to do is to parse the value\n    // from a string.\n    let usd = Usd::try_from(\"10.87\").unwrap();\n    println!(\"{:#?}\", usd);\n\n    // TODO smouthly conversion\n    //let x: USD = match remain {\n    //    Credit(x) =\u003e interests(USD::from(x), 12, 3);\n    //    Debt(x) =\u003e interests(USD::from(x), 12, 3);\n    //};\n}\n\n// You can define function over generic assets:\n\n// Adding assets of type T return an asset of type T\nfn add_assets\u003cT: CheckedOps\u003e(x: Asset\u003cT\u003e, y: Asset\u003cT\u003e) -\u003e Option\u003cAsset\u003cT\u003e\u003e {\n    x + y\n}\n\n// Adding credits can only result in a Credit\nfn add_credits\u003cT: CheckedOps\u003e(x: Credit\u003cT\u003e, y: Credit\u003cT\u003e) -\u003e Option\u003cCredit\u003cT\u003e\u003e {\n    x + y\n}\n\n// Adding debts can only result in a Debt\nfn add_debts\u003cT: CheckedOps\u003e(x: Debt\u003cT\u003e, y: Debt\u003cT\u003e) -\u003e Option\u003cDebt\u003cT\u003e\u003e {\n    x + y\n}\n\n```\n\n## Safety\n\n1. Is impossible to add assets of different types or add an asset with a numeric value.\n2. Every operation that concern an asset (add mul div) is checked and fail on incorrect values.\n3. Build assets from primitive types is safe [TODO].\n4. When the result of an operation is positive we have a `Credit` otherwise we have `Debt`, is not\npossible to build a `Credit` with a negative value or a `Debt` with a positive value.\n5. The library have 0 dependency.\n\n## Performance\n\nMerx in order to add assets do a checked add. From the benchmark it seems that Merx is a\nlittle faster in doing that than a plain function like the one below.\n\n```rust\nfn checked_add_and_compare_64(a: i64, b: i64, max: i64) -\u003e Option\u003ci64\u003e {\n    let sum = a.checked_add(b)?;\n    if sum.checked_abs()? \u003c= max {\n        return Some(sum);\n    }\n    return None\n}\n```\n\n```\nBenchmarking add 64 bit int\nBenchmarking add 64 bit int: Warming up for 3.0000 s\nBenchmarking add 64 bit int: Collecting 100 samples in estimated 5.0000 s (2.5B iterations)\nBenchmarking add 64 bit int: Analyzing\nadd 64 bit int          time:   [2.0150 ns 2.0205 ns 2.0281 ns]\n                        change: [-1.3576% -0.5189% +0.0981%] (p = 0.19 \u003e 0.05)\n                        No change in performance detected.\nFound 8 outliers among 100 measurements (8.00%)\n  3 (3.00%) high mild\n  5 (5.00%) high severe\n\nBenchmarking add 64 bit assets\nBenchmarking add 64 bit assets: Warming up for 3.0000 s\nBenchmarking add 64 bit assets: Collecting 100 samples in estimated 5.0000 s (2.5B iterations)\nBenchmarking add 64 bit assets: Analyzing\nadd 64 bit assets       time:   [2.0138 ns 2.0168 ns 2.0210 ns]\n                        change: [-1.0088% -0.3617% +0.1265%] (p = 0.25 \u003e 0.05)\n                        No change in performance detected.\nFound 9 outliers among 100 measurements (9.00%)\n  3 (3.00%) high mild\n  6 (6.00%) high severe\n```\n\nThis happens also when assets are multiplied or divided. I think that the library is not\nslow but I can not justify the above numbers. I'm not confident in these benchmarks and in my\nbenchmarking ability, the benchmarks must be reviewed.\n\n## Precision\n\n[TODO]\n\n## Alternatives\n\nThe similest crate that I have been able to find is [commodity](https://crates.io/crates/commodity),\nmerx is different from commodity in ... TODO\n\nBelow a list of crates that solve problems that are similar or related at the one solved by merx.\n\n**Fixed point arithmetic**\n* [fixed](https://docs.rs/fixed/0.5.4/fixed/).\n*  ... TODO\n\n**Decimal**\n* [rust_decimal](https://crates.io/crates/rust_decimal)\n\n**Money crates**\n[TODO]\n\n**Units crates**\n* [yaiouom](https://github.com/Yoric/yaiouom)\n* ... TODO\n\n## Todo\n\n - [ ] Documentation\n - [ ] Better zero\n - [x] Fix try_from floating point \n - [ ] Remove support fro `Credit\u003cT\u003e + Debt\u003cT\u003e`\n - [ ] Conversion between Asset Debits and Credits\n - [ ] Error on upper_bound overflow\n - [ ] Set upper bound for asset with no upper bound\n - [ ] Make the inner numeric value generic over ...?\n - [ ] Use the crate fixed as inner type (when it will support generic const)\n - [ ] Impl PartialEq for Asset and all the primitive numeric types\n - [ ] Add error with thiserror\n - [ ] Serde serialize deserialize\n - [ ] Division and multiplication between asset, float and between asset and fixed\n - [ ] Add all standard operations for rationals like truncate floor ecc ecc\n - [ ] Add conversion between assets with exchange rate setted\n - [ ] A lot of public thinghs should be private\n - [ ] Benchmarks\n - [ ] Documentation\n - [ ] Precise modality with bigint insted of fixed point\n - [ ] Add the possibility to define a rounding strategy when an asset is defined\n - [ ] Build asset from primitive values is safe\n\n## License\n\nMIT OR [UNLICENSE](https://unlicense.org/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffi3%2Fmerx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffi3%2Fmerx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffi3%2Fmerx/lists"}