1// Rust JSON-RPC Library
2// Written in 2019 by
3// Andrew Poelstra <apoelstra@wpsoftware.net>
4//
5// To the extent possible under law, the author(s) have dedicated all
6// copyright and related and neighboring rights to this software to
7// the public domain worldwide. This software is distributed without
8// any warranty.
9//
10// You should have received a copy of the CC0 Public Domain Dedication
11// along with this software.
12// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13//
1415use std::borrow::Cow;
16use std::hash::{Hash, Hasher};
1718use serde_json::Value;
1920/// Newtype around `Value` which allows hashing for use as hashmap keys
21/// This is needed for batch requests.
22///
23/// The reason `Value` does not support `Hash` or `Eq` by itself
24/// is that it supports `f64` values; but for batch requests we
25/// will only be hashing the "id" field of the request/response
26/// pair, which should never need decimal precision and therefore
27/// never use `f64`.
28#[derive(Clone, PartialEq, Debug)]
29pub struct HashableValue<'a>(pub Cow<'a, Value>);
3031impl<'a> Eq for HashableValue<'a> {}
3233impl<'a> Hash for HashableValue<'a> {
34fn hash<H: Hasher>(&self, state: &mut H) {
35match *self.0.as_ref() {
36 Value::Null => "null".hash(state),
37 Value::Bool(false) => "false".hash(state),
38 Value::Bool(true) => "true".hash(state),
39 Value::Number(ref n) => {
40"number".hash(state);
41if let Some(n) = n.as_i64() {
42 n.hash(state);
43 } else if let Some(n) = n.as_u64() {
44 n.hash(state);
45 } else {
46 n.to_string().hash(state);
47 }
48 }
49 Value::String(ref s) => {
50"string".hash(state);
51 s.hash(state);
52 }
53 Value::Array(ref v) => {
54"array".hash(state);
55 v.len().hash(state);
56for obj in v {
57 HashableValue(Cow::Borrowed(obj)).hash(state);
58 }
59 }
60 Value::Object(ref m) => {
61"object".hash(state);
62 m.len().hash(state);
63for (key, val) in m {
64 key.hash(state);
65 HashableValue(Cow::Borrowed(val)).hash(state);
66 }
67 }
68 }
69 }
70}
7172#[cfg(test)]
73mod tests {
74use std::borrow::Cow;
75use std::collections::HashSet;
76use std::str::FromStr;
7778use super::*;
7980#[test]
81fn hash_value() {
82let val = HashableValue(Cow::Owned(Value::from_str("null").unwrap()));
83let t = HashableValue(Cow::Owned(Value::from_str("true").unwrap()));
84let f = HashableValue(Cow::Owned(Value::from_str("false").unwrap()));
85let ns =
86 HashableValue(Cow::Owned(Value::from_str("[0, -0, 123.4567, -100000000]").unwrap()));
87let m =
88 HashableValue(Cow::Owned(Value::from_str("{ \"field\": 0, \"field\": -0 }").unwrap()));
8990let mut coll = HashSet::new();
9192assert!(!coll.contains(&val));
93 coll.insert(val.clone());
94assert!(coll.contains(&val));
9596assert!(!coll.contains(&t));
97assert!(!coll.contains(&f));
98 coll.insert(t.clone());
99assert!(coll.contains(&t));
100assert!(!coll.contains(&f));
101 coll.insert(f.clone());
102assert!(coll.contains(&t));
103assert!(coll.contains(&f));
104105assert!(!coll.contains(&ns));
106 coll.insert(ns.clone());
107assert!(coll.contains(&ns));
108109assert!(!coll.contains(&m));
110 coll.insert(m.clone());
111assert!(coll.contains(&m));
112 }
113}