Skip to content

Commit ec8c2ae

Browse files
committed
jsonb: Introduce JsonDom type and parsing/conversion
1 parent 3d6bd8e commit ec8c2ae

File tree

8 files changed

+611
-11
lines changed

8 files changed

+611
-11
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ Supported derivations:
242242
* `#[mysql(rename = "some_name")]` – overrides column name of a field
243243
* `#[mysql(json)]` - column will be interpreted as a JSON string containing
244244
a value of a field type
245+
* `#[mysql(with = path::to::convert_fn)]``convert_fn` will be used to deserialize
246+
a field value (expects a function with a signature that mimics
247+
`TryFrom<Value, Error=FromValueError>``)
245248

246249
#### Example
247250

src/binlog/decimal/mod.rs

+45-4
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub const POWERS_10: [i32; DIG_PER_DEC + 1] = [
4949
///
5050
/// * serialization/deserialization to/from binary format
5151
/// (see `read_bin` and `write_bin` functions);
52-
/// * parsing from decimal string/buffer (see `Decimal::parse_bytes`, `FromStr` impl);
52+
/// * parsing from decimal string/buffer (see `Decimal::parse_str_bytes`, `FromStr` impl);
5353
/// * conversion to decimal string (using `Display`).
5454
///
5555
/// # Notes
@@ -58,7 +58,7 @@ pub const POWERS_10: [i32; DIG_PER_DEC + 1] = [
5858
/// i.e. both `rhs` and `lhs` will be serialized into temporary buffers;
5959
/// * even though MySql's `string2decimal` function allows scientific notation,
6060
/// this implementation denies it.
61-
#[derive(Default, Debug, Eq)]
61+
#[derive(Default, Debug, Eq, Clone)]
6262
pub struct Decimal {
6363
/// The number of *decimal* digits (NOT number of `Digit`s!) before the point.
6464
intg: usize,
@@ -75,13 +75,33 @@ impl Decimal {
7575
decimal_bin_size(self.intg + self.frac, self.frac)
7676
}
7777

78+
/// See [`Decimal::parse_str_bytes`].
79+
#[deprecated = "use parse_str_bytes"]
7880
pub fn parse_bytes(bytes: &[u8]) -> Result<Self, ParseDecimalError> {
7981
match std::str::from_utf8(bytes) {
8082
Ok(string) => Decimal::from_str(string),
8183
Err(_) => Err(ParseDecimalError),
8284
}
8385
}
8486

87+
/// Runs `Decimal::from_str` on the given bytes.
88+
pub fn parse_str_bytes(bytes: &[u8]) -> Result<Self, ParseDecimalError> {
89+
macro_rules! decimal_str {
90+
($x:ident) => {
91+
if $x
92+
.iter()
93+
.all(|x| x.is_ascii_digit() || *x == b'+' || matches!(x, b'-'..=b'.'))
94+
{
95+
// SAFETY: UTF-8 is asserted by the if condition
96+
Some(unsafe { std::str::from_utf8_unchecked($x) })
97+
} else {
98+
None
99+
}
100+
};
101+
}
102+
Decimal::from_str(decimal_str!(bytes).ok_or(ParseDecimalError)?)
103+
}
104+
85105
pub fn write_bin<T: Write>(&self, mut output: T) -> io::Result<()> {
86106
// result bits must be inverted if the sign is negative,
87107
// we'll XOR it with `mask` to achieve this.
@@ -139,6 +159,27 @@ impl Decimal {
139159
output.write_all(&out_buf)
140160
}
141161

162+
/// Reads packed representation of a [`Decimal`].
163+
///
164+
/// Packed representation is:
165+
///
166+
/// 1. precision (u8)
167+
/// 2. scale (u8)
168+
/// 3. serialized decimal value (see [`Decimal::read_bin`])
169+
pub fn read_packed<T: Read>(mut input: T, keep_precision: bool) -> io::Result<Self> {
170+
let mut precision_and_scale = [0_u8, 0_u8];
171+
input.read_exact(&mut precision_and_scale)?;
172+
Self::read_bin(
173+
input,
174+
precision_and_scale[0] as usize,
175+
precision_and_scale[1] as usize,
176+
keep_precision,
177+
)
178+
}
179+
180+
/// Reads serialized representation of a decimal value.
181+
///
182+
/// The value is usually written in the packed form (see [`Decimal::read_packed`]).
142183
pub fn read_bin<T: Read>(
143184
mut input: T,
144185
precision: usize,
@@ -158,10 +199,10 @@ impl Decimal {
158199

159200
// is it negative or not
160201
let mask = if buffer.first().copied().unwrap_or(0) & 0x80 == 0 {
161-
// positive, so mask should do noghing
202+
// positive, so mask should do nothing
162203
0
163204
} else {
164-
// negative, so mask snould invert bits
205+
// negative, so mask should invert bits
165206
-1
166207
};
167208

src/binlog/decimal/test/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ proptest! {
135135
let num = dbg!(&num);
136136

137137
// test string2decimal
138-
let dec = dbg!(super::Decimal::parse_bytes(num.as_bytes()).unwrap());
138+
let dec = dbg!(super::Decimal::parse_str_bytes(num.as_bytes()).unwrap());
139139
let mysql_dec = dbg!(decimal_t::rust_string2decimal(num).unwrap());
140140
assert_eq!(dec.intg, mysql_dec.intg as usize);
141141
assert_eq!(dec.frac, mysql_dec.frac as usize);

0 commit comments

Comments
 (0)