From 5117509437da49b07d6908522df80f940b60b0d1 Mon Sep 17 00:00:00 2001
From: "Charles E. Lehner" <charles.lehner@spruceid.com>
Date: Mon, 21 Sep 2020 13:44:24 -0400
Subject: [PATCH] Move OneOrMany into own file

---
 src/lib.rs         |  1 +
 src/one_or_many.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++
 src/vc.rs          | 63 +-----------------------------------
 3 files changed, 81 insertions(+), 62 deletions(-)
 create mode 100644 src/one_or_many.rs

diff --git a/src/lib.rs b/src/lib.rs
index f7fd982f6..c2995fe88 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,6 +2,7 @@ pub mod der;
 pub mod did;
 pub mod error;
 pub mod jwk;
+pub(crate) mod one_or_many;
 pub mod vc;
 
 extern crate pest;
diff --git a/src/one_or_many.rs b/src/one_or_many.rs
new file mode 100644
index 000000000..347bb00c1
--- /dev/null
+++ b/src/one_or_many.rs
@@ -0,0 +1,79 @@
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+#[serde(untagged)]
+pub enum OneOrMany<T> {
+    One(T),
+    Many(Vec<T>),
+}
+
+impl<T> OneOrMany<T> {
+    pub fn len(&self) -> usize {
+        match self {
+            Self::One(_) => 1,
+            Self::Many(values) => values.len(),
+        }
+    }
+
+    pub fn contains(&self, x: &T) -> bool
+    where
+        T: PartialEq<T>,
+    {
+        match self {
+            Self::One(value) => x == value,
+            Self::Many(values) => values.contains(x),
+        }
+    }
+
+    pub fn first(&self) -> Option<&T> {
+        match self {
+            Self::One(value) => Some(&value),
+            Self::Many(values) => {
+                if values.len() > 0 {
+                    Some(&values[0])
+                } else {
+                    None
+                }
+            }
+        }
+    }
+
+    pub fn to_single(&self) -> Option<&T> {
+        match self {
+            Self::One(value) => Some(&value),
+            Self::Many(values) => {
+                if values.len() == 1 {
+                    Some(&values[0])
+                } else {
+                    None
+                }
+            }
+        }
+    }
+}
+
+// consuming iterator
+impl<T> IntoIterator for OneOrMany<T> {
+    type Item = T;
+    type IntoIter = std::vec::IntoIter<Self::Item>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        match self {
+            Self::One(value) => vec![value].into_iter(),
+            Self::Many(values) => values.into_iter(),
+        }
+    }
+}
+
+// non-consuming iterator
+impl<'a, T> IntoIterator for &'a OneOrMany<T> {
+    type Item = &'a T;
+    type IntoIter = std::vec::IntoIter<Self::Item>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        match self {
+            OneOrMany::One(value) => vec![value].into_iter(),
+            OneOrMany::Many(values) => values.into_iter().collect::<Vec<Self::Item>>().into_iter(),
+        }
+    }
+}
diff --git a/src/vc.rs b/src/vc.rs
index 7a6f81516..d574fa86b 100644
--- a/src/vc.rs
+++ b/src/vc.rs
@@ -3,6 +3,7 @@ use std::convert::TryFrom;
 
 use crate::error::Error;
 use crate::jwk::{JWTKeys, Params};
+use crate::one_or_many::OneOrMany;
 
 use chrono::prelude::*;
 use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation};
@@ -63,13 +64,6 @@ pub struct Credential {
     pub refresh_service: Option<OneOrMany<RefreshService>>,
 }
 
-#[derive(Debug, Serialize, Deserialize, Clone)]
-#[serde(untagged)]
-pub enum OneOrMany<T> {
-    One(T),
-    Many(Vec<T>),
-}
-
 #[derive(Debug, Serialize, Deserialize, Clone)]
 #[serde(untagged)]
 #[serde(try_from = "OneOrMany<Context>")]
@@ -231,61 +225,6 @@ pub struct JWTClaims {
     pub verifiable_presentation: Option<Presentation>,
 }
 
-impl<T> OneOrMany<T> {
-    pub fn any<F>(&self, f: F) -> bool
-    where
-        F: Fn(&T) -> bool,
-    {
-        match self {
-            Self::One(value) => f(value),
-            Self::Many(values) => values.iter().any(f),
-        }
-    }
-
-    pub fn len(&self) -> usize {
-        match self {
-            Self::One(_) => 1,
-            Self::Many(values) => values.len(),
-        }
-    }
-
-    pub fn contains(&self, x: &T) -> bool
-    where
-        T: PartialEq<T>,
-    {
-        match self {
-            Self::One(value) => x == value,
-            Self::Many(values) => values.contains(x),
-        }
-    }
-
-    pub fn first(&self) -> Option<&T> {
-        match self {
-            Self::One(value) => Some(&value),
-            Self::Many(values) => {
-                if values.len() > 0 {
-                    Some(&values[0])
-                } else {
-                    None
-                }
-            }
-        }
-    }
-
-    pub fn to_single(&self) -> Option<&T> {
-        match self {
-            Self::One(value) => Some(&value),
-            Self::Many(values) => {
-                if values.len() == 1 {
-                    Some(&values[0])
-                } else {
-                    None
-                }
-            }
-        }
-    }
-}
-
 impl TryFrom<OneOrMany<Context>> for Contexts {
     type Error = Error;
     fn try_from(context: OneOrMany<Context>) -> Result<Self, Self::Error> {