Skip to content

Commit 829355a

Browse files
authored
Introduces macros feature and cleans up serde documentation a little bit (paupino#628)
1 parent c4d4d3e commit 829355a

File tree

7 files changed

+159
-17
lines changed

7 files changed

+159
-17
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Version History
22

3+
## 1.x
4+
5+
### Added
6+
7+
* `rust_decimal_macros` can now be utilized using the `macros` feature flag.
8+
9+
### Changed
10+
11+
* Added documentation for serde features as well as a few examples.
12+
313
## 1.33.1
414

515
### Fixed

Cargo.toml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ proptest = { default-features = false, optional = true, features = ["std"], vers
3131
rand = { default-features = false, optional = true, version = "0.8" }
3232
rkyv = { default-features = false, features = ["size_32", "std"], optional = true, version = "0.7.42" }
3333
rocket = { default-features = false, optional = true, version = "0.5.0-rc.3" }
34+
rust_decimal_macros = { default-features = false, optional = true, version = "1.33" } # This needs to be the n-1 published version
3435
serde = { default-features = false, optional = true, version = "1.0" }
3536
serde_json = { default-features = false, optional = true, version = "1.0" }
3637
tokio-postgres = { default-features = false, optional = true, version = "0.7" }
@@ -42,14 +43,15 @@ criterion = { default-features = false, version = "0.5" }
4243
csv = "1"
4344
futures = { default-features = false, version = "0.3" }
4445
rand = { default-features = false, features = ["getrandom"], version = "0.8" }
45-
rust_decimal_macros = { path = "macros" } # This should be ok since it's just for tests
46+
rust_decimal_macros = { default-features = false, version = "1.33" }
4647
serde = { default-features = false, features = ["derive"], version = "1.0" }
4748
serde_json = "1.0"
4849
tokio = { default-features = false, features = ["macros", "rt-multi-thread", "test-util"], version = "1.0" }
4950
version-sync = { default-features = false, features = ["html_root_url_updated", "markdown_deps_updated"], version = "0.9" }
5051

5152
[features]
5253
default = ["serde", "std"]
54+
macros = ["dep:rust_decimal_macros"]
5355

5456
borsh = ["dep:borsh", "std"]
5557
c-repr = [] # Force Decimal to be repr(C)
@@ -88,4 +90,8 @@ name = "comparison"
8890
path = "benches/comparison.rs"
8991

9092
[workspace]
91-
members = [".", "./macros"]
93+
members = [
94+
".",
95+
"./macros"
96+
]
97+
resolver = "2"

README.md

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,27 @@ Using [`cargo-edit`](https://crates.io/crates/cargo-edit):
1919
$ cargo add rust_decimal
2020
```
2121

22-
In addition, if you would like to use the optimized macro for convenient creation of decimals:
22+
If you would like to use the optimized macro for convenient creation of decimals you can add `rust_decimal` with the `macros` feature flag:
2323

2424
```sh
25-
$ cargo add rust_decimal_macros
25+
$ cargo add rust_decimal --features macros
2626
```
2727

2828
Alternatively, you can edit your `Cargo.toml` directly and run `cargo update`:
2929

3030
```toml
3131
[dependencies]
32-
rust_decimal = "1.33"
33-
rust_decimal_macros = "1.33"
32+
rust_decimal = { version = "1.33", features = ["macros"] }
3433
```
3534

3635
## Usage
3736

38-
Decimal numbers can be created in a few distinct ways. The easiest and most efficient method of creating a Decimal is to use the procedural macro within the `rust_decimal_macros` crate:
37+
Decimal numbers can be created in a few distinct ways. The easiest and most efficient method of creating a Decimal is to use the procedural macro that can be enabled using the `macros` feature:
3938

4039
```rust
41-
// Procedural macros need importing directly
40+
// The macros feature exposes a `dec` macro which will parse the input into a raw decimal number at compile time.
41+
// It is also exposed when using `rust_decimal::prelude::*`. That said, you can also import the
42+
// `rust_decimal_macros` crate and use the macro directly from there.
4243
use rust_decimal_macros::dec;
4344

4445
let number = dec!(-1.23) + dec!(3.45);
@@ -198,8 +199,8 @@ Enable `rust-fuzz` support by implementing the `Arbitrary` trait.
198199

199200
### `serde-float`
200201

201-
**Note:** it is recommended to use the `serde-with-*` features for greater control. This allows configurability at the data
202-
level.
202+
> **Note:** This feature applies float serialization/deserialization rules as the default method for handling `Decimal` numbers.
203+
See also the `serde-with-*` features for greater flexibility.
203204

204205
Enable this so that JSON serialization of `Decimal` types are sent as a float instead of a string (default).
205206

@@ -212,8 +213,8 @@ e.g. with this turned on, JSON serialization would output:
212213

213214
### `serde-str`
214215

215-
**Note:** it is recommended to use the `serde-with-*` features for greater control. This allows configurability at the data
216-
level.
216+
> **Note:** This feature applies string serialization/deserialization rules as the default method for handling `Decimal` numbers.
217+
See also the `serde-with-*` features for greater flexibility.
217218

218219
This is typically useful for `bincode` or `csv` like implementations.
219220

@@ -227,17 +228,20 @@ converting to `f64` _loses_ precision, it's highly recommended that you do NOT e
227228

228229
### `serde-arbitrary-precision`
229230

230-
**Note:** it is recommended to use the `serde-with-*` features for greater control. This allows configurability at the data
231-
level.
231+
> **Note:** This feature applies arbitrary serialization/deserialization rules as the default method for handling `Decimal` numbers.
232+
See also the `serde-with-*` features for greater flexibility.
232233

233234
This is used primarily with `serde_json` and consequently adds it as a "weak dependency". This supports the
234235
`arbitrary_precision` feature inside `serde_json` when parsing decimals.
235236

236237
This is recommended when parsing "float" looking data as it will prevent data loss.
237238

239+
Please note, this currently serializes numbers in a float like format by default, which can be an unexpected consequence. For greater
240+
control over the serialization format, please use the `serde-with-arbitrary-precision` feature.
241+
238242
### `serde-with-float`
239243

240-
Enable this to access the module for serializing `Decimal` types to a float. This can be use in `struct` definitions like so:
244+
Enable this to access the module for serializing `Decimal` types to a float. This can be used in `struct` definitions like so:
241245

242246
```rust
243247
#[derive(Serialize, Deserialize)]
@@ -254,9 +258,18 @@ pub struct OptionFloatExample {
254258
}
255259
```
256260

261+
Alternatively, if only the serialization feature is desired (e.g. to keep flexibility while deserialization):
262+
```rust
263+
#[derive(Serialize, Deserialize)]
264+
pub struct FloatExample {
265+
#[serde(serialize_with = "rust_decimal::serde::float::serialize")]
266+
value: Decimal,
267+
}
268+
```
269+
257270
### `serde-with-str`
258271

259-
Enable this to access the module for serializing `Decimal` types to a `String`. This can be use in `struct` definitions like so:
272+
Enable this to access the module for serializing `Decimal` types to a `String`. This can be used in `struct` definitions like so:
260273

261274
```rust
262275
#[derive(Serialize, Deserialize)]
@@ -273,9 +286,19 @@ pub struct OptionStrExample {
273286
}
274287
```
275288

289+
This feature isn't typically required for serialization however can be useful for deserialization purposes since it does not require
290+
a type hint. Consequently, you can force this for just deserialization by:
291+
```rust
292+
#[derive(Serialize, Deserialize)]
293+
pub struct StrExample {
294+
#[serde(deserialize_with = "rust_decimal::serde::str::deserialize")]
295+
value: Decimal,
296+
}
297+
```
298+
276299
### `serde-with-arbitrary-precision`
277300

278-
Enable this to access the module for serializing `Decimal` types to a `String`. This can be use in `struct` definitions like so:
301+
Enable this to access the module for deserializing `Decimal` types using the `serde_json/arbitrary_precision` feature. This can be used in `struct` definitions like so:
279302

280303
```rust
281304
#[derive(Serialize, Deserialize)]
@@ -292,6 +315,20 @@ pub struct OptionArbitraryExample {
292315
}
293316
```
294317

318+
An unexpected consequence of this feature is that it will serialize as a float like number. To prevent this, you can
319+
target the struct to only deserialize with the `arbitrary_precision` feature:
320+
```rust
321+
#[derive(Serialize, Deserialize)]
322+
pub struct ArbitraryExample {
323+
#[serde(deserialize_with = "rust_decimal::serde::arbitrary_precision::deserialize")]
324+
value: Decimal,
325+
}
326+
```
327+
328+
This will ensure that serialization still occurs as a string.
329+
330+
Please see the `examples` directory for more information regarding `serde_json` scenarios.
331+
295332
### `std`
296333

297334
Enable `std` library support. This is enabled by default, however in the future will be opt in. For now, to support `no_std`

examples/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Examples
2+
3+
This contains some more advanced examples of using the rust decimal library of complex usage.
4+
5+
All examples are crate based to demonstrate feature configurations. Examples can be run by using:
6+
7+
```shell
8+
cd examples/<example-name>
9+
cargo run
10+
```
11+
12+
## serde-json-scenarios
13+
14+
This example shows how to use the `serde` crate to serialize and deserialize the `Decimal` type using multiple different
15+
serialization formats.
16+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "serde-json-scenarios"
3+
version = "0.0.0"
4+
edition = "2021"
5+
publish = false
6+
7+
[workspace]
8+
9+
[dependencies]
10+
rust_decimal = { path = "../..", features = ["macros", "serde-with-arbitrary-precision"] }
11+
serde = { version = "1.0", features = ["derive"] }
12+
serde_json = { version = "1.0", features = ["arbitrary_precision"]}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use rust_decimal::prelude::*;
2+
3+
type ExampleResult = Result<(), Box<dyn std::error::Error>>;
4+
5+
fn main() -> ExampleResult {
6+
demonstrate_default_behavior()?;
7+
demonstrate_arbitrary_precision_deserialization_with_string_serialization()?;
8+
Ok(())
9+
}
10+
11+
/// The default behavior of the library always expects string results. That is, it will serialize the
12+
/// Decimal as string, but also expect a string when deserializing.
13+
/// Note: this is not enough for bincode representations since there is no deserialization hint that the
14+
/// field is a string.
15+
fn demonstrate_default_behavior() -> ExampleResult {
16+
#[derive(serde::Serialize, serde::Deserialize)]
17+
struct Total {
18+
value: Decimal,
19+
}
20+
let total = Total { value: dec!(1.23) };
21+
let serialized = serde_json::to_string(&total)?;
22+
assert_eq!(r#"{"value":"1.23"}"#, serialized);
23+
24+
// If we try to deserialize the same string we should succeed
25+
let deserialized: Total = serde_json::from_str(&serialized)?;
26+
assert_eq!(dec!(1.23), deserialized.value);
27+
28+
// Technically, by default we also support deserializing from a number, however this is doing a float
29+
// conversion and is not recommended.
30+
let deserialized: Total = serde_json::from_str(r#"{"value":1.23}"#)?;
31+
assert_eq!(dec!(1.23), deserialized.value);
32+
Ok(())
33+
}
34+
35+
/// This demonstrates using arbitrary precision for a decimal value - even though the
36+
/// default string serialization behavior is baked in.
37+
fn demonstrate_arbitrary_precision_deserialization_with_string_serialization() -> ExampleResult {
38+
#[derive(serde::Serialize, serde::Deserialize)]
39+
struct Total {
40+
#[serde(deserialize_with = "rust_decimal::serde::arbitrary_precision::deserialize")]
41+
value: Decimal,
42+
}
43+
44+
let total = Total { value: dec!(1.23) };
45+
let serialized = serde_json::to_string(&total)?;
46+
assert_eq!(r#"{"value":"1.23"}"#, serialized);
47+
48+
// If we try to deserialize the same string we should succeed
49+
let deserialized: Total = serde_json::from_str(&serialized)?;
50+
assert_eq!(dec!(1.23), deserialized.value);
51+
52+
// If we try to deserialize a float then this will succeed as well
53+
let deserialized: Total = serde_json::from_str(r#"{"value":1.23}"#)?;
54+
assert_eq!(dec!(1.23), deserialized.value);
55+
Ok(())
56+
}

src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,18 @@ pub use error::Error;
5757
#[cfg(feature = "maths")]
5858
pub use maths::MathematicalOps;
5959

60+
#[cfg(feature = "macros")]
61+
pub use rust_decimal_macros::dec;
62+
6063
/// A convenience module appropriate for glob imports (`use rust_decimal::prelude::*;`).
6164
pub mod prelude {
6265
#[cfg(feature = "maths")]
6366
pub use crate::maths::MathematicalOps;
6467
pub use crate::{Decimal, RoundingStrategy};
6568
pub use core::str::FromStr;
6669
pub use num_traits::{FromPrimitive, One, Signed, ToPrimitive, Zero};
70+
#[cfg(feature = "macros")]
71+
pub use rust_decimal_macros::dec;
6772
}
6873

6974
#[cfg(all(feature = "diesel1", not(feature = "diesel2")))]

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy