hakuban 0.7.2

Data-object sharing library
Documentation
use std::{collections::HashSet, hash::{Hash, Hasher}};

use anyhow::Result;
use serde::{Deserialize, Serialize};


/// Unique tag identifier
#[derive(Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct TagDescriptor {
	pub json: serde_json::Value,
}

/// Unique object identifier
#[derive(Clone, Serialize, Deserialize)]
pub struct ObjectDescriptor {
	pub tags: HashSet<TagDescriptor>,
	pub json: serde_json::Value,
}


impl ObjectDescriptor {
	pub fn new<Q: Into<TagDescriptor>, S: IntoIterator<Item = Q>, T: Into<serde_json::value::Value>>(tags: S, json: T) -> ObjectDescriptor {
		let json: serde_json::value::Value = json.into();
		ObjectDescriptor { tags: tags.into_iter().map(|descriptor| descriptor.into()).collect(), json }
	}

	pub fn json_to_string(&self) -> String {
		self.json.to_string()
	}

	pub fn tags_to_strings(&self) -> Vec<String> {
		self.tags.iter().map(|tag| tag.json_to_string()).collect()
	}

	pub fn from_json_strings(tags: Vec<String>, json: String) -> Result<ObjectDescriptor> {
		let tags: HashSet<TagDescriptor> = tags.into_iter().map(TagDescriptor::from_json_string).collect::<Result<_, anyhow::Error>>()?;
		let json: serde_json::value::Value = serde_json::from_str(json.as_str())?;
		Ok(ObjectDescriptor { tags, json })
	}
}

impl TagDescriptor {
	pub fn new<T: Into<serde_json::value::Value>>(value: T) -> TagDescriptor {
		let json: serde_json::value::Value = value.into();
		TagDescriptor { json }
	}

	pub fn json_to_string(&self) -> String {
		self.json.to_string()
	}

	pub fn from_json_string(json: String) -> Result<TagDescriptor> {
		let json: serde_json::value::Value = serde_json::from_str(json.as_str())?;
		Ok(TagDescriptor { json })
	}
}



use itertools::Itertools;


pub(crate) fn hash_serde_json_value<H: Hasher>(value: &serde_json::Value, state: &mut H) {
	match value {
		serde_json::Value::Null => {
			state.write_u8(0);
		}
		serde_json::Value::Bool(value) => {
			state.write_u8(1);
			state.write_u8(u8::from(*value));
		}
		serde_json::Value::Number(value) => {
			if value.is_f64() {
				state.write_u8(2);
				state.write_u64(value.as_f64().unwrap() as u64); //????
			} else if value.is_i64() {
				state.write_u8(3);
				state.write_i64(value.as_i64().unwrap());
			} else if value.is_u64() {
				state.write_u8(4);
				state.write_u64(value.as_u64().unwrap());
			} else {
				panic!()
			};
		}
		serde_json::Value::String(value) => {
			state.write_u8(5);
			state.write(value.as_bytes());
		}
		serde_json::Value::Array(value) => {
			state.write_u8(6);
			for value in value {
				hash_serde_json_value(value, state);
			}
		}
		serde_json::Value::Object(value) => {
			state.write_u8(7);
			for key in value.keys().cloned().sorted() {
				//is this copying entire string or cow?
				state.write(key.as_bytes());
				hash_serde_json_value(value.get(&key).unwrap(), state);
			}
		}
	}
}

impl<T: Into<serde_json::value::Value>> From<T> for TagDescriptor {
	fn from(value: T) -> Self {
		let json: serde_json::value::Value = value.into();
		TagDescriptor { json }
	}
}

impl<Q: Into<serde_json::value::Value>, S: IntoIterator<Item = Q>, T: Into<serde_json::value::Value>> From<(S, T)> for ObjectDescriptor {
	fn from(value: (S, T)) -> Self {
		let json: serde_json::value::Value = value.1.into();
		ObjectDescriptor { tags: value.0.into_iter().map(|descriptor| TagDescriptor { json: descriptor.into() }).collect(), json }
	}
}

impl<T: Into<serde_json::value::Value>> From<(T,)> for ObjectDescriptor {
	fn from(value: (T,)) -> Self {
		let json: serde_json::value::Value = value.0.into();
		ObjectDescriptor { tags: HashSet::new(), json }
	}
}


impl From<serde_json::value::Value> for ObjectDescriptor {
	fn from(value: serde_json::value::Value) -> Self {
		ObjectDescriptor { tags: HashSet::new(), json: value }
	}
}


impl PartialEq for TagDescriptor {
	fn eq(&self, other: &Self) -> bool {
		self.json == other.json
	}
}

impl PartialEq for ObjectDescriptor {
	fn eq(&self, other: &Self) -> bool {
		self.json == other.json && self.tags.symmetric_difference(&other.tags).count() == 0
	}
}

impl Eq for ObjectDescriptor {}
impl Eq for TagDescriptor {}


impl Hash for ObjectDescriptor {
	fn hash<H: Hasher>(&self, state: &mut H) {
		hash_serde_json_value(&self.json, state);
		//rn deliberately collides on objects with same jsons but different tag sets. should hash tags-set too when we get hash-cache in place.
	}
}

impl Hash for TagDescriptor {
	fn hash<H: Hasher>(&self, state: &mut H) {
		hash_serde_json_value(&self.json, state);
	}
}


impl std::fmt::Debug for ObjectDescriptor {
	fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		if fmt.alternate() {
			fmt.write_str("ObjectDescriptor:")?;
		}
		fmt.write_str("[")?;
		for (i, tag) in self.tags.iter().enumerate() {
			tag.fmt(fmt)?;
			if i < self.tags.len() - 1 {
				fmt.write_str(",")?;
			}
		}
		fmt.write_str("]:")?;
		fmt.write_fmt(format_args!("{}", self.json))
	}
}

impl std::fmt::Display for ObjectDescriptor {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		f.write_fmt(format_args!("{}", self.json))
	}
}

impl std::fmt::Debug for TagDescriptor {
	fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		if fmt.alternate() {
			fmt.write_str("TagDescriptor:")?;
		}
		fmt.write_fmt(format_args!("{}", self.json))
	}
}

impl std::fmt::Display for TagDescriptor {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		f.write_fmt(format_args!("{}", self.json))
	}
}


/*
#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
pub enum Descriptor {
	Object(ObjectDescriptor),
	Tag(TagDescriptor),
}
*/

#[cfg(test)]
mod tests {

	use super::*;

	#[test]
	fn tag_descriptor_can_be_serialized_and_deserialized_with_json() {
		let serialized1 = b"{}";
		let descriptor = serde_json::from_slice::<TagDescriptor>(serialized1).unwrap();
		let mut serialized2: Vec<u8> = vec![];
		descriptor.serialize(&mut serde_json::Serializer::new(&mut serialized2)).unwrap();
		assert_eq!(serialized2, serialized1);
	}

	#[test]
	fn tag_descriptor_can_be_constructed_from_whatever_is_convertible_to_json() {
		let _x: TagDescriptor = "aoeu".to_string().into();
		let _y: TagDescriptor = serde_json::json!({"aaa": 123}).to_string().into();
	}

	#[test]
	fn object_descriptor_can_be_serialized_and_deserialized_with_json() {
		let serialized1 = b"{\"tags\":[\"bbbb\",\"aaa\"],\"json\":\"{}\"}";
		let descriptor = serde_json::from_slice::<ObjectDescriptor>(serialized1).unwrap();
		//let descriptor: ObjectDescriptor = (vec!["aaa","bbbb"], "{}").into();
		let mut serialized2: Vec<u8> = vec![];
		descriptor.serialize(&mut serde_json::Serializer::new(&mut serialized2)).unwrap();
		let descriptor2 = serde_json::from_slice::<ObjectDescriptor>(serialized2.as_slice()).unwrap();
		//println!("{}",String::from_utf8(serialized2).unwrap());
		assert_eq!(descriptor, descriptor2);
	}
}