type hardening
This commit is contained in:
@@ -7,6 +7,7 @@ license.workspace = true
|
||||
[dependencies]
|
||||
prost-types = { workspace = true }
|
||||
rust_decimal = { workspace = true }
|
||||
rusty-money = { workspace = true }
|
||||
|
||||
tonic = "0.14.6"
|
||||
prost = "0.14.4"
|
||||
|
||||
@@ -131,7 +131,8 @@ message PostTableDataRequest {
|
||||
// - BOOLEAN: bool value
|
||||
// - TIMESTAMPTZ: ISO 8601/RFC 3339 string (parsed to TIMESTAMPTZ)
|
||||
// - INTEGER: number with no fractional part and within i32 range
|
||||
// - BIGINT: number with no fractional part and within i64 range
|
||||
// - BIGINT: canonical integer string for the full i64 range; a number is
|
||||
// accepted only within protobuf NumberValue's exact integer range
|
||||
// - NUMERIC(p,s): string representation only; empty string becomes NULL
|
||||
// (numbers for NUMERIC are rejected to avoid precision loss)
|
||||
//
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
pub mod search;
|
||||
pub mod decimal;
|
||||
pub mod grpc_error;
|
||||
pub mod money;
|
||||
pub mod relationship;
|
||||
pub mod system_column;
|
||||
|
||||
|
||||
53
common/src/money.rs
Normal file
53
common/src/money.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
// common/src/money.rs
|
||||
//!
|
||||
//! The one canonical currency spelling shared by the client and the server.
|
||||
//!
|
||||
//! A currency code is stored, sent and compared as its canonical uppercase
|
||||
//! ISO-4217 alphabetic code. Both ends resolve it with
|
||||
//! [`require_iso_currency_code`], so the client rejects exactly what the server
|
||||
//! would reject, and each side renders the shared error as its own error type.
|
||||
|
||||
pub use rusty_money::iso;
|
||||
|
||||
/// Resolves a canonical uppercase ISO-4217 alphabetic code to its currency.
|
||||
///
|
||||
/// Rejects anything not already canonical: lowercase (`eur`), surrounding
|
||||
/// whitespace, and any length but three. Normalising here instead would let a
|
||||
/// value be written in one spelling and compared in another.
|
||||
pub fn require_iso_currency_code(currency_code: &str) -> Result<&'static iso::Currency, String> {
|
||||
if currency_code.len() != 3
|
||||
|| !currency_code
|
||||
.chars()
|
||||
.all(|character| character.is_ascii_uppercase())
|
||||
{
|
||||
return Err("Currency must be a canonical uppercase ISO-4217 code".to_string());
|
||||
}
|
||||
|
||||
iso::find(currency_code).ok_or_else(|| format!("Unknown ISO-4217 currency: {currency_code}"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn canonical_codes_resolve_to_their_currency() {
|
||||
assert_eq!(require_iso_currency_code("EUR").unwrap(), iso::EUR);
|
||||
assert_eq!(
|
||||
require_iso_currency_code("USD").unwrap().iso_alpha_code,
|
||||
"USD"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_canonical_or_unknown_codes_are_rejected() {
|
||||
for currency_code in [
|
||||
"", "E", "EU", "EURO", "eur", "Eur", " EUR", "EUR ", "E R", "E\u{20AC}R", "AAA", "123",
|
||||
] {
|
||||
assert!(
|
||||
require_iso_currency_code(currency_code).is_err(),
|
||||
"unexpectedly accepted {currency_code:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -27,7 +27,8 @@ pub struct PostTableDataRequest {
|
||||
/// * BOOLEAN: bool value
|
||||
/// * TIMESTAMPTZ: ISO 8601/RFC 3339 string (parsed to TIMESTAMPTZ)
|
||||
/// * INTEGER: number with no fractional part and within i32 range
|
||||
/// * BIGINT: number with no fractional part and within i64 range
|
||||
/// * BIGINT: canonical integer string for the full i64 range; a number is
|
||||
/// accepted only within protobuf NumberValue's exact integer range
|
||||
/// * NUMERIC(p,s): string representation only; empty string becomes NULL
|
||||
/// (numbers for NUMERIC are rejected to avoid precision loss)
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user