@@ -129,7 +129,7 @@ fn parse_type_impl(
129129 ) ;
130130 }
131131 }
132- "Vec" | "Option" => {
132+ "Vec" | "HashSet" | "BTreeSet" | " Option" => {
133133 if let Some ( syn:: GenericArgument :: Type ( inner_ty) ) = args. args . first ( ) {
134134 let inner_schema = parse_type_to_schema_ref (
135135 inner_ty,
@@ -139,6 +139,11 @@ fn parse_type_impl(
139139 if ident_str == "Vec" {
140140 return SchemaRef :: Inline ( Box :: new ( Schema :: array ( inner_schema) ) ) ;
141141 }
142+ if ident_str == "HashSet" || ident_str == "BTreeSet" {
143+ let mut schema = Schema :: array ( inner_schema) ;
144+ schema. unique_items = Some ( true ) ;
145+ return SchemaRef :: Inline ( Box :: new ( schema) ) ;
146+ }
142147 // Option<T> -> nullable schema
143148 match inner_schema {
144149 SchemaRef :: Inline ( mut schema) => {
@@ -322,7 +327,7 @@ fn parse_type_impl(
322327 } ) ) ,
323328 // Standard library types that should not be referenced
324329 // Note: HashMap and BTreeMap are handled above in generic types
325- "Vec" | "Option" | "Result" | "Json" | "Path" | "Query" | "Header" => {
330+ "Vec" | "HashSet" | "BTreeSet" | " Option" | "Result" | "Json" | "Path" | "Query" | "Header" => {
326331 // These are not schema types, return object schema
327332 SchemaRef :: Inline ( Box :: new ( Schema :: new ( SchemaType :: Object ) ) )
328333 }
@@ -1417,4 +1422,85 @@ mod tests {
14171422 panic ! ( "Expected inline string schema for &mut String" ) ;
14181423 }
14191424 }
1425+
1426+ // ========== Coverage: HashSet/BTreeSet → uniqueItems ==========
1427+
1428+ #[ test]
1429+ fn test_hashset_string_produces_unique_items_array ( ) {
1430+ let ty: Type = syn:: parse_str ( "HashSet<String>" ) . unwrap ( ) ;
1431+ let schema_ref = parse_type_to_schema_ref ( & ty, & HashSet :: new ( ) , & HashMap :: new ( ) ) ;
1432+ if let SchemaRef :: Inline ( schema) = & schema_ref {
1433+ assert_eq ! ( schema. schema_type, Some ( SchemaType :: Array ) ) ;
1434+ assert_eq ! ( schema. unique_items, Some ( true ) ) ;
1435+ if let Some ( SchemaRef :: Inline ( items) ) = schema. items . as_deref ( ) {
1436+ assert_eq ! ( items. schema_type, Some ( SchemaType :: String ) ) ;
1437+ } else {
1438+ panic ! ( "Expected inline string items for HashSet<String>" ) ;
1439+ }
1440+ } else {
1441+ panic ! ( "Expected inline schema for HashSet<String>" ) ;
1442+ }
1443+ }
1444+
1445+ #[ test]
1446+ fn test_btreeset_i32_produces_unique_items_array ( ) {
1447+ let ty: Type = syn:: parse_str ( "BTreeSet<i32>" ) . unwrap ( ) ;
1448+ let schema_ref = parse_type_to_schema_ref ( & ty, & HashSet :: new ( ) , & HashMap :: new ( ) ) ;
1449+ if let SchemaRef :: Inline ( schema) = & schema_ref {
1450+ assert_eq ! ( schema. schema_type, Some ( SchemaType :: Array ) ) ;
1451+ assert_eq ! ( schema. unique_items, Some ( true ) ) ;
1452+ if let Some ( SchemaRef :: Inline ( items) ) = schema. items . as_deref ( ) {
1453+ assert_eq ! ( items. schema_type, Some ( SchemaType :: Integer ) ) ;
1454+ } else {
1455+ panic ! ( "Expected inline integer items for BTreeSet<i32>" ) ;
1456+ }
1457+ } else {
1458+ panic ! ( "Expected inline schema for BTreeSet<i32>" ) ;
1459+ }
1460+ }
1461+
1462+ #[ test]
1463+ fn test_option_hashset_is_nullable_unique_array ( ) {
1464+ let ty: Type = syn:: parse_str ( "Option<HashSet<i64>>" ) . unwrap ( ) ;
1465+ let schema_ref = parse_type_to_schema_ref ( & ty, & HashSet :: new ( ) , & HashMap :: new ( ) ) ;
1466+ if let SchemaRef :: Inline ( schema) = & schema_ref {
1467+ assert_eq ! ( schema. schema_type, Some ( SchemaType :: Array ) ) ;
1468+ assert_eq ! ( schema. unique_items, Some ( true ) ) ;
1469+ assert_eq ! ( schema. nullable, Some ( true ) ) ;
1470+ if let Some ( SchemaRef :: Inline ( items) ) = schema. items . as_deref ( ) {
1471+ assert_eq ! ( items. schema_type, Some ( SchemaType :: Integer ) ) ;
1472+ } else {
1473+ panic ! ( "Expected inline integer items for Option<HashSet<i64>>" ) ;
1474+ }
1475+ } else {
1476+ panic ! ( "Expected inline schema for Option<HashSet<i64>>" ) ;
1477+ }
1478+ }
1479+
1480+ #[ test]
1481+ fn test_vec_does_not_have_unique_items ( ) {
1482+ let ty: Type = syn:: parse_str ( "Vec<String>" ) . unwrap ( ) ;
1483+ let schema_ref = parse_type_to_schema_ref ( & ty, & HashSet :: new ( ) , & HashMap :: new ( ) ) ;
1484+ if let SchemaRef :: Inline ( schema) = & schema_ref {
1485+ assert_eq ! ( schema. schema_type, Some ( SchemaType :: Array ) ) ;
1486+ assert ! ( schema. unique_items. is_none( ) ) ;
1487+ } else {
1488+ panic ! ( "Expected inline schema for Vec<String>" ) ;
1489+ }
1490+ }
1491+
1492+ #[ test]
1493+ fn test_bare_hashset_without_generics ( ) {
1494+ // HashSet without angle brackets → falls through to bare-name match
1495+ let ty: Type = syn:: parse_str ( "HashSet" ) . unwrap ( ) ;
1496+ let schema_ref = parse_type_to_schema_ref ( & ty, & HashSet :: new ( ) , & HashMap :: new ( ) ) ;
1497+ assert ! ( matches!( schema_ref, SchemaRef :: Inline ( _) ) ) ;
1498+ }
1499+
1500+ #[ test]
1501+ fn test_bare_btreeset_without_generics ( ) {
1502+ let ty: Type = syn:: parse_str ( "BTreeSet" ) . unwrap ( ) ;
1503+ let schema_ref = parse_type_to_schema_ref ( & ty, & HashSet :: new ( ) , & HashMap :: new ( ) ) ;
1504+ assert ! ( matches!( schema_ref, SchemaRef :: Inline ( _) ) ) ;
1505+ }
14201506}
0 commit comments