[dhcp] Use combinators to load stash configs

Change-Id: Ibb398579fb8ab34420e06f1075d34b2ae49fe9a8
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/513852
Fuchsia-Auto-Submit: Tamir Duberstein <tamird@google.com>
Reviewed-by: Amanda Tait <atait@google.com>
Commit-Queue: Tamir Duberstein <tamird@google.com>
diff --git a/src/connectivity/network/dhcp/BUILD.gn b/src/connectivity/network/dhcp/BUILD.gn
index ea919b5..c393ab1 100644
--- a/src/connectivity/network/dhcp/BUILD.gn
+++ b/src/connectivity/network/dhcp/BUILD.gn
@@ -24,6 +24,7 @@
     "//src/lib/zircon/rust:fuchsia-zircon",
     "//third_party/rust_crates:anyhow",
     "//third_party/rust_crates:bytes",
+    "//third_party/rust_crates:futures",
     "//third_party/rust_crates:hex",
     "//third_party/rust_crates:log",
     "//third_party/rust_crates:num-derive",
@@ -36,7 +37,6 @@
 
   test_deps = [
     "//src/connectivity/lib/net-declare",
-    "//third_party/rust_crates:futures",
     "//third_party/rust_crates:matches",
   ]
 
diff --git a/src/connectivity/network/dhcp/src/stash.rs b/src/connectivity/network/dhcp/src/stash.rs
index 59fa5d4..92c8910 100644
--- a/src/connectivity/network/dhcp/src/stash.rs
+++ b/src/connectivity/network/dhcp/src/stash.rs
@@ -136,53 +136,56 @@
     /// hashmap. Any key-value pair which could not be parsed or deserialized will be removed and
     /// skipped.
     pub async fn load_client_configs(&self) -> Result<CachedClients, StashError> {
+        use futures::TryStreamExt as _;
+
         let (iter, server) =
             fidl::endpoints::create_proxy::<fidl_fuchsia_stash::GetIteratorMarker>()?;
         let () = self.proxy.get_prefix(&self.prefix, server)?;
-        let mut cache = std::collections::HashMap::new();
-        loop {
+        futures::stream::try_unfold(iter, |iter| async move {
             let kvs = iter.get_next().await?;
-            if kvs.is_empty() {
-                break;
-            }
-            for kv in kvs {
-                let key = match kv.key.split("-").last() {
-                    Some(v) => v,
-                    None => {
-                        // Invalid key-value pair: remove the invalid pair and try the next one.
-                        log::warn!("failed to parse key string: {}", kv.key);
-                        let () = self.rm_key(&kv.key)?;
-                        continue;
-                    }
-                };
-                let key = match ClientIdentifier::from_str(key) {
-                    Ok(v) => v,
-                    Err(e) => {
-                        log::warn!("client id from string conversion failed: {}", e);
-                        let () = self.rm_key(&kv.key)?;
-                        continue;
-                    }
-                };
-                let val = match kv.val {
-                    fidl_fuchsia_stash::Value::Stringval(v) => v,
-                    v => {
-                        log::warn!("invalid value variant stored in stash: {:?}", v);
-                        let () = self.rm_key(&kv.key)?;
-                        continue;
-                    }
-                };
-                let val: CachedConfig = match serde_json::from_str(&val) {
-                    Ok(v) => v,
-                    Err(e) => {
-                        log::warn!("failed to parse JSON from string: {}", e);
-                        let () = self.rm_key(&kv.key)?;
-                        continue;
-                    }
-                };
-                let _: Option<_> = cache.insert(key, val);
-            }
-        }
-        Ok(cache)
+            let yielded = (!kvs.is_empty()).then(|| (kvs, iter));
+            Result::<_, StashError>::Ok(yielded)
+        })
+        .map_ok(|kvs| futures::stream::iter(kvs.into_iter().map(Ok)))
+        .try_flatten()
+        .try_filter_map(|kv| async move {
+            let key = match kv.key.split("-").last() {
+                Some(v) => v,
+                None => {
+                    // Invalid key-value pair: remove the invalid pair and try the next one.
+                    log::warn!("failed to parse key string: {}", kv.key);
+                    let () = self.rm_key(&kv.key)?;
+                    return Ok(None);
+                }
+            };
+            let key = match ClientIdentifier::from_str(key) {
+                Ok(v) => v,
+                Err(e) => {
+                    log::warn!("client id from string conversion failed: {}", e);
+                    let () = self.rm_key(&kv.key)?;
+                    return Ok(None);
+                }
+            };
+            let val = match kv.val {
+                fidl_fuchsia_stash::Value::Stringval(v) => v,
+                v => {
+                    log::warn!("invalid value variant stored in stash: {:?}", v);
+                    let () = self.rm_key(&kv.key)?;
+                    return Ok(None);
+                }
+            };
+            let val: CachedConfig = match serde_json::from_str(&val) {
+                Ok(v) => v,
+                Err(e) => {
+                    log::warn!("failed to parse JSON from string: {}", e);
+                    let () = self.rm_key(&kv.key)?;
+                    return Ok(None);
+                }
+            };
+            Ok(Some((key, val)))
+        })
+        .try_collect()
+        .await
     }
 
     /// Loads a map of `OptionCode`s to `DhcpOption`s from data stored in `fuchsia.stash`.