type hardening
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1288,6 +1288,7 @@ dependencies = [
|
||||
"prost-build",
|
||||
"prost-types",
|
||||
"rust_decimal",
|
||||
"rusty-money",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tantivy",
|
||||
|
||||
@@ -43,6 +43,9 @@ tantivy = "0.26.1"
|
||||
# Steel_decimal crate
|
||||
rust_decimal = { version = "1.42.0", features = ["maths", "serde"] }
|
||||
rust_decimal_macros = "1.40.0"
|
||||
# One version across the workspace: `&'static iso::Currency` only crosses crate
|
||||
# boundaries if every crate resolves rusty-money to the same instance.
|
||||
rusty-money = "0.5.0"
|
||||
thiserror = "2.0.18"
|
||||
regex = "1.12.4"
|
||||
|
||||
|
||||
2
client
2
client
Submodule client updated: e3b5d57c90...520fb563f4
@@ -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)
|
||||
///
|
||||
|
||||
2
server
2
server
Submodule server updated: 412af6ea3f...62be9cbbaf
Submodule tui-canvas updated: 1251d22406...b1761279ff
Submodule tui-pages updated: 29e1e6a42e...46afc191db
Reference in New Issue
Block a user