Add TimestampMetadataBuilder to simplify making TimestampMetadata
diff --git a/src/metadata.rs b/src/metadata.rs
index 5713be8..68b47bf 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -257,20 +257,22 @@
     /// # use chrono::prelude::*;
     /// # use tuf::crypto::{PrivateKey, SignatureScheme, HashAlgorithm};
     /// # use tuf::interchange::Json;
-    /// # use tuf::metadata::{MetadataDescription, TimestampMetadata, SignedMetadata};
+    /// # use tuf::metadata::{MetadataDescription, TimestampMetadataBuilder};
     /// #
     /// # fn main() {
     /// # let key: &[u8] = include_bytes!("../tests/ed25519/ed25519-1.pk8.der");
     /// let key = PrivateKey::from_pkcs8(&key, SignatureScheme::Ed25519).unwrap();
     ///
-    /// let timestamp = TimestampMetadata::new(
+    /// let description = MetadataDescription::from_reader(
+    ///     &[0x01, 0x02, 0x03][..],
     ///     1,
-    ///     Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
-    ///     MetadataDescription::from_reader(&*vec![0x01, 0x02, 0x03], 1,
-    ///         &[HashAlgorithm::Sha256]).unwrap()
+    ///     &[HashAlgorithm::Sha256]
     /// ).unwrap();
     ///
-    /// SignedMetadata::<Json, _>::new(timestamp, &key).unwrap();
+    /// let timestamp = TimestampMetadataBuilder::from_metadata_description(description)
+    ///     .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+    ///     .signed::<Json>(&key)
+    ///     .unwrap();
     /// # }
     /// ```
     pub fn new(metadata: M, private_key: &PrivateKey) -> Result<SignedMetadata<D, M>> {
@@ -934,6 +936,78 @@
     }
 }
 
+/// Helper to construct `TimestampMetadata`.
+pub struct TimestampMetadataBuilder {
+    version: u32,
+    expires: DateTime<Utc>,
+    snapshot: MetadataDescription,
+}
+
+impl TimestampMetadataBuilder {
+    /// Create a new `TimestampMetadataBuilder` from a given snapshot. It defaults to:
+    ///
+    /// * version: 1
+    /// * expires: 1 day from the current time.
+    pub fn from_snapshot<D, M>(
+        snapshot: &SignedMetadata<D, M>,
+        hash_algs: &[HashAlgorithm],
+    ) -> Result<Self>
+    where
+        D: DataInterchange,
+        M: Metadata,
+    {
+        let bytes = D::canonicalize(&D::serialize(&snapshot)?)?;
+        let description = MetadataDescription::from_reader(
+            &*bytes,
+            snapshot.version(),
+            hash_algs,
+        )?;
+
+        Ok(Self::from_metadata_description(description))
+    }
+
+    /// Create a new `TimestampMetadataBuilder` from a given
+    /// `MetadataDescription`. It defaults to:
+    ///
+    /// * version: 1
+    /// * expires: 1 day from the current time.
+    pub fn from_metadata_description(description: MetadataDescription) -> Self {
+        TimestampMetadataBuilder {
+            version: 1,
+            expires: Utc::now() + Duration::days(1),
+            snapshot: description,
+        }
+    }
+
+    /// Set the version number for this metadata.
+    pub fn version(mut self, version: u32) -> Self {
+        self.version = version;
+        self
+    }
+
+    /// Set the time this metadata expires.
+    pub fn expires(mut self, expires: DateTime<Utc>) -> Self {
+        self.expires = expires;
+        self
+    }
+
+    /// Construct a new `TimestampMetadata`.
+    pub fn build(self) -> Result<TimestampMetadata> {
+        TimestampMetadata::new(
+            self.version,
+            self.expires,
+            self.snapshot,
+        )
+    }
+
+    /// Construct a new `SignedMetadata<D, TimestampMetadata>`.
+    pub fn signed<D>(self, private_key: &PrivateKey) -> Result<SignedMetadata<D, TimestampMetadata>>
+        where D: DataInterchange,
+    {
+        Ok(SignedMetadata::new(self.build()?, private_key)?)
+    }
+}
+
 /// Metadata for the timestamp role.
 #[derive(Debug, Clone, PartialEq)]
 pub struct TimestampMetadata {
@@ -1942,16 +2016,17 @@
 
     #[test]
     fn serde_timestamp_metadata() {
-        let timestamp = TimestampMetadata::new(
+        let description = MetadataDescription::new(
             1,
-            Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
-            MetadataDescription::new(
-                1,
-                100,
-                hashmap! { HashAlgorithm::Sha256 => HashValue::new(vec![]) },
-            ).unwrap(),
+            100,
+            hashmap! { HashAlgorithm::Sha256 => HashValue::new(vec![]) },
         ).unwrap();
 
+        let timestamp = TimestampMetadataBuilder::from_metadata_description(description)
+            .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+            .build()
+            .unwrap();
+
         let jsn = json!({
             "type": "timestamp",
             "version": 1,
@@ -2203,12 +2278,17 @@
     }
 
     fn make_timestamp() -> json::Value {
-        let timestamp = TimestampMetadata::new(
+        let description = MetadataDescription::from_reader(
+            &[][..],
             1,
-            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
-            MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
+            &[HashAlgorithm::Sha256],
         ).unwrap();
 
+        let timestamp = TimestampMetadataBuilder::from_metadata_description(description)
+            .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+            .build()
+            .unwrap();
+
         json::to_value(&timestamp).unwrap()
     }
 
diff --git a/src/tuf.rs b/src/tuf.rs
index f7e2311..d285d6d 100644
--- a/src/tuf.rs
+++ b/src/tuf.rs
@@ -616,7 +616,12 @@
     use chrono::prelude::*;
     use crypto::{HashAlgorithm, PrivateKey, SignatureScheme};
     use interchange::Json;
-    use metadata::{MetadataDescription, RootMetadataBuilder, SnapshotMetadataBuilder};
+    use metadata::{
+        MetadataDescription,
+        RootMetadataBuilder,
+        SnapshotMetadataBuilder,
+        TimestampMetadataBuilder,
+    };
 
     lazy_static! {
         static ref KEYS: Vec<PrivateKey> = {
@@ -726,13 +731,14 @@
 
         let mut tuf = Tuf::from_root(root).unwrap();
 
-        let timestamp = TimestampMetadata::new(
-            1,
-            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
-            MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
-        ).unwrap();
-        let timestamp: SignedMetadata<Json, TimestampMetadata> =
-            SignedMetadata::new(timestamp, &KEYS[1]).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .signed::<Json>(&KEYS[1])
+            .unwrap();
+
+        let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+            .unwrap()
+            .signed::<Json>(&KEYS[1])
+            .unwrap();
 
         assert_eq!(tuf.update_timestamp(timestamp.clone()), Ok(true));
 
@@ -752,15 +758,15 @@
 
         let mut tuf = Tuf::from_root(root).unwrap();
 
-        let timestamp = TimestampMetadata::new(
-            1,
-            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
-            MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
-        ).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .signed::<Json>(&KEYS[1])
+            .unwrap();
 
-        // sign it with the root key
-        let timestamp: SignedMetadata<Json, TimestampMetadata> =
-            SignedMetadata::new(timestamp, &KEYS[0]).unwrap();
+        let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+            .unwrap()
+            // sign it with the root key
+            .signed::<Json>(&KEYS[0])
+            .unwrap();
 
         assert!(tuf.update_timestamp(timestamp).is_err())
     }
@@ -777,20 +783,17 @@
 
         let mut tuf = Tuf::from_root(root).unwrap();
 
-        let timestamp = TimestampMetadata::new(
-            1,
-            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
-            MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
-        ).unwrap();
-        let timestamp: SignedMetadata<Json, TimestampMetadata> =
-            SignedMetadata::new(timestamp, &KEYS[2]).unwrap();
-
-        tuf.update_timestamp(timestamp).unwrap();
-
         let snapshot = SnapshotMetadataBuilder::new()
             .signed(&KEYS[1])
             .unwrap();
 
+        let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+            .unwrap()
+            .signed::<Json>(&KEYS[2])
+            .unwrap();
+
+        tuf.update_timestamp(timestamp).unwrap();
+
         assert_eq!(tuf.update_snapshot(snapshot.clone()), Ok(true));
 
         // second update should do nothing
@@ -809,20 +812,17 @@
 
         let mut tuf = Tuf::from_root(root).unwrap();
 
-        let timestamp = TimestampMetadata::new(
-            1,
-            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
-            MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
-        ).unwrap();
-        let timestamp: SignedMetadata<Json, TimestampMetadata> =
-            SignedMetadata::new(timestamp, &KEYS[2]).unwrap();
-
-        tuf.update_timestamp(timestamp).unwrap();
-
         let snapshot = SnapshotMetadataBuilder::new()
             .signed::<Json>(&KEYS[2])
             .unwrap();
 
+        let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+            .unwrap()
+            .signed::<Json>(&KEYS[2])
+            .unwrap();
+
+        tuf.update_timestamp(timestamp).unwrap();
+
         assert!(tuf.update_snapshot(snapshot).is_err());
     }
 
@@ -838,13 +838,15 @@
 
         let mut tuf = Tuf::from_root(root).unwrap();
 
-        let timestamp = TimestampMetadata::new(
-            1,
-            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
-            MetadataDescription::from_reader(&*vec![], 2, &[HashAlgorithm::Sha256]).unwrap(),
-        ).unwrap();
-        let timestamp: SignedMetadata<Json, TimestampMetadata> =
-            SignedMetadata::new(timestamp, &KEYS[2]).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .version(2)
+            .signed::<Json>(&KEYS[2])
+            .unwrap();
+
+        let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+            .unwrap()
+            .signed::<Json>(&KEYS[2])
+            .unwrap();
 
         tuf.update_timestamp(timestamp).unwrap();
 
@@ -865,18 +867,6 @@
             .signed::<Json>(&KEYS[0])
             .unwrap();
 
-        let mut tuf = Tuf::from_root(root).unwrap();
-
-        let timestamp = TimestampMetadata::new(
-            1,
-            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
-            MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
-        ).unwrap();
-        let timestamp: SignedMetadata<Json, _> =
-            SignedMetadata::new(timestamp, &KEYS[3]).unwrap();
-
-        tuf.update_timestamp(timestamp).unwrap();
-
         let snapshot = SnapshotMetadataBuilder::new()
             .insert_metadata_description(
                 MetadataPath::from_role(&Role::Targets),
@@ -885,6 +875,14 @@
             .signed::<Json>(&KEYS[1])
             .unwrap();
 
+        let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+            .unwrap()
+            .signed::<Json>(&KEYS[3])
+            .unwrap();
+
+        let mut tuf = Tuf::from_root(root).unwrap();
+
+        tuf.update_timestamp(timestamp).unwrap();
         tuf.update_snapshot(snapshot).unwrap();
 
         let targets =
@@ -911,16 +909,6 @@
 
         let mut tuf = Tuf::from_root(root).unwrap();
 
-        let timestamp = TimestampMetadata::new(
-            1,
-            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
-            MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
-        ).unwrap();
-        let timestamp: SignedMetadata<Json, TimestampMetadata> =
-            SignedMetadata::new(timestamp, &KEYS[3]).unwrap();
-
-        tuf.update_timestamp(timestamp).unwrap();
-
         let snapshot = SnapshotMetadataBuilder::new()
             .insert_metadata_description(
                 MetadataPath::from_role(&Role::Targets),
@@ -929,6 +917,12 @@
             .signed::<Json>(&KEYS[1])
             .unwrap();
 
+        let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+            .unwrap()
+            .signed::<Json>(&KEYS[3])
+            .unwrap();
+
+        tuf.update_timestamp(timestamp).unwrap();
         tuf.update_snapshot(snapshot).unwrap();
 
         let targets =
@@ -952,16 +946,6 @@
 
         let mut tuf = Tuf::from_root(root).unwrap();
 
-        let timestamp = TimestampMetadata::new(
-            1,
-            Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
-            MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
-        ).unwrap();
-        let timestamp: SignedMetadata<Json, TimestampMetadata> =
-            SignedMetadata::new(timestamp, &KEYS[3]).unwrap();
-
-        tuf.update_timestamp(timestamp).unwrap();
-
         let snapshot = SnapshotMetadataBuilder::new()
             .insert_metadata_description(
                 MetadataPath::from_role(&Role::Targets),
@@ -970,6 +954,12 @@
             .signed::<Json>(&KEYS[1])
             .unwrap();
 
+        let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+            .unwrap()
+            .signed::<Json>(&KEYS[3])
+            .unwrap();
+
+        tuf.update_timestamp(timestamp).unwrap();
         tuf.update_snapshot(snapshot).unwrap();
 
         let targets =
diff --git a/tests/integration.rs b/tests/integration.rs
index 700f980..8cd4455 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -9,8 +9,9 @@
 use tuf::crypto::{HashAlgorithm, PrivateKey, SignatureScheme};
 use tuf::interchange::Json;
 use tuf::metadata::{
-    Delegation, Delegations, MetadataDescription, MetadataPath, RootMetadataBuilder,
-    SignedMetadata, SnapshotMetadataBuilder, TargetDescription, TargetsMetadata, TimestampMetadata,
+    Delegation, Delegations, MetadataDescription, MetadataPath,
+    RootMetadataBuilder, SignedMetadata, SnapshotMetadataBuilder,
+    TargetDescription, TargetsMetadata, TimestampMetadataBuilder,
     VirtualTargetPath,
 };
 use tuf::Tuf;
@@ -32,7 +33,7 @@
 
     //// build the root ////
 
-    let signed = RootMetadataBuilder::new()
+    let root = RootMetadataBuilder::new()
         .root_key(root_key.public().clone())
         .snapshot_key(snapshot_key.public().clone())
         .targets_key(targets_key.public().clone())
@@ -40,20 +41,11 @@
         .signed::<Json>(&root_key)
         .unwrap();
 
-    let mut tuf = Tuf::<Json>::from_root_pinned(signed, &[root_key.key_id().clone()]).unwrap();
+    let mut tuf = Tuf::<Json>::from_root_pinned(root, &[root_key.key_id().clone()]).unwrap();
 
-    //// build the timestamp ////
-    let snap = MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap();
-    let timestamp = TimestampMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), snap).unwrap();
+    //// build the snapshot and timestamp ////
 
-    let signed =
-        SignedMetadata::<Json, _>::new(timestamp, &timestamp_key).unwrap();
-
-    tuf.update_timestamp(signed).unwrap();
-
-    //// build the snapshot ////
-
-    let signed = SnapshotMetadataBuilder::new()
+    let snapshot = SnapshotMetadataBuilder::new()
         .insert_metadata_description(
             MetadataPath::new("targets".into()).unwrap(),
             MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
@@ -65,7 +57,13 @@
         .signed::<Json>(&snapshot_key)
         .unwrap();
 
-    tuf.update_snapshot(signed).unwrap();
+    let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+        .unwrap()
+        .signed::<Json>(&timestamp_key)
+        .unwrap();
+
+    tuf.update_timestamp(timestamp).unwrap();
+    tuf.update_snapshot(snapshot).unwrap();
 
     //// build the targets ////
     let delegations = Delegations::new(
@@ -129,7 +127,7 @@
 
     //// build the root ////
 
-    let signed = RootMetadataBuilder::new()
+    let root = RootMetadataBuilder::new()
         .root_key(root_key.public().clone())
         .snapshot_key(snapshot_key.public().clone())
         .targets_key(targets_key.public().clone())
@@ -137,20 +135,11 @@
         .signed::<Json>(&root_key)
         .unwrap();
 
-    let mut tuf = Tuf::<Json>::from_root_pinned(signed, &[root_key.key_id().clone()]).unwrap();
+    let mut tuf = Tuf::<Json>::from_root_pinned(root, &[root_key.key_id().clone()]).unwrap();
 
-    //// build the timestamp ////
-    let snap = MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap();
-    let timestamp = TimestampMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), snap).unwrap();
+    //// build the snapshot and timestamp ////
 
-    let signed =
-        SignedMetadata::<Json, _>::new(timestamp, &timestamp_key).unwrap();
-
-    tuf.update_timestamp(signed).unwrap();
-
-    //// build the snapshot ////
-
-    let signed = SnapshotMetadataBuilder::new()
+    let snapshot = SnapshotMetadataBuilder::new()
         .insert_metadata_description(
             MetadataPath::new("targets".into()).unwrap(),
             MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
@@ -166,9 +155,16 @@
         .signed::<Json>(&snapshot_key)
         .unwrap();
 
-    tuf.update_snapshot(signed).unwrap();
+    let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
+        .unwrap()
+        .signed::<Json>(&timestamp_key)
+        .unwrap();
+
+    tuf.update_timestamp(timestamp).unwrap();
+    tuf.update_snapshot(snapshot).unwrap();
 
     //// build the targets ////
+
     let delegations = Delegations::new(
         &hashset![delegation_a_key.public().clone()],
         vec![
@@ -199,6 +195,7 @@
     tuf.update_targets(signed).unwrap();
 
     //// build delegation A ////
+
     let delegations = Delegations::new(
         &hashset![delegation_b_key.public().clone()],
         vec![
@@ -231,6 +228,7 @@
         .unwrap();
 
     //// build delegation B ////
+
     let target_file: &[u8] = b"bar";
     let target_map = hashmap! {
         VirtualTargetPath::new("foo".into()).unwrap() =>
diff --git a/tests/simple_example.rs b/tests/simple_example.rs
index 6d6ed08..e2b4028 100644
--- a/tests/simple_example.rs
+++ b/tests/simple_example.rs
@@ -7,11 +7,11 @@
 use chrono::prelude::*;
 use tuf::client::{Client, Config, PathTranslator};
 use tuf::crypto::{HashAlgorithm, KeyId, PrivateKey, SignatureScheme};
-use tuf::interchange::{DataInterchange, Json};
+use tuf::interchange::Json;
 use tuf::metadata::{
-    MetadataDescription, MetadataPath, MetadataVersion, RootMetadataBuilder, SignedMetadata,
-    SnapshotMetadataBuilder, TargetDescription, TargetPath, TargetsMetadata, TimestampMetadata,
-    VirtualTargetPath,
+    MetadataPath, MetadataVersion, RootMetadataBuilder, SignedMetadata,
+    SnapshotMetadataBuilder, TargetDescription, TargetPath, TargetsMetadata,
+    TimestampMetadataBuilder, VirtualTargetPath,
 };
 use tuf::repository::{EphemeralRepository, Repository};
 use tuf::Result;
@@ -128,38 +128,35 @@
 
     //// build the snapshot ////
 
-    let signed = SnapshotMetadataBuilder::new()
+    let snapshot = SnapshotMetadataBuilder::new()
         .insert_metadata(&signed, &[HashAlgorithm::Sha256])?
         .signed::<Json>(&snapshot_key)?;
 
     remote.store_metadata(
         &MetadataPath::new("snapshot".into())?,
         &MetadataVersion::Number(1),
-        &signed,
+        &snapshot,
     )?;
     remote.store_metadata(
         &MetadataPath::new("snapshot".into())?,
         &MetadataVersion::None,
-        &signed,
+        &snapshot,
     )?;
 
-    let snapshot_bytes = Json::canonicalize(&Json::serialize(&signed)?)?;
-
     //// build the timestamp ////
-    let snap = MetadataDescription::from_reader(&*snapshot_bytes, 1, &[HashAlgorithm::Sha256])?;
-    let timestamp = TimestampMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), snap)?;
 
-    let signed = SignedMetadata::<Json, _>::new(timestamp, &timestamp_key)?;
+    let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])?
+        .signed::<Json>(&timestamp_key)?;
 
     remote.store_metadata(
         &MetadataPath::new("timestamp".into())?,
         &MetadataVersion::Number(1),
-        &signed,
+        &timestamp,
     )?;
     remote.store_metadata(
         &MetadataPath::new("timestamp".into())?,
         &MetadataVersion::None,
-        &signed,
+        &timestamp,
     )?;
 
     Ok(vec![root_key.key_id().clone()])