diff --git a/.changelog/unreleased/improvements/1374-compatmode-instances.md b/.changelog/unreleased/improvements/1374-compatmode-instances.md new file mode 100644 index 00000000..82f21432 --- /dev/null +++ b/.changelog/unreleased/improvements/1374-compatmode-instances.md @@ -0,0 +1 @@ +- `[cometbft-rpc]` Add `FromStr`, `Serialize` and `Deserialize` instances to `CompatMode` ([\#21](https://github.com/cometbft/cometbft-rs/issues/21)) diff --git a/rpc/src/client/compat.rs b/rpc/src/client/compat.rs index c19a9e10..c5a9e81f 100644 --- a/rpc/src/client/compat.rs +++ b/rpc/src/client/compat.rs @@ -1,6 +1,9 @@ //! Support for dynamic compatibility with older protocol versions. use core::fmt; +use core::str::FromStr; + +use serde::{de::Deserializer, Deserialize, Serialize, Serializer}; use cometbft::Version; @@ -15,6 +18,12 @@ pub enum CompatMode { /// Use a compatibility mode for the RPC protocol used since CometBFT 0.37. /// This is the default mode that has persisted into CometBFT 1.0. V0_37, + // NOTE: When adding a newer version, do not forget to update: + // - CompatMode::latest() + // - CompatMode::from_version() + // - impl Display for CompatMode + // - impl FromStr for CompatMode + // - The tests } impl Default for CompatMode { @@ -66,6 +75,45 @@ impl fmt::Display for CompatMode { } } +impl FromStr for CompatMode { + type Err = Error; + + fn from_str(s: &str) -> Result { + const VALID_COMPAT_MODES: &str = "v0.34, v0.37"; + + // Trim leading 'v', if present + match s.trim_start_matches('v') { + "0.34" => Ok(CompatMode::V0_34), + "0.37" => Ok(CompatMode::V0_37), + _ => Err(Error::invalid_compat_mode( + s.to_string(), + VALID_COMPAT_MODES, + )), + } + } +} + +impl<'de> Deserialize<'de> for CompatMode { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de; + + let s = String::deserialize(deserializer)?; + FromStr::from_str(&s).map_err(de::Error::custom) + } +} + +impl Serialize for CompatMode { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.to_string().serialize(serializer) + } +} + #[cfg(test)] mod tests { use super::CompatMode; @@ -112,4 +160,17 @@ mod tests { let res = CompatMode::from_version(parse_version("poobah")); assert!(res.is_err()); } + + #[test] + fn test_from_str() { + assert_eq!("0.34".parse::().unwrap(), CompatMode::V0_34); + assert_eq!("0.37".parse::().unwrap(), CompatMode::V0_37); + + let res = "0.33".parse::(); + assert!(res.is_err()); + let res = "0.38".parse::(); + assert!(res.is_err()); + let res = "foobar".parse::(); + assert!(res.is_err()); + } } diff --git a/rpc/src/error.rs b/rpc/src/error.rs index 230a4a5a..ae3c0fd5 100644 --- a/rpc/src/error.rs +++ b/rpc/src/error.rs @@ -216,6 +216,16 @@ define_error! { | e | { format_args!("unsupported CometBFT version reported by the node: {}", e.version) }, + + InvalidCompatMode + { + mode: String, + supported: &'static str, + } + | e | { + format_args!("invalid compatibility mode: '{}' (supported: {})", + e.mode, e.supported) + }, } }