Created
October 14, 2018 18:10
-
-
Save AljoschaMeyer/c4d3c80dd81a4d57615929bc87781192 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate ssb_legacy_msg_data; | |
extern crate ssb_multiformats; | |
use ssb_legacy_msg_data::{ | |
StringlyTypedError, | |
LegacyF64, | |
Serialize, | |
Deserialize, | |
Deserializer, | |
de::{ | |
Visitor, | |
ObjectAccess, | |
} | |
}; | |
use ssb_multiformats::{multihash::Multihash, multikey::Multikey}; | |
pub enum Content<V> { | |
Encrypted(String), | |
Value(V) | |
} | |
/// A complete ssb message, signed and all. | |
pub struct Message<V> { | |
previous: Option<Multihash>, | |
author: Multikey, | |
sequence: u64, | |
timestamp: LegacyF64, | |
content: Content<V>, | |
swapped: bool, | |
signature: Vec<u8> | |
} | |
impl<'de, V> Deserialize<'de> for Message<V> where V: Deserialize<'de> { | |
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> { | |
deserializer.deserialize_object(MessageVisitor::new()) | |
} | |
} | |
struct MessageVisitor<V> { | |
_v: std::marker::PhantomData<V> | |
} | |
impl<V> MessageVisitor<V> { | |
fn new() -> MessageVisitor<V> { | |
MessageVisitor { | |
_v: std::marker::PhantomData | |
} | |
} | |
} | |
impl<'de, V> Visitor<'de> for MessageVisitor<V> where V: Deserialize<'de> { | |
type Value = Message<V>; | |
fn visit_object<A>(self, mut object: A) -> Result<Self::Value, A::Error> where A: ObjectAccess<'de> { | |
let previous; | |
match object.next_entry::<MaybeMultihash>()? { | |
None => return Err(A::Error::custom(format!("Missing `previous` metadata"))), | |
Some((key, val)) => { | |
if key == "previous" { | |
previous = val; | |
} else { | |
return Err(A::Error::custom(format!("First metadata entry must be `previous`"))); | |
} | |
} | |
} | |
let swapped; | |
let author; | |
let sequence; | |
match object.next_key()? { | |
None => return Err(A::Error::custom(format!("Missing `author` metadata"))), | |
Some(key) => { | |
if key == "author" { | |
swapped = false; | |
author = object.next_value::<Multikey>()?; | |
match object.next_entry::<LegacyF64>()? { | |
None => return Err(A::Error::custom(format!("Missing `sequence` metadata"))), | |
Some((key, val)) => { | |
if key == "sequence" { | |
let f: f64 = val.into(); | |
sequence = f as u64; | |
} else { | |
return Err(A::Error::custom(format!("Third metadata entry must be `sequence`"))); | |
} | |
} | |
} | |
} else if key == "sequence" { | |
swapped = true; | |
let f: f64 = object.next_value::<LegacyF64>()?.into(); | |
sequence = f as u64; | |
match object.next_entry::<Multikey>()? { | |
None => return Err(A::Error::custom(format!("Missing `author` metadata"))), | |
Some((key, val)) => { | |
if key == "author" { | |
author = val; | |
} else { | |
return Err(A::Error::custom(format!("Third metadata entry must be `author`"))); | |
} | |
} | |
} | |
} else { | |
return Err(A::Error::custom(format!("Second metadata entry must be `author` or `sequence`"))); | |
} | |
} | |
} | |
let timestamp; | |
match object.next_entry::<LegacyF64>()? { | |
None => return Err(A::Error::custom(format!("Missing `timestamp` metadata"))), | |
Some((key, val)) => { | |
if key == "timestamp" { | |
timestamp = val; | |
} else { | |
return Err(A::Error::custom(format!("Fourth metadata entry must be `timestamp`"))); | |
} | |
} | |
} | |
match object.next_entry::<String>()? { | |
None => return Err(A::Error::custom(format!("Missing `hash` metadata"))), | |
Some((key, val)) => { | |
if key == "hash" { | |
if val != "sha256" { | |
return Err(A::Error::custom(format!("Fifth metadata entry must be `hash`"))); | |
} | |
} else { | |
return Err(A::Error::custom(format!("Fifth metadata entry must be `hash`"))); | |
} | |
} | |
} | |
let content; | |
match object.next_entry::<V>()? { | |
None => return Err(A::Error::custom(format!("Missing `content` metadata"))), | |
Some((key, val)) => { | |
if key == "content" { | |
content = val; | |
} else { | |
return Err(A::Error::custom(format!("Sixth metadata entry must be `content`"))); | |
} | |
} | |
} | |
let sig_string; | |
match object.next_entry::<String>()? { | |
None => return Err(A::Error::custom(format!("Missing `signature` metadata"))), | |
Some((key, val)) => { | |
if key == "signature" { | |
sig_string = val; | |
} else { | |
return Err(A::Error::custom(format!("Seventh metadata entry must be `signature`"))); | |
} | |
} | |
} | |
let signature; | |
match author.deserialize_signature(sig_string.as_bytes()) { | |
Ok(sig) => signature = sig, | |
Err(e) => return Err(A::Error::custom(format!("{}", e))), | |
} | |
if let Some(_) = object.next_key()? { | |
return Err(A::Error::custom(format!("Message must contain exactly seven entries"))); | |
} | |
Ok(Message { | |
previous: previous.0, | |
author, | |
sequence, | |
timestamp, | |
swapped, | |
content, | |
signature, | |
}) | |
} | |
} | |
struct MaybeMultihash(Option<Multihash>); | |
impl<'de> Deserialize<'de> for MaybeMultihash { | |
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> { | |
deserializer.deserialize_object(MaybeMultihashVisitor) | |
} | |
} | |
struct MaybeMultihashVisitor; | |
impl<'de> Visitor<'de> for MaybeMultihashVisitor { | |
type Value = MaybeMultihash; | |
fn visit_null<E>(self) -> Result<Self::Value, E> where E: StringlyTypedError { | |
return Ok(MaybeMultihash(None)); | |
} | |
fn visit_string<E>(self, v: String) -> Result<Self::Value, E> where E: StringlyTypedError { | |
Multihash::from_legacy(&v.as_bytes()).map(|mh| MaybeMultihash(Some(mh))) | |
.map_err(|err| E::custom(format!("Invalid multihash: {}", err))) | |
} | |
} | |
struct ContentVisitor<V> { | |
_v: std::marker::PhantomData<V> | |
} | |
impl<V> ContentVisitor<V> { | |
fn new() -> ContentVisitor<V> { | |
ContentVisitor { | |
_v: std::marker::PhantomData | |
} | |
} | |
} | |
impl<'de, V> Visitor<'de> for ContentVisitor<V> where V: Deserialize<'de> { | |
type Value = Content<V>; | |
fn visit_object<A>(self, mut object: A) -> Result<Self::Value, A::Error> where A: ObjectAccess<'de> { | |
unimplemented!() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment