Add SnapshotMetadataBuilder to simplify making SnapshotMetadata
diff --git a/src/metadata.rs b/src/metadata.rs
index 99315c5..5713be8 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -180,16 +180,21 @@
             _ => false,
         }
     }
+
+    /// Return the name of the role.
+    pub fn name(&self) -> &'static str {
+        match *self {
+            Role::Root => "root",
+            Role::Snapshot => "snapshot",
+            Role::Targets => "targets",
+            Role::Timestamp => "timestamp",
+        }
+    }
 }
 
 impl Display for Role {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            Role::Root => write!(f, "root"),
-            Role::Snapshot => write!(f, "snapshot"),
-            Role::Targets => write!(f, "targets"),
-            Role::Timestamp => write!(f, "timestamp"),
-        }
+        f.write_str(self.name())
     }
 }
 
@@ -1083,6 +1088,114 @@
     }
 }
 
+/// Helper to construct `SnapshotMetadata`.
+pub struct SnapshotMetadataBuilder {
+    version: u32,
+    expires: DateTime<Utc>,
+    meta: HashMap<MetadataPath, MetadataDescription>,
+}
+
+impl SnapshotMetadataBuilder {
+    /// Create a new `SnapshotMetadataBuilder`. It defaults to:
+    ///
+    /// * version: 1
+    /// * expires: 7 days from the current time.
+    pub fn new() -> Self {
+        SnapshotMetadataBuilder {
+            version: 1,
+            expires: Utc::now() + Duration::days(7),
+            meta: HashMap::new(),
+        }
+    }
+
+    /// 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
+    }
+
+    /// Add metadata to the snapshot using the default path.
+    pub fn insert_metadata<D, M>(
+        self,
+        metadata: &SignedMetadata<D, M>,
+        hash_algs: &[HashAlgorithm],
+    ) -> Result<Self>
+    where
+          M: Metadata,
+          D: DataInterchange,
+    {
+        self.insert_metadata_with_path(
+            M::ROLE.name(),
+            metadata,
+            hash_algs,
+        )
+    }
+
+    /// Add metadata to the snapshot using a custom path.
+    pub fn insert_metadata_with_path<P, D, M>(
+        self,
+        path: P,
+        metadata: &SignedMetadata<D, M>,
+        hash_algs: &[HashAlgorithm],
+    ) -> Result<Self>
+    where
+          P: Into<String>,
+          M: Metadata,
+          D: DataInterchange,
+    {
+        let bytes = D::canonicalize(&D::serialize(metadata)?)?;
+        let description = MetadataDescription::from_reader(
+            &*bytes,
+            metadata.version(),
+            hash_algs,
+        )?;
+        let path = MetadataPath::new(path.into())?;
+        Ok(self.insert_metadata_description(path, description))
+    }
+
+    /// Add `MetadataDescription` to the snapshot using a custom path.
+    pub fn insert_metadata_description(
+        mut self,
+        path: MetadataPath,
+        description: MetadataDescription,
+    ) -> Self {
+        self.meta.insert(path.into(), description);
+        self
+    }
+
+    /// Construct a new `SignedMetadata`.
+    pub fn build(self) -> Result<SnapshotMetadata> {
+        SnapshotMetadata::new(
+            self.version,
+            self.expires,
+            self.meta,
+        )
+    }
+
+    /// Construct a new `SignedMetadata<D, SnapshotMetadata>`.
+    pub fn signed<D>(self, private_key: &PrivateKey) -> Result<SignedMetadata<D, SnapshotMetadata>>
+        where D: DataInterchange,
+    {
+        Ok(SignedMetadata::new(self.build()?, private_key)?)
+    }
+}
+
+impl From<SnapshotMetadata> for SnapshotMetadataBuilder {
+    fn from(meta: SnapshotMetadata) -> Self {
+        SnapshotMetadataBuilder {
+            version: meta.version,
+            expires: meta.expires,
+            meta: meta.meta,
+        }
+    }
+}
+
 /// Metadata for the snapshot role.
 #[derive(Debug, Clone, PartialEq)]
 pub struct SnapshotMetadata {
@@ -1860,18 +1973,18 @@
 
     #[test]
     fn serde_snapshot_metadata() {
-        let snapshot = SnapshotMetadata::new(
-            1,
-            Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
-            hashmap! {
-                MetadataPath::new("foo".into()).unwrap() =>
-                    MetadataDescription::new(
-                        1,
-                        100,
-                        hashmap! { HashAlgorithm::Sha256 => HashValue::new(vec![]) },
-                    ).unwrap(),
-            },
-        ).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+            .insert_metadata_description(
+                MetadataPath::new("foo".into()).unwrap(),
+                MetadataDescription::new(
+                    1,
+                    100,
+                    hashmap! { HashAlgorithm::Sha256 => HashValue::new(vec![]) },
+                ).unwrap(),
+            )
+            .build()
+            .unwrap();
 
         let jsn = json!({
             "type": "snapshot",
@@ -1986,18 +2099,18 @@
 
     #[test]
     fn serde_signed_metadata() {
-        let snapshot = SnapshotMetadata::new(
-            1,
-            Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
-            hashmap! {
-                MetadataPath::new("foo".into()).unwrap() =>
-                    MetadataDescription::new(
-                        1,
-                        100,
-                        hashmap! { HashAlgorithm::Sha256 => HashValue::new(vec![]) },
-                    ).unwrap(),
-            },
-        ).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+            .insert_metadata_description(
+                MetadataPath::new("foo".into()).unwrap(),
+                MetadataDescription::new(
+                    1,
+                    100,
+                    hashmap! { HashAlgorithm::Sha256 => HashValue::new(vec![]) },
+                ).unwrap(),
+            )
+            .build()
+            .unwrap();
 
         let key = PrivateKey::from_pkcs8(ED25519_1_PK8, SignatureScheme::Ed25519).unwrap();
 
@@ -2081,8 +2194,10 @@
     }
 
     fn make_snapshot() -> json::Value {
-        let snapshot =
-            SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!()).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .expires(Utc.ymd(2038, 1, 1).and_hms(0, 0, 0))
+            .build()
+            .unwrap();
 
         json::to_value(&snapshot).unwrap()
     }
diff --git a/src/tuf.rs b/src/tuf.rs
index c6c7b5e..f7e2311 100644
--- a/src/tuf.rs
+++ b/src/tuf.rs
@@ -616,7 +616,7 @@
     use chrono::prelude::*;
     use crypto::{HashAlgorithm, PrivateKey, SignatureScheme};
     use interchange::Json;
-    use metadata::{MetadataDescription, RootMetadataBuilder};
+    use metadata::{MetadataDescription, RootMetadataBuilder, SnapshotMetadataBuilder};
 
     lazy_static! {
         static ref KEYS: Vec<PrivateKey> = {
@@ -787,10 +787,9 @@
 
         tuf.update_timestamp(timestamp).unwrap();
 
-        let snapshot =
-            SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!()).unwrap();
-        let snapshot: SignedMetadata<Json, SnapshotMetadata> =
-            SignedMetadata::new(snapshot, &KEYS[1]).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .signed(&KEYS[1])
+            .unwrap();
 
         assert_eq!(tuf.update_snapshot(snapshot.clone()), Ok(true));
 
@@ -820,10 +819,9 @@
 
         tuf.update_timestamp(timestamp).unwrap();
 
-        let snapshot =
-            SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!()).unwrap();
-        let snapshot: SignedMetadata<Json, SnapshotMetadata> =
-            SignedMetadata::new(snapshot, &KEYS[2]).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .signed::<Json>(&KEYS[2])
+            .unwrap();
 
         assert!(tuf.update_snapshot(snapshot).is_err());
     }
@@ -850,10 +848,9 @@
 
         tuf.update_timestamp(timestamp).unwrap();
 
-        let snapshot =
-            SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!()).unwrap();
-        let snapshot: SignedMetadata<Json, SnapshotMetadata> =
-            SignedMetadata::new(snapshot, &KEYS[1]).unwrap();
+        let snapshot = SnapshotMetadataBuilder::new()
+            .signed::<Json>(&KEYS[1])
+            .unwrap();
 
         assert!(tuf.update_snapshot(snapshot).is_err());
     }
@@ -880,14 +877,13 @@
 
         tuf.update_timestamp(timestamp).unwrap();
 
-        let meta_map = hashmap!(
-            MetadataPath::from_role(&Role::Targets) =>
+        let snapshot = SnapshotMetadataBuilder::new()
+            .insert_metadata_description(
+                MetadataPath::from_role(&Role::Targets),
                 MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
-        );
-        let snapshot =
-            SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map).unwrap();
-        let snapshot: SignedMetadata<Json, _> =
-            SignedMetadata::new(snapshot, &KEYS[1]).unwrap();
+            )
+            .signed::<Json>(&KEYS[1])
+            .unwrap();
 
         tuf.update_snapshot(snapshot).unwrap();
 
@@ -925,14 +921,13 @@
 
         tuf.update_timestamp(timestamp).unwrap();
 
-        let meta_map = hashmap!(
-            MetadataPath::from_role(&Role::Targets) =>
+        let snapshot = SnapshotMetadataBuilder::new()
+            .insert_metadata_description(
+                MetadataPath::from_role(&Role::Targets),
                 MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
-        );
-        let snapshot =
-            SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map).unwrap();
-        let snapshot: SignedMetadata<Json, SnapshotMetadata> =
-            SignedMetadata::new(snapshot, &KEYS[1]).unwrap();
+            )
+            .signed::<Json>(&KEYS[1])
+            .unwrap();
 
         tuf.update_snapshot(snapshot).unwrap();
 
@@ -967,14 +962,13 @@
 
         tuf.update_timestamp(timestamp).unwrap();
 
-        let meta_map = hashmap!(
-            MetadataPath::from_role(&Role::Targets) =>
+        let snapshot = SnapshotMetadataBuilder::new()
+            .insert_metadata_description(
+                MetadataPath::from_role(&Role::Targets),
                 MetadataDescription::from_reader(&*vec![], 2, &[HashAlgorithm::Sha256]).unwrap(),
-        );
-        let snapshot =
-            SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map).unwrap();
-        let snapshot: SignedMetadata<Json, SnapshotMetadata> =
-            SignedMetadata::new(snapshot, &KEYS[1]).unwrap();
+            )
+            .signed::<Json>(&KEYS[1])
+            .unwrap();
 
         tuf.update_snapshot(snapshot).unwrap();
 
diff --git a/tests/integration.rs b/tests/integration.rs
index f732a73..700f980 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -10,7 +10,7 @@
 use tuf::interchange::Json;
 use tuf::metadata::{
     Delegation, Delegations, MetadataDescription, MetadataPath, RootMetadataBuilder,
-    SignedMetadata, SnapshotMetadata, TargetDescription, TargetsMetadata, TimestampMetadata,
+    SignedMetadata, SnapshotMetadataBuilder, TargetDescription, TargetsMetadata, TimestampMetadata,
     VirtualTargetPath,
 };
 use tuf::Tuf;
@@ -52,16 +52,18 @@
     tuf.update_timestamp(signed).unwrap();
 
     //// build the snapshot ////
-    let meta_map = hashmap! {
-        MetadataPath::new("targets".into()).unwrap() =>
-            MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
-        MetadataPath::new("delegation".into()).unwrap() =>
-            MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
-    };
-    let snapshot =
-        SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map).unwrap();
 
-    let signed = SignedMetadata::<Json, _>::new(snapshot, &snapshot_key).unwrap();
+    let signed = SnapshotMetadataBuilder::new()
+        .insert_metadata_description(
+            MetadataPath::new("targets".into()).unwrap(),
+            MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
+        )
+        .insert_metadata_description(
+            MetadataPath::new("delegation".into()).unwrap(),
+            MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
+        )
+        .signed::<Json>(&snapshot_key)
+        .unwrap();
 
     tuf.update_snapshot(signed).unwrap();
 
@@ -147,18 +149,22 @@
     tuf.update_timestamp(signed).unwrap();
 
     //// build the snapshot ////
-    let meta_map = hashmap! {
-        MetadataPath::new("targets".into()).unwrap() =>
-            MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
-        MetadataPath::new("delegation-a".into()).unwrap() =>
-            MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
-        MetadataPath::new("delegation-b".into()).unwrap() =>
-            MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
-    };
-    let snapshot =
-        SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map).unwrap();
 
-    let signed = SignedMetadata::<Json, _>::new(snapshot, &snapshot_key).unwrap();
+    let signed = SnapshotMetadataBuilder::new()
+        .insert_metadata_description(
+            MetadataPath::new("targets".into()).unwrap(),
+            MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
+        )
+        .insert_metadata_description(
+            MetadataPath::new("delegation-a".into()).unwrap(),
+            MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
+        )
+        .insert_metadata_description(
+            MetadataPath::new("delegation-b".into()).unwrap(),
+            MetadataDescription::from_reader(&*vec![0u8], 1, &[HashAlgorithm::Sha256]).unwrap(),
+        )
+        .signed::<Json>(&snapshot_key)
+        .unwrap();
 
     tuf.update_snapshot(signed).unwrap();
 
diff --git a/tests/simple_example.rs b/tests/simple_example.rs
index f264d1b..6d6ed08 100644
--- a/tests/simple_example.rs
+++ b/tests/simple_example.rs
@@ -10,7 +10,7 @@
 use tuf::interchange::{DataInterchange, Json};
 use tuf::metadata::{
     MetadataDescription, MetadataPath, MetadataVersion, RootMetadataBuilder, SignedMetadata,
-    SnapshotMetadata, TargetDescription, TargetPath, TargetsMetadata, TimestampMetadata,
+    SnapshotMetadataBuilder, TargetDescription, TargetPath, TargetsMetadata, TimestampMetadata,
     VirtualTargetPath,
 };
 use tuf::repository::{EphemeralRepository, Repository};
@@ -89,8 +89,7 @@
         .snapshot_key(snapshot_key.public().clone())
         .targets_key(targets_key.public().clone())
         .timestamp_key(timestamp_key.public().clone())
-        .signed::<Json>(&root_key)
-        .unwrap();
+        .signed::<Json>(&root_key)?;
 
     remote.store_metadata(
         &MetadataPath::new("root".into())?,
@@ -127,16 +126,11 @@
         &signed,
     )?;
 
-    let targets_bytes = Json::canonicalize(&Json::serialize(&signed)?)?;
-
     //// build the snapshot ////
-    let meta_map = hashmap! {
-        MetadataPath::new("targets".into())? =>
-            MetadataDescription::from_reader(&*targets_bytes, 1, &[HashAlgorithm::Sha256])?,
-    };
-    let snapshot = SnapshotMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), meta_map)?;
 
-    let signed = SignedMetadata::<Json, _>::new(snapshot, &snapshot_key)?;
+    let signed = SnapshotMetadataBuilder::new()
+        .insert_metadata(&signed, &[HashAlgorithm::Sha256])?
+        .signed::<Json>(&snapshot_key)?;
 
     remote.store_metadata(
         &MetadataPath::new("snapshot".into())?,