Skip to main content

fakecloud_support/
shared.rs

1//! Primitives shared across the AWS Support handlers: id synthesis, the
2//! submitter identity, and ISO-8601 timestamps. Kept in one place so the
3//! create / describe paths cannot diverge on wire format.
4
5use serde_json::Value;
6
7/// Current time as an ISO-8601 UTC string with millisecond precision, e.g.
8/// `2013-08-23T20:10:32.000Z`. The Support `TimeCreated` / `ExpiryTime` shapes
9/// carry no `@timestampFormat`, and the live service returns ISO-8601 strings.
10pub fn iso_now() -> String {
11    chrono::Utc::now()
12        .format("%Y-%m-%dT%H:%M:%S%.3fZ")
13        .to_string()
14}
15
16/// The current UTC year, used in the `case-{account}-{year}-{hex}` case id.
17pub fn current_year() -> i32 {
18    chrono::Utc::now()
19        .format("%Y")
20        .to_string()
21        .parse()
22        .unwrap_or(2013)
23}
24
25/// FNV-1a hash for deterministic synthesis of ids from a seed so a given
26/// resource's derived value is stable across reads and restarts.
27pub fn hash_str(s: &str) -> u64 {
28    let mut h: u64 = 0xcbf2_9ce4_8422_2325;
29    for b in s.as_bytes() {
30        h ^= u64::from(*b);
31        h = h.wrapping_mul(0x0000_0100_0000_01b3);
32    }
33    h
34}
35
36/// A fresh 32-hex-character random id (used for the case-id tail).
37pub fn hex32() -> String {
38    uuid::Uuid::new_v4().simple().to_string()
39}
40
41/// AWS-shaped Support case id: `case-{account}-{year}-{16 hex}`.
42pub fn new_case_id(account: &str, year: i32) -> String {
43    let tail = &hex32()[..16];
44    format!("case-{account}-{year}-{tail}")
45}
46
47/// A 10-digit numeric `displayId` derived from the case id so it round-trips.
48pub fn display_id(case_id: &str) -> String {
49    let n = hash_str(case_id) % 9_000_000_000 + 1_000_000_000;
50    n.to_string()
51}
52
53/// A fresh attachment-set id (`as-{32 hex}`), matching the live service's
54/// opaque token shape.
55pub fn new_attachment_set_id() -> String {
56    format!("as-{}", hex32())
57}
58
59/// A fresh attachment id (`attachment-{32 hex}`).
60pub fn new_attachment_id() -> String {
61    format!("attachment-{}", hex32())
62}
63
64/// Read a string member from a request body.
65pub fn str_member<'a>(body: &'a Value, name: &str) -> Option<&'a str> {
66    body.get(name).and_then(Value::as_str)
67}