Add TargetsMetadataBuilder to simplify making TargetsMetadata
diff --git a/src/metadata.rs b/src/metadata.rs
index 6bbb1da..dbc5770 100644
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -1613,6 +1613,88 @@
     }
 }
 
+/// Helper to construct `TargetsMetadata`.
+pub struct TargetsMetadataBuilder {
+    version: u32,
+    expires: DateTime<Utc>,
+    targets: HashMap<VirtualTargetPath, TargetDescription>,
+    delegations: Option<Delegations>,
+}
+
+impl TargetsMetadataBuilder {
+    /// Create a new `TargetsMetadata`. It defaults to:
+    ///
+    /// * version: 1
+    /// * expires: 90 days from the current time.
+    pub fn new() -> Self {
+        TargetsMetadataBuilder {
+            version: 1,
+            expires: Utc::now() + Duration::days(90),
+            targets: HashMap::new(),
+            delegations: None,
+        }
+    }
+
+    /// 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 target to the target metadata.
+    pub fn insert_target_from_reader<R>(
+        self,
+        path: VirtualTargetPath,
+        read: R,
+        hash_algs: &[HashAlgorithm],
+    ) -> Result<Self>
+    where
+          R: Read,
+    {
+        let description = TargetDescription::from_reader(read, hash_algs)?;
+        Ok(self.insert_target_description(path, description))
+    }
+
+    /// Add `TargetDescription` to this target metadata target description.
+    pub fn insert_target_description(
+        mut self,
+        path: VirtualTargetPath,
+        description: TargetDescription,
+    ) -> Self {
+        self.targets.insert(path, description);
+        self
+    }
+
+    /// Add `Delegatiuons` to this target metadata.
+    pub fn delegations(mut self, delegations: Delegations) -> Self {
+        self.delegations = Some(delegations);
+        self
+    }
+
+    /// Construct a new `TargetsMetadata`.
+    pub fn build(self) -> Result<TargetsMetadata> {
+        TargetsMetadata::new(
+            self.version,
+            self.expires,
+            self.targets,
+            self.delegations,
+        )
+    }
+
+    /// Construct a new `SignedMetadata<D, TargetsMetadata>`.
+    pub fn signed<D>(self, private_key: &PrivateKey) -> Result<SignedMetadata<D, TargetsMetadata>>
+        where D: DataInterchange,
+    {
+        Ok(SignedMetadata::new(self.build()?, private_key)?)
+    }
+}
+
 /// Wrapper to described a collections of delegations.
 #[derive(Debug, PartialEq, Clone)]
 pub struct Delegations {
@@ -2066,18 +2148,17 @@
 
     #[test]
     fn serde_targets_metadata() {
-        let targets = TargetsMetadata::new(
-            1,
-            Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
-            hashmap! {
-                VirtualTargetPath::new("foo".into()).unwrap() =>
-                    TargetDescription::from_reader(
-                        b"foo" as &[u8],
-                        &[HashAlgorithm::Sha256],
-                    ).unwrap(),
-            },
-            None,
-        ).unwrap();
+        let targets = TargetsMetadataBuilder::new()
+            .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+            .insert_target_description(
+                VirtualTargetPath::new("foo".into()).unwrap(),
+                TargetDescription::from_reader(
+                    &b"foo"[..],
+                    &[HashAlgorithm::Sha256],
+                ).unwrap(),
+            )
+            .build()
+            .unwrap();
 
         let jsn = json!({
             "type": "targets",
@@ -2115,12 +2196,11 @@
             ],
         ).unwrap();
 
-        let targets = TargetsMetadata::new(
-            1,
-            Utc.ymd(2017, 1, 1).and_hms(0, 0, 0),
-            HashMap::new(),
-            Some(delegations),
-        ).unwrap();
+        let targets = TargetsMetadataBuilder::new()
+            .expires(Utc.ymd(2017, 1, 1).and_hms(0, 0, 0))
+            .delegations(delegations)
+            .build()
+            .unwrap();
 
         let jsn = json!({
             "type": "targets",
diff --git a/src/tuf.rs b/src/tuf.rs
index d285d6d..a5c60d6 100644
--- a/src/tuf.rs
+++ b/src/tuf.rs
@@ -613,14 +613,13 @@
 #[cfg(test)]
 mod test {
     use super::*;
-    use chrono::prelude::*;
     use crypto::{HashAlgorithm, PrivateKey, SignatureScheme};
     use interchange::Json;
     use metadata::{
-        MetadataDescription,
         RootMetadataBuilder,
         SnapshotMetadataBuilder,
         TimestampMetadataBuilder,
+        TargetsMetadataBuilder,
     };
 
     lazy_static! {
@@ -818,6 +817,7 @@
 
         let timestamp = TimestampMetadataBuilder::from_snapshot(&snapshot, &[HashAlgorithm::Sha256])
             .unwrap()
+            // sign it with the targets key
             .signed::<Json>(&KEYS[2])
             .unwrap();
 
@@ -851,6 +851,7 @@
         tuf.update_timestamp(timestamp).unwrap();
 
         let snapshot = SnapshotMetadataBuilder::new()
+            .version(1)
             .signed::<Json>(&KEYS[1])
             .unwrap();
 
@@ -867,11 +868,12 @@
             .signed::<Json>(&KEYS[0])
             .unwrap();
 
+        let targets = TargetsMetadataBuilder::new()
+            .signed::<Json>(&KEYS[2])
+            .unwrap();
+
         let snapshot = SnapshotMetadataBuilder::new()
-            .insert_metadata_description(
-                MetadataPath::from_role(&Role::Targets),
-                MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
-            )
+            .insert_metadata(&targets, &[HashAlgorithm::Sha256]).unwrap()
             .signed::<Json>(&KEYS[1])
             .unwrap();
 
@@ -885,12 +887,6 @@
         tuf.update_timestamp(timestamp).unwrap();
         tuf.update_snapshot(snapshot).unwrap();
 
-        let targets =
-            TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!(), None)
-                .unwrap();
-        let targets: SignedMetadata<Json, _> =
-            SignedMetadata::new(targets, &KEYS[2]).unwrap();
-
         assert_eq!(tuf.update_targets(targets.clone()), Ok(true));
 
         // second update should do nothing
@@ -909,11 +905,13 @@
 
         let mut tuf = Tuf::from_root(root).unwrap();
 
+        let targets = TargetsMetadataBuilder::new()
+            // sign it with the timestamp key
+            .signed::<Json>(&KEYS[3])
+            .unwrap();
+
         let snapshot = SnapshotMetadataBuilder::new()
-            .insert_metadata_description(
-                MetadataPath::from_role(&Role::Targets),
-                MetadataDescription::from_reader(&*vec![], 1, &[HashAlgorithm::Sha256]).unwrap(),
-            )
+            .insert_metadata(&targets, &[HashAlgorithm::Sha256]).unwrap()
             .signed::<Json>(&KEYS[1])
             .unwrap();
 
@@ -925,12 +923,6 @@
         tuf.update_timestamp(timestamp).unwrap();
         tuf.update_snapshot(snapshot).unwrap();
 
-        let targets =
-            TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!(), None)
-                .unwrap();
-        let targets: SignedMetadata<Json, TargetsMetadata> =
-            SignedMetadata::new(targets, &KEYS[3]).unwrap();
-
         assert!(tuf.update_targets(targets).is_err());
     }
 
@@ -946,11 +938,13 @@
 
         let mut tuf = Tuf::from_root(root).unwrap();
 
+        let targets = TargetsMetadataBuilder::new()
+            .version(2)
+            .signed::<Json>(&KEYS[2])
+            .unwrap();
+
         let snapshot = SnapshotMetadataBuilder::new()
-            .insert_metadata_description(
-                MetadataPath::from_role(&Role::Targets),
-                MetadataDescription::from_reader(&*vec![], 2, &[HashAlgorithm::Sha256]).unwrap(),
-            )
+            .insert_metadata(&targets, &[HashAlgorithm::Sha256]).unwrap()
             .signed::<Json>(&KEYS[1])
             .unwrap();
 
@@ -962,11 +956,10 @@
         tuf.update_timestamp(timestamp).unwrap();
         tuf.update_snapshot(snapshot).unwrap();
 
-        let targets =
-            TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), hashmap!(), None)
-                .unwrap();
-        let targets: SignedMetadata<Json, TargetsMetadata> =
-            SignedMetadata::new(targets, &KEYS[2]).unwrap();
+        let targets = TargetsMetadataBuilder::new()
+            .version(1)
+            .signed::<Json>(&KEYS[2])
+            .unwrap();
 
         assert!(tuf.update_targets(targets).is_err());
     }
diff --git a/tests/integration.rs b/tests/integration.rs
index 8cd4455..ac0dcd6 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -1,17 +1,13 @@
-extern crate chrono;
 #[macro_use]
 extern crate maplit;
 extern crate tuf;
 
-use chrono::offset::Utc;
-use chrono::prelude::*;
-use std::collections::HashMap;
 use tuf::crypto::{HashAlgorithm, PrivateKey, SignatureScheme};
 use tuf::interchange::Json;
 use tuf::metadata::{
     Delegation, Delegations, MetadataDescription, MetadataPath,
-    RootMetadataBuilder, SignedMetadata, SnapshotMetadataBuilder,
-    TargetDescription, TargetsMetadata, TimestampMetadataBuilder,
+    RootMetadataBuilder, SnapshotMetadataBuilder,
+    TargetsMetadataBuilder, TimestampMetadataBuilder,
     VirtualTargetPath,
 };
 use tuf::Tuf;
@@ -84,30 +80,25 @@
             ).unwrap(),
         ],
     ).unwrap();
-    let targets = TargetsMetadata::new(
-        1,
-        Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
-        HashMap::new(),
-        Some(delegations),
-    ).unwrap();
+    let targets = TargetsMetadataBuilder::new()
+        .delegations(delegations)
+        .signed::<Json>(&targets_key)
+        .unwrap();
 
-    let signed = SignedMetadata::<Json, _>::new(targets, &targets_key).unwrap();
-
-    tuf.update_targets(signed).unwrap();
+    tuf.update_targets(targets).unwrap();
 
     //// build the delegation ////
     let target_file: &[u8] = b"bar";
-    let target_map = hashmap! {
-        VirtualTargetPath::new("foo".into()).unwrap() =>
-            TargetDescription::from_reader(target_file, &[HashAlgorithm::Sha256]).unwrap(),
-    };
-    let delegation =
-        TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), target_map, None).unwrap();
+    let delegation = TargetsMetadataBuilder::new()
+        .insert_target_from_reader(
+            VirtualTargetPath::new("foo".into()).unwrap(),
+            target_file,
+            &[HashAlgorithm::Sha256],
+        ).unwrap()
+        .signed::<Json>(&delegation_key)
+        .unwrap();
 
-    let signed =
-        SignedMetadata::<Json, _>::new(delegation, &delegation_key).unwrap();
-
-    tuf.update_delegation(&MetadataPath::new("delegation".into()).unwrap(), signed)
+    tuf.update_delegation(&MetadataPath::new("delegation".into()).unwrap(), delegation)
         .unwrap();
 
     assert!(
@@ -183,16 +174,12 @@
             ).unwrap(),
         ],
     ).unwrap();
-    let targets = TargetsMetadata::new(
-        1,
-        Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
-        HashMap::new(),
-        Some(delegations),
-    ).unwrap();
+    let targets = TargetsMetadataBuilder::new()
+        .delegations(delegations)
+        .signed::<Json>(&targets_key)
+        .unwrap();
 
-    let signed = SignedMetadata::<Json, _>::new(targets, &targets_key).unwrap();
-
-    tuf.update_targets(signed).unwrap();
+    tuf.update_targets(targets).unwrap();
 
     //// build delegation A ////
 
@@ -214,34 +201,29 @@
             ).unwrap(),
         ],
     ).unwrap();
-    let delegation = TargetsMetadata::new(
-        1,
-        Utc.ymd(2038, 1, 1).and_hms(0, 0, 0),
-        HashMap::new(),
-        Some(delegations),
-    ).unwrap();
 
-    let signed =
-        SignedMetadata::<Json, _>::new(delegation, &delegation_a_key).unwrap();
+    let delegation = TargetsMetadataBuilder::new()
+        .delegations(delegations)
+        .signed::<Json>(&delegation_a_key)
+        .unwrap();
 
-    tuf.update_delegation(&MetadataPath::new("delegation-a".into()).unwrap(), signed)
+    tuf.update_delegation(&MetadataPath::new("delegation-a".into()).unwrap(), delegation)
         .unwrap();
 
     //// build delegation B ////
 
     let target_file: &[u8] = b"bar";
-    let target_map = hashmap! {
-        VirtualTargetPath::new("foo".into()).unwrap() =>
-            TargetDescription::from_reader(target_file, &[HashAlgorithm::Sha256]).unwrap(),
-    };
 
-    let delegation =
-        TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), target_map, None).unwrap();
+    let delegation = TargetsMetadataBuilder::new()
+        .insert_target_from_reader(
+            VirtualTargetPath::new("foo".into()).unwrap(),
+            target_file,
+            &[HashAlgorithm::Sha256],
+        ).unwrap()
+        .signed::<Json>(&delegation_b_key)
+        .unwrap();
 
-    let signed =
-        SignedMetadata::<Json, _>::new(delegation, &delegation_b_key).unwrap();
-
-    tuf.update_delegation(&MetadataPath::new("delegation-b".into()).unwrap(), signed)
+    tuf.update_delegation(&MetadataPath::new("delegation-b".into()).unwrap(), delegation)
         .unwrap();
 
     assert!(
diff --git a/tests/simple_example.rs b/tests/simple_example.rs
index e2b4028..2b1d639 100644
--- a/tests/simple_example.rs
+++ b/tests/simple_example.rs
@@ -1,16 +1,12 @@
 extern crate chrono;
-#[macro_use]
-extern crate maplit;
 extern crate tuf;
 
-use chrono::offset::Utc;
-use chrono::prelude::*;
 use tuf::client::{Client, Config, PathTranslator};
 use tuf::crypto::{HashAlgorithm, KeyId, PrivateKey, SignatureScheme};
 use tuf::interchange::Json;
 use tuf::metadata::{
-    MetadataPath, MetadataVersion, RootMetadataBuilder, SignedMetadata,
-    SnapshotMetadataBuilder, TargetDescription, TargetPath, TargetsMetadata,
+    MetadataPath, MetadataVersion, RootMetadataBuilder,
+    SnapshotMetadataBuilder, TargetPath, TargetsMetadataBuilder,
     TimestampMetadataBuilder, VirtualTargetPath,
 };
 use tuf::repository::{EphemeralRepository, Repository};
@@ -105,31 +101,33 @@
     //// build the targets ////
 
     let target_file: &[u8] = b"things fade, alternatives exclude";
+
     let target_path = TargetPath::new("foo-bar".into())?;
-    let target_description = TargetDescription::from_reader(target_file, &[HashAlgorithm::Sha256])?;
     let _ = remote.store_target(target_file, &target_path);
 
-    let target_map =
-        hashmap!(config.path_translator().real_to_virtual(&target_path)? => target_description);
-    let targets = TargetsMetadata::new(1, Utc.ymd(2038, 1, 1).and_hms(0, 0, 0), target_map, None)?;
-
-    let signed = SignedMetadata::<Json, _>::new(targets, &targets_key)?;
+    let targets = TargetsMetadataBuilder::new()
+        .insert_target_from_reader(
+            config.path_translator().real_to_virtual(&target_path)?,
+            target_file,
+            &[HashAlgorithm::Sha256],
+        )?
+        .signed::<Json>(&targets_key)?;
 
     remote.store_metadata(
         &MetadataPath::new("targets".into())?,
         &MetadataVersion::Number(1),
-        &signed,
+        &targets,
     )?;
     remote.store_metadata(
         &MetadataPath::new("targets".into())?,
         &MetadataVersion::None,
-        &signed,
+        &targets,
     )?;
 
     //// build the snapshot ////
 
     let snapshot = SnapshotMetadataBuilder::new()
-        .insert_metadata(&signed, &[HashAlgorithm::Sha256])?
+        .insert_metadata(&targets, &[HashAlgorithm::Sha256])?
         .signed::<Json>(&snapshot_key)?;
 
     remote.store_metadata(