[fidl_table_validation] Support required vector fields

TEST: fx run-test fidl_table_validation
Change-Id: I7f99ec77ff09189a93054316afd42c5cbcf61e06
diff --git a/src/lib/fidl_table_validation/fidl_table_validation_derive/src/lib.rs b/src/lib/fidl_table_validation/fidl_table_validation_derive/src/lib.rs
index fc13e90..3be35cf 100644
--- a/src/lib/fidl_table_validation/fidl_table_validation_derive/src/lib.rs
+++ b/src/lib/fidl_table_validation/fidl_table_validation_derive/src/lib.rs
@@ -112,24 +112,44 @@
         .transpose()
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 struct FidlField {
     ident: Ident,
+    #[allow(unused)]
+    in_vec: bool,
     kind: FidlFieldKind,
 }
 
 impl TryFrom<Field> for FidlField {
     type Error = Error;
     fn try_from(src: Field) -> Result<Self> {
-        let span = src.span().clone();
+        let ident = match src.ident {
+            Some(ident) => Ok(ident),
+            None => Err(Error::new(
+                src.span(),
+                "ValidFidlTable can only be derived for non-tuple structs.",
+            )),
+        }?;
+
         let attrs = src.attrs;
         let kind = FidlFieldKind::try_from(attrs.as_slice())?;
-        match src.ident {
-            Some(ident) => Ok(FidlField { ident, kind }),
-            None => {
-                Err(Error::new(span, "ValidFidlTable can only be derived for non-tuple structs."))
+
+        let in_vec = match src.ty {
+            syn::Type::Path(path) if path.qself.is_none() => {
+                let mut segments = path.path.segments.iter();
+                let mut get_segment = || segments.next().map(|s| format!("{}", s.ident));
+                let first_segment = get_segment();
+                let second_segment = get_segment();
+                match (kind.clone(), first_segment, second_segment) {
+                    (FidlFieldKind::Required, Some(segment), _) if segment == "Vec" => true,
+                    (FidlFieldKind::Optional, _, Some(segment)) if segment == "Vec" => true,
+                    _ => false,
+                }
             }
-        }
+            _ => false,
+        };
+
+        Ok(FidlField { ident, in_vec, kind })
     }
 }
 
@@ -144,11 +164,23 @@
         match &self.kind {
             FidlFieldKind::Required => {
                 let camel_case = self.camel_case();
-                quote!(
-                    #ident: std::convert::TryFrom::try_from(
-                        src.#ident.ok_or(#missing_field_error_type::#camel_case)?
-                    ).map_err(anyhow::Error::from)?,
-                )
+                match self.in_vec {
+                    true => quote!(
+                        #ident: {
+                            let src_vec = src.#ident.ok_or(#missing_field_error_type::#camel_case)?;
+                            src_vec
+                                .into_iter()
+                                .map(std::convert::TryFrom::try_from)
+                                .map(|r| r.map_err(anyhow::Error::from))
+                                .collect::<std::result::Result<_, anyhow::Error>>()?
+                        },
+                    ),
+                    false => quote!(
+                        #ident: std::convert::TryFrom::try_from(
+                            src.#ident.ok_or(#missing_field_error_type::#camel_case)?
+                        ).map_err(anyhow::Error::from)?,
+                    ),
+                }
             }
             FidlFieldKind::Optional => quote!(
                 #ident: if let Some(field) = src.#ident {
@@ -168,7 +200,7 @@
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 enum FidlFieldKind {
     Required,
     Optional,
@@ -258,11 +290,18 @@
     field_intos.extend(fields.iter().map(|field| {
         let ident = &field.ident;
         match &field.kind {
-            FidlFieldKind::Required | FidlFieldKind::HasDefault(_) => quote!(
-                #ident: Some(
-                    src.#ident.into()
+            FidlFieldKind::Required | FidlFieldKind::HasDefault(_) => match field.in_vec {
+                true => quote!(
+                    #ident: Some(
+                        src.#ident.into_iter().map(Into::into).collect()
+                    ),
                 ),
-            ),
+                false => quote!(
+                    #ident: Some(
+                        src.#ident.into()
+                    ),
+                ),
+            },
             FidlFieldKind::Optional => quote!(
                 #ident: if let Some(field) = src.#ident {
                     Some(field.into())
diff --git a/src/lib/fidl_table_validation/fidl_table_validation_tests/src/lib.rs b/src/lib/fidl_table_validation/fidl_table_validation_tests/src/lib.rs
index 22ef2d1..3c4e4e2 100644
--- a/src/lib/fidl_table_validation/fidl_table_validation_tests/src/lib.rs
+++ b/src/lib/fidl_table_validation/fidl_table_validation_tests/src/lib.rs
@@ -330,3 +330,42 @@
         Err(e) => panic!("Did not expect to fail to build ValidFidl: got {:?}", e),
     };
 }
+
+#[test]
+fn works_with_vec_wrapped_nested_fields() {
+    #[derive(Default, Debug, PartialEq)]
+    struct NestedFidl {
+        required: Option<usize>,
+    }
+    dummy_impl_decodable!(NestedFidl);
+
+    #[derive(ValidFidlTable, Debug, PartialEq)]
+    #[fidl_table_src(NestedFidl)]
+    struct ValidNestedFidl {
+        required: usize,
+    }
+
+    #[derive(Default, Debug, PartialEq)]
+    struct Fidl {
+        vec: Option<Vec<NestedFidl>>,
+    }
+    dummy_impl_decodable!(Fidl);
+
+    #[derive(Default, ValidFidlTable, Debug, PartialEq)]
+    #[fidl_table_src(Fidl)]
+    struct ValidFidl {
+        vec: Vec<ValidNestedFidl>,
+    }
+
+    match ValidFidl::try_from(Fidl {
+        vec: Some(vec![NestedFidl { required: Some(5) }, NestedFidl { required: Some(6) }]),
+    }) {
+        Ok(valid) => assert_eq!(
+            ValidFidl {
+                vec: vec![ValidNestedFidl { required: 5 }, ValidNestedFidl { required: 6 }]
+            },
+            valid
+        ),
+        Err(e) => panic!("Did not expect to fail to build ValidFidl: got {:?}", e),
+    };
+}