cuprate_json_rpc/version.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
//! [`Version`]: JSON-RPC 2.0 version marker.
//---------------------------------------------------------------------------------------------------- Use
use serde::de::{Error, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
//---------------------------------------------------------------------------------------------------- Version
/// [Protocol version marker](https://www.jsonrpc.org/specification#compatibility).
///
/// This represents the JSON-RPC version.
///
/// This is an empty marker type that always gets (de)serialized as [`Self::TWO`].
///
/// It is the only valid value for the `jsonrpc` field in the
/// [`Request`](crate::Request) and [`Response`](crate::Request) objects.
///
/// JSON-RPC 2.0 allows for backwards compatibility with `1.0` but this crate
/// (and this type) will not accept that, and will fail in deserialization
/// when encountering anything but [`Self::TWO`].
///
/// # Formatting
/// When using Rust formatting, [`Version`] is formatted as `2.0`.
///
/// When using JSON serialization, `Version` is formatted with quotes indicating
/// it is a JSON string and not a JSON float, i.e. it gets formatted as `"2.0"`, not `2.0`.
///
/// # Example
/// ```rust
/// use cuprate_json_rpc::Version;
/// use serde_json::{to_string, to_string_pretty, from_str};
///
/// assert_eq!(Version::TWO, "2.0");
/// let version = Version;
///
/// // All debug/display formats are the same.
/// assert_eq!(format!("{version:?}"), Version::TWO);
/// assert_eq!(format!("{version:#?}"), Version::TWO);
/// assert_eq!(format!("{version}"), Version::TWO);
///
/// // JSON serialization will add extra quotes to
/// // indicate it is a string and not a float.
/// assert_eq!(to_string(&Version).unwrap(), "\"2.0\"");
/// assert_eq!(to_string_pretty(&Version).unwrap(), "\"2.0\"");
///
/// // Deserialization only accepts the JSON string "2.0".
/// assert!(from_str::<Version>(&"\"2.0\"").is_ok());
/// // This is JSON float, not a string.
/// assert!(from_str::<Version>(&"2.0").is_err());
///
/// assert!(from_str::<Version>(&"2").is_err());
/// assert!(from_str::<Version>(&"1.0").is_err());
/// assert!(from_str::<Version>(&"20").is_err());
/// assert!(from_str::<Version>(&"two").is_err());
/// assert!(from_str::<Version>(&"2.1").is_err());
/// assert!(from_str::<Version>(&"v2.0").is_err());
/// assert!(from_str::<Version>("").is_err());
/// ```
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Version;
impl Version {
/// The string `2.0`.
///
/// Note that this does not have extra quotes to mark
/// that it's a JSON string and not a float.
/// ```rust
/// use cuprate_json_rpc::Version;
///
/// let string = format!("{}", Version);
/// assert_eq!(string, "2.0");
/// assert_ne!(string, "\"2.0\"");
/// ```
pub const TWO: &'static str = "2.0";
}
//---------------------------------------------------------------------------------------------------- Trait impl
impl Serialize for Version {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
s.serialize_str(Self::TWO)
}
}
impl std::fmt::Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", Self::TWO)
}
}
impl std::fmt::Debug for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", Self::TWO)
}
}
//---------------------------------------------------------------------------------------------------- Serde impl
/// Empty serde visitor for [`Version`].
struct VersionVisitor;
impl Visitor<'_> for VersionVisitor {
type Value = Version;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("Identifier must be the exact string: \"2.0\"")
}
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
if v == Version::TWO {
Ok(Version)
} else {
Err(Error::invalid_value(serde::de::Unexpected::Str(v), &self))
}
}
}
impl<'de> Deserialize<'de> for Version {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
d.deserialize_str(VersionVisitor)
}
}