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 {
55 None
56 } else {
57 Some(self.as_json_str())
58 }
59 }
60
61 #[inline]
63 pub const fn as_json_str(self) -> &'static str {
64 match self {
65 Self::Pure => "pure",
66 Self::View => "view",
67 Self::NonPayable => "nonpayable",
68 Self::Payable => "payable",
69 }
70 }
71}
72
73#[cfg(feature = "serde")]
106pub mod serde_state_mutability_compat {
107 use super::*;
108 use serde::ser::SerializeStruct;
109
110 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
114 deserializer: D,
115 ) -> Result<StateMutability, D::Error> {
116 #[derive(Deserialize)]
117 #[serde(rename_all = "camelCase")]
118 struct StateMutabilityCompat {
119 #[serde(default)]
120 state_mutability: Option<StateMutability>,
121 #[serde(default)]
122 payable: Option<bool>,
123 #[serde(default)]
124 constant: Option<bool>,
125 }
126
127 impl StateMutabilityCompat {
128 fn flatten(self) -> Option<StateMutability> {
129 let Self { state_mutability, payable, constant } = self;
130 if state_mutability.is_some() {
131 return state_mutability;
132 }
133 match (payable.unwrap_or(false), constant.unwrap_or(false)) {
134 (false, false) => Some(StateMutability::default()),
135 (true, false) => Some(StateMutability::Payable),
136 (false, true) => Some(StateMutability::View),
137 (true, true) => None,
138 }
139 }
140 }
141
142 StateMutabilityCompat::deserialize(deserializer).and_then(|compat| {
143 compat.flatten().ok_or_else(|| serde::de::Error::custom(COMPAT_ERROR))
144 })
145 }
146
147 pub fn serialize<S: serde::Serializer>(
151 state_mutability: &StateMutability,
152 serializer: S,
153 ) -> Result<S::Ok, S::Error> {
154 let mut s = serializer.serialize_struct("StateMutability", 1)?;
155 s.serialize_field("stateMutability", state_mutability)?;
156 s.end()
157 }
158}
159
160#[cfg(all(test, feature = "serde"))]
161mod tests {
162 use super::*;
163 use alloc::string::ToString;
164
165 #[derive(Debug, Serialize, Deserialize)]
166 struct CompatTest {
167 #[serde(default, flatten, with = "serde_state_mutability_compat")]
168 sm: StateMutability,
169 }
170
171 #[test]
172 fn test_compat() {
173 let test = |expect: StateMutability, json: &str| {
174 let compat = serde_json::from_str::<CompatTest>(json).expect(json);
175 assert_eq!(compat.sm, expect, "{json:?}");
176
177 let re_ser = serde_json::to_string(&compat).expect(json);
178 let expect = format!(r#"{{"stateMutability":"{}"}}"#, expect.as_json_str());
179 assert_eq!(re_ser, expect, "{json:?}");
180 };
181
182 test(StateMutability::Pure, r#"{"stateMutability":"pure"}"#);
183 test(
184 StateMutability::Pure,
185 r#"{"stateMutability":"pure","constant":false,"payable":false}"#,
186 );
187
188 test(StateMutability::View, r#"{"constant":true}"#);
189 test(StateMutability::View, r#"{"constant":true,"payable":false}"#);
190
191 test(StateMutability::Payable, r#"{"payable":true}"#);
192 test(StateMutability::Payable, r#"{"constant":false,"payable":true}"#);
193
194 test(StateMutability::NonPayable, r#"{}"#);
195 test(StateMutability::NonPayable, r#"{"constant":false}"#);
196 test(StateMutability::NonPayable, r#"{"payable":false}"#);
197 test(StateMutability::NonPayable, r#"{"constant":false,"payable":false}"#);
198
199 let json = r#"{"constant":true,"payable":true}"#;
200 let e = serde_json::from_str::<CompatTest>(json).unwrap_err().to_string();
201 assert!(e.contains(COMPAT_ERROR), "{e:?}");
202 }
203}