1use std::collections::BTreeMap;
2
3use serde::de::Error as _;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7use crate::types::Operation;
8
9fn deserialize_stringified<'de, D>(deserializer: D) -> std::result::Result<String, D::Error>
10where
11 D: serde::Deserializer<'de>,
12{
13 let value = Value::deserialize(deserializer)?;
14 match value {
15 Value::String(text) => Ok(text),
16 Value::Number(number) => Ok(number.to_string()),
17 other => Err(D::Error::custom(format!(
18 "expected string or number, got {other}"
19 ))),
20 }
21}
22
23fn deserialize_opt_stringified<'de, D>(
24 deserializer: D,
25) -> std::result::Result<Option<String>, D::Error>
26where
27 D: serde::Deserializer<'de>,
28{
29 Option::<Value>::deserialize(deserializer)?
30 .map(|value| match value {
31 Value::String(text) => Ok(text),
32 Value::Number(number) => Ok(number.to_string()),
33 other => Err(D::Error::custom(format!(
34 "expected string or number, got {other}"
35 ))),
36 })
37 .transpose()
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
41pub struct ExtendedAccount {
42 pub name: String,
43 #[serde(default, deserialize_with = "deserialize_opt_stringified")]
44 pub reputation: Option<String>,
45 #[serde(default)]
46 pub memo_key: Option<String>,
47 #[serde(flatten)]
48 pub extra: BTreeMap<String, Value>,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
52pub struct AccountReputation {
53 pub account: String,
54 #[serde(deserialize_with = "deserialize_stringified")]
55 pub reputation: String,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
59pub struct OwnerHistory {
60 #[serde(default)]
61 pub account: Option<String>,
62 #[serde(default)]
63 pub previous_owner_authority: Option<Value>,
64 #[serde(default)]
65 pub last_valid_time: Option<String>,
66 #[serde(flatten)]
67 pub extra: BTreeMap<String, Value>,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
71pub struct RecoveryRequest {
72 #[serde(default)]
73 pub account_to_recover: Option<String>,
74 #[serde(default)]
75 pub recovery_account: Option<String>,
76 #[serde(default)]
77 pub new_owner_authority: Option<Value>,
78 #[serde(default)]
79 pub expires: Option<String>,
80 #[serde(flatten)]
81 pub extra: BTreeMap<String, Value>,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
85pub struct AccountHistoryEntry {
86 pub index: u64,
87 #[serde(default)]
88 pub timestamp: Option<String>,
89 #[serde(default)]
90 pub op: Option<Operation>,
91 #[serde(flatten)]
92 pub extra: BTreeMap<String, Value>,
93}
94
95#[cfg(test)]
96mod tests {
97 use serde_json::json;
98
99 use crate::types::{AccountReputation, ExtendedAccount};
100
101 #[test]
102 fn extended_account_supports_numeric_reputation() {
103 let account: ExtendedAccount = serde_json::from_value(json!({
104 "name": "beggars",
105 "reputation": 0,
106 "memo_key": "STM1111111111111111111111111111111114T1Anm",
107 }))
108 .expect("account should deserialize");
109
110 assert_eq!(account.name, "beggars");
111 assert_eq!(account.reputation.as_deref(), Some("0"));
112 }
113
114 #[test]
115 fn account_reputation_supports_numeric_reputation() {
116 let reputation: AccountReputation = serde_json::from_value(json!({
117 "account": "alice",
118 "reputation": 12345,
119 }))
120 .expect("reputation should deserialize");
121
122 assert_eq!(reputation.account, "alice");
123 assert_eq!(reputation.reputation, "12345");
124 }
125}