walletkit_core/
field_element.rs1use std::ops::Deref;
4use std::str::FromStr;
5
6use world_id_core::FieldElement as CoreFieldElement;
7
8use crate::error::WalletKitError;
9
10#[allow(clippy::module_name_repetitions)]
18#[derive(Debug, Clone, uniffi::Object)]
19pub struct FieldElement(pub CoreFieldElement);
20
21#[uniffi::export]
22impl FieldElement {
23 #[uniffi::constructor]
29 pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, WalletKitError> {
30 let len = bytes.len();
31 let val: [u8; 32] =
32 bytes.try_into().map_err(|_| WalletKitError::InvalidInput {
33 attribute: "field_element".to_string(),
34 reason: format!("Expected 32 bytes for field element, got {len}"),
35 })?;
36
37 let field_element = CoreFieldElement::from_be_bytes(&val)?;
38 Ok(Self(field_element))
39 }
40
41 #[must_use]
45 #[uniffi::constructor]
46 pub fn from_u64(value: u64) -> Self {
47 Self(CoreFieldElement::from(value))
48 }
49
50 #[must_use]
54 pub fn to_bytes(&self) -> Vec<u8> {
55 self.0.to_be_bytes().to_vec()
56 }
57
58 #[uniffi::constructor]
66 pub fn try_from_hex_string(hex_string: &str) -> Result<Self, WalletKitError> {
67 let fe = CoreFieldElement::from_str(hex_string)?;
68 Ok(Self(fe))
69 }
70
71 #[must_use]
73 pub fn to_hex_string(&self) -> String {
74 self.0.to_string()
75 }
76}
77
78impl From<FieldElement> for CoreFieldElement {
79 fn from(val: FieldElement) -> Self {
80 val.0
81 }
82}
83
84impl From<CoreFieldElement> for FieldElement {
85 fn from(val: CoreFieldElement) -> Self {
86 Self(val)
87 }
88}
89
90impl From<u64> for FieldElement {
91 fn from(value: u64) -> Self {
92 Self(CoreFieldElement::from(value))
93 }
94}
95
96impl Deref for FieldElement {
97 type Target = CoreFieldElement;
98
99 fn deref(&self) -> &Self::Target {
100 &self.0
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_from_u64() {
110 let fe = FieldElement::from_u64(42);
111 let bytes = fe.to_bytes();
112 assert!(!bytes.is_empty());
113 assert_eq!(bytes[31], 0x2a);
114 }
115
116 #[test]
117 fn test_round_trip_bytes() {
118 let original = FieldElement::from_u64(12345);
119 let bytes = original.to_bytes();
120 let restored = FieldElement::from_bytes(bytes).unwrap();
121
122 let original_bytes = original.to_bytes();
124 let restored_bytes = restored.to_bytes();
125 assert_eq!(original_bytes, restored_bytes);
126 }
127
128 #[test]
129 fn test_hex_round_trip() {
130 let original = FieldElement::from_u64(999);
131 let hex = original.to_hex_string();
132 let restored = FieldElement::try_from_hex_string(&hex).unwrap();
133
134 let original_bytes = original.to_bytes();
135 let restored_bytes = restored.to_bytes();
136 assert_eq!(original_bytes, restored_bytes);
137 }
138
139 #[test]
140 fn test_hex_string_with_and_without_0x() {
141 let fe = FieldElement::from_u64(255);
142 let hex = fe.to_hex_string();
143
144 let with_prefix = FieldElement::try_from_hex_string(&hex).unwrap();
146
147 let hex_no_prefix = hex.trim_start_matches("0x");
149 let without_prefix = FieldElement::try_from_hex_string(hex_no_prefix).unwrap();
150
151 let with_bytes = with_prefix.to_bytes();
152 let without_bytes = without_prefix.to_bytes();
153 assert_eq!(with_bytes, without_bytes);
154 }
155
156 #[test]
157 fn test_invalid_hex_string() {
158 assert!(FieldElement::try_from_hex_string("0xZZZZ").is_err());
159 assert!(FieldElement::try_from_hex_string("not hex").is_err());
160 }
161
162 #[test]
164 fn test_encoding_round_trip() {
165 let sub_one = CoreFieldElement::from(42u64);
166 let sub_two = FieldElement::from(sub_one);
167
168 assert_eq!(sub_one, *sub_two);
169 assert_eq!(sub_one.to_string(), sub_two.to_hex_string());
170
171 let sub_three =
172 FieldElement::try_from_hex_string(&sub_two.to_hex_string()).unwrap();
173 assert_eq!(sub_one, *sub_three);
174 }
175}