alloy_sol_type_parser/
state_mutability.rs1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4#[cfg(feature = "serde")]
5const COMPAT_ERROR: &str = "state mutability cannot be both `payable` and `constant`";
6
7#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
15pub enum StateMutability {
16 Pure,
18 View,
20 #[default]
27 NonPayable,
28 Payable,
30}
31
32impl core::str::FromStr for StateMutability {
33 type Err = ();
34
35 fn from_str(s: &str) -> Result<Self, Self::Err> {
36 Self::parse(s).ok_or(())
37 }
38}
39
40impl StateMutability {
41 pub fn parse(s: &str) -> Option<Self> {
43 match s {
44 "pure" => Some(Self::Pure),
45 "view" => Some(Self::View),
46 "payable" => Some(Self::Payable),
47 _ => None,
48 }
49 }
50
51 #[inline]
53 pub const fn as_str(self) -> Option<&'static str> {
54 if let Self::NonPayable = self { None } else { Some(self.as_json_str()) }
55 }
56
57 #[inline]
59 pub const fn as_json_str(self) -> &'static str {
60 match self {
61 Self::Pure => "pure",
62 Self::View => "view",
63 Self::NonPayable => "nonpayable",
64 Self::Payable => "payable",
65 }
66 }
67}
68
69#[cfg(feature = "serde")]
102pub mod serde_state_mutability_compat {
103 use super::*;
104 use serde::ser::SerializeStruct;
105
106 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
110 deserializer: D,
111 ) -> Result<StateMutability, D::Error> {
112 #[derive(Deserialize)]
113 #[serde(rename_all = "camelCase")]
114 struct StateMutabilityCompat {
115 #[serde(default)]
116 state_mutability: Option<StateMutability>,
117 #[serde(default)]
118 payable: Option<bool>,
119 #[serde(default)]
120 constant: Option<bool>,
121 }
122
123 impl StateMutabilityCompat {
124 fn flatten(self) -> Option<StateMutability> {
125 let Self { state_mutability, payable, constant } = self;
126 if state_mutability.is_some() {
127 return state_mutability;
128 }
129 match (payable.unwrap_or(false), constant.unwrap_or(false)) {
130 (false, false) => Some(StateMutability::default()),
131 (true, false) => Some(StateMutability::Payable),
132 (false, true) => Some(StateMutability::View),
133 (true, true) => None,
134 }
135 }
136 }
137
138 StateMutabilityCompat::deserialize(deserializer).and_then(|compat| {
139 compat.flatten().ok_or_else(|| serde::de::Error::custom(COMPAT_ERROR))
140 })
141 }
142
143 pub fn serialize<S: serde::Serializer>(
147 state_mutability: &StateMutability,
148 serializer: S,
149 ) -> Result<S::Ok, S::Error> {
150 let mut s = serializer.serialize_struct("StateMutability", 1)?;
151 s.serialize_field("stateMutability", state_mutability)?;
152 s.end()
153 }
154}
155
156#[cfg(all(test, feature = "serde"))]
157mod tests {
158 use super::*;
159 use alloc::string::ToString;
160
161 #[derive(Debug, Serialize, Deserialize)]
162 struct CompatTest {
163 #[serde(default, flatten, with = "serde_state_mutability_compat")]
164 sm: StateMutability,
165 }
166
167 #[test]
168 fn test_compat() {
169 let test = |expect: StateMutability, json: &str| {
170 let compat = serde_json::from_str::<CompatTest>(json).expect(json);
171 assert_eq!(compat.sm, expect, "{json:?}");
172
173 let re_ser = serde_json::to_string(&compat).expect(json);
174 let expect = format!(r#"{{"stateMutability":"{}"}}"#, expect.as_json_str());
175 assert_eq!(re_ser, expect, "{json:?}");
176 };
177
178 test(StateMutability::Pure, r#"{"stateMutability":"pure"}"#);
179 test(
180 StateMutability::Pure,
181 r#"{"stateMutability":"pure","constant":false,"payable":false}"#,
182 );
183
184 test(StateMutability::View, r#"{"constant":true}"#);
185 test(StateMutability::View, r#"{"constant":true,"payable":false}"#);
186
187 test(StateMutability::Payable, r#"{"payable":true}"#);
188 test(StateMutability::Payable, r#"{"constant":false,"payable":true}"#);
189
190 test(StateMutability::NonPayable, r#"{}"#);
191 test(StateMutability::NonPayable, r#"{"constant":false}"#);
192 test(StateMutability::NonPayable, r#"{"payable":false}"#);
193 test(StateMutability::NonPayable, r#"{"constant":false,"payable":false}"#);
194
195 let json = r#"{"constant":true,"payable":true}"#;
196 let e = serde_json::from_str::<CompatTest>(json).unwrap_err().to_string();
197 assert!(e.contains(COMPAT_ERROR), "{e:?}");
198 }
199}