Repository: juhaku/utoipa (Rust)

Issue:
ToSchema doesn't support slices
This fails with a [compiler error](https://github.com/juhaku/utoipa/blob/daa9f43f9f05cb8e2cc08f688cc8b26148004f12/utoipa-gen/src/component.rs#L85):

```
#[derive(Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct Foo<'a> {
    pub arr: &'a [i64],
}
```

Unfortunately, value_type doesn't help

```
#[derive(Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct Foo<'a> {
    #[schema(value_type = Vec<i64>)]
    pub arr: &'a [i64],
}
```

Apparently because the default TypeTree is always evaluated, and only then replaced by the TypeTree from value_type.

I added `Type::Slice(array) => Self::get_type_paths(&array.elem),` [to the match](https://github.com/juhaku/utoipa/blob/daa9f43f9f05cb8e2cc08f688cc8b26148004f12/utoipa-gen/src/component.rs#L71) and it seems to work fine.


Discussion:
As for the value_type not helping, this fixes it: https://github.com/nkovacs/utoipa/commit/c4280adebfcb92eb0ad079c9d3aff8c627f51356
Although I'm not sure why the generic stuff only runs on the default type tree? Maybe that's also a bug and it should work on the value_type as well.
That might be a bug, I need to check this out too, after I finnish one thing I am in middle of. It might well be that the slice's are not taken in account as the should. 

> Although I'm not sure why the generic stuff only runs on the default type tree? Maybe that's also a bug and it should work on the value_type as well.

True, good point. :thinking:  Most probably the generic and aliaces check should also happen from the override type as well. 

The `value_type` with `Vec<i64>` works because it will choose the `Group` or `Array` type from the match statements if I am not mistaken. I belive it is legit to add the support for `Type::Slice(...)` as well to the match statement.

This is indeed bug, I'll try to fix this asap. Normally there should be no need to add `#[schema(value_type = ...)]` for slice or array fields a they should be recoginized by default as array types in OpenAPI.
Thanks!



Patch:
diff --git a/utoipa-gen/src/component.rs b/utoipa-gen/src/component.rs
index 1e4e5d39..48ee2e5d 100644
--- a/utoipa-gen/src/component.rs
+++ b/utoipa-gen/src/component.rs
@@ -43,6 +43,9 @@ fn get_deprecated(attributes: &[Attribute]) -> Option<Deprecated> {
 enum TypeTreeValue<'t> {
     TypePath(&'t TypePath),
     Path(&'t Path),
+    /// Slice and array types need to be manually defined, since they cannot be recognized from
+    /// generic arguments.
+    Array(Vec<TypeTreeValue<'t>>),
 }
 
 /// [`TypeTree`] of items which represents a single parsed `type` of a
@@ -68,7 +71,8 @@ impl<'t> TypeTree<'t> {
             Type::Reference(reference) => Self::get_type_paths(reference.elem.as_ref()),
             Type::Tuple(tuple) => tuple.elems.iter().flat_map(Self::get_type_paths).collect(),
             Type::Group(group) => Self::get_type_paths(group.elem.as_ref()),
-            Type::Array(array) => Self::get_type_paths(&array.elem),
+            Type::Slice(slice) => vec![TypeTreeValue::Array(Self::get_type_paths(&slice.elem))],
+            Type::Array(array) => vec![TypeTreeValue::Array(Self::get_type_paths(&array.elem))],
             Type::TraitObject(trait_object) => {
                 trait_object
                     .bounds
@@ -82,7 +86,7 @@ impl<'t> TypeTree<'t> {
                     .map(|path| vec![TypeTreeValue::Path(path)]).unwrap_or_else(Vec::new)
             }
             _ => abort_call_site!(
-                "unexpected type in component part get type path, expected one of: Path, Reference, Group"
+                "unexpected type in component part get type path, expected one of: Path, Tuple, Reference, Group, Array, Slice, TraitObject"
             ),
         }
     }
@@ -108,7 +112,16 @@ impl<'t> TypeTree<'t> {
             let path = match value {
                 TypeTreeValue::TypePath(type_path) => &type_path.path,
                 TypeTreeValue::Path(path) => path,
+                TypeTreeValue::Array(value) => {
+                    return TypeTree {
+                        path: None,
+                        value_type: ValueType::Object,
+                        generic_type: Some(GenericType::Vec),
+                        children: Some(vec![Self::from_type_paths(value)]),
+                    };
+                }
             };
+
             // there will always be one segment at least
             let last_segment = path
                 .segments


Test patch:
diff --git a/utoipa-gen/tests/schema_derive_test.rs b/utoipa-gen/tests/schema_derive_test.rs
index ad0660d2..46eea274 100644
--- a/utoipa-gen/tests/schema_derive_test.rs
+++ b/utoipa-gen/tests/schema_derive_test.rs
@@ -2879,3 +2879,38 @@ fn derive_struct_with_validation_fields() {
         })
     );
 }
+
+#[test]
+fn derive_schema_with_slice_and_array() {
+    let value = api_doc! {
+        struct Item<'a> {
+            array: [&'a str; 10],
+            slice: &'a [&'a str],
+        }
+    };
+
+    assert_json_eq!(
+        value,
+        json!({
+            "properties": {
+                "array": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
+                "slice": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                }
+            },
+            "required": [
+                "array",
+                "slice"
+            ],
+            "type": "object"
+        })
+    )
+}