Skip to content

Instantly share code, notes, and snippets.

@AljoschaMeyer
Created October 14, 2018 18:10
Show Gist options
  • Save AljoschaMeyer/c4d3c80dd81a4d57615929bc87781192 to your computer and use it in GitHub Desktop.
Save AljoschaMeyer/c4d3c80dd81a4d57615929bc87781192 to your computer and use it in GitHub Desktop.
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