use std::fmt;
use std::io::{Read, Write};
use attestation::Attestation;
use error::Error;
use hex::Hexed;
use op::Op;
use ser;
const RECURSION_LIMIT: usize = 256;
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum StepData {
Fork,
Op(Op),
Attestation(Attestation)
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Step {
pub data: StepData,
pub output: Vec<u8>,
pub next: Vec<Step>
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Timestamp {
pub start_digest: Vec<u8>,
pub first_step: Step
}
impl Timestamp {
fn deserialize_step_recurse<R: Read>(deser: &mut ser::Deserializer<R>, input_digest: Vec<u8>, tag: Option<u8>, recursion_limit: usize) -> Result<Step, Error> {
if recursion_limit == 0 {
return Err(Error::StackOverflow);
}
let tag = match tag {
Some(tag) => tag,
None => deser.read_byte()?
};
match tag {
0x00 => {
let attest = Attestation::deserialize(deser)?;
trace!("[{:3}] Attestation: {}", recursion_limit, attest);
Ok(Step {
data: StepData::Attestation(attest),
output: input_digest,
next: vec![]
})
}
0xff => {
let mut forks = vec![];
let mut next_tag = 0xff;
while next_tag == 0xff {
trace!("[{:3}] Forking..", recursion_limit);
forks.push(Timestamp::deserialize_step_recurse(deser, input_digest.clone(), None, recursion_limit - 1)?);
next_tag = deser.read_byte()?;
}
forks.push(Timestamp::deserialize_step_recurse(deser, input_digest.clone(), Some(next_tag), recursion_limit - 1)?);
Ok(Step {
data: StepData::Fork,
output: input_digest,
next: forks
})
}
tag => {
let op = Op::deserialize_with_tag(deser, tag)?;
let output_digest = op.execute(&input_digest);
trace!("[{:3}] Tag {} maps {} to {}.", recursion_limit, op, Hexed(&input_digest), Hexed(&output_digest));
let next = vec![Timestamp::deserialize_step_recurse(deser, output_digest.clone(), None, recursion_limit - 1)?];
Ok(Step {
data: StepData::Op(op),
output: output_digest,
next: next
})
}
}
}
pub fn deserialize<R: Read>(deser: &mut ser::Deserializer<R>, digest: Vec<u8>) -> Result<Timestamp, Error> {
let first_step = Timestamp::deserialize_step_recurse(deser, digest.clone(), None, RECURSION_LIMIT)?;
Ok(Timestamp {
start_digest: digest,
first_step: first_step
})
}
fn serialize_step_recurse<W: Write>(ser: &mut ser::Serializer<W>, step: &Step) -> Result<(), Error> {
match step.data {
StepData::Fork => {
for i in 0..step.next.len() - 1 {
ser.write_byte(0xff)?;
Timestamp::serialize_step_recurse(ser, &step.next[i])?;
}
Timestamp::serialize_step_recurse(ser, &step.next[step.next.len() - 1])
}
StepData::Op(ref op) => {
op.serialize(ser)?;
Timestamp::serialize_step_recurse(ser, &step.next[0])
}
StepData::Attestation(ref attest) => {
ser.write_byte(0x00)?;
attest.serialize(ser)
}
}
}
pub fn serialize<W: Write>(&self, ser: &mut ser::Serializer<W>) -> Result<(), Error> {
Timestamp::serialize_step_recurse(ser, &self.first_step)
}
}
fn fmt_recurse(step: &Step, f: &mut fmt::Formatter, depth: usize, first_line: bool) -> fmt::Result {
fn indent(f: &mut fmt::Formatter, depth: usize, first_line: bool) -> fmt::Result {
if depth == 0 {
return Ok(());
}
for _ in 0..depth-1 {
f.write_str(" ")?;
}
if first_line {
f.write_str("--->")?;
} else {
f.write_str(" ")?;
}
Ok(())
}
match step.data {
StepData::Fork => {
indent(f, depth, first_line)?;
writeln!(f, "(fork {} ways)", step.next.len())?;
for fork in &step.next {
fmt_recurse(fork, f, depth + 1, true)?;
}
Ok(())
}
StepData::Op(ref op) => {
indent(f, depth, first_line)?;
writeln!(f, "execute {}", op)?;
indent(f, depth, false)?;
writeln!(f, " result {}", Hexed(&step.output))?;
fmt_recurse(&step.next[0], f, depth, false)
}
StepData::Attestation(ref attest) => {
indent(f, depth, first_line)?;
writeln!(f, "result attested by {}", attest)
}
}
}
impl fmt::Display for Timestamp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Starting digest: {}", Hexed(&self.start_digest))?;
fmt_recurse(&self.first_step, f, 0, false)
}
}