diff --git a/core/data/tests/can_generate_slice_of_user_type/input.rs b/core/data/tests/can_generate_slice_of_user_type/input.rs new file mode 100644 index 00000000..9950eb22 --- /dev/null +++ b/core/data/tests/can_generate_slice_of_user_type/input.rs @@ -0,0 +1,5 @@ +#[typeshare] +#[derive(Serialize)] +pub struct Video<'a> { + pub tags: &'a [Tag], +} diff --git a/core/data/tests/can_generate_slice_of_user_type/output.go b/core/data/tests/can_generate_slice_of_user_type/output.go new file mode 100644 index 00000000..bd4ceee8 --- /dev/null +++ b/core/data/tests/can_generate_slice_of_user_type/output.go @@ -0,0 +1,7 @@ +package proto + +import "encoding/json" + +type Video struct { + Tags []Tag `json:"tags"` +} diff --git a/core/data/tests/can_generate_slice_of_user_type/output.kt b/core/data/tests/can_generate_slice_of_user_type/output.kt new file mode 100644 index 00000000..8bc019a5 --- /dev/null +++ b/core/data/tests/can_generate_slice_of_user_type/output.kt @@ -0,0 +1,12 @@ +@file:NoLiveLiterals + +package com.agilebits.onepassword + +import androidx.compose.runtime.NoLiveLiterals +import kotlinx.serialization.* + +@Serializable +data class Video ( + val tags: List +) + diff --git a/core/data/tests/can_generate_slice_of_user_type/output.scala b/core/data/tests/can_generate_slice_of_user_type/output.scala new file mode 100644 index 00000000..11888b10 --- /dev/null +++ b/core/data/tests/can_generate_slice_of_user_type/output.scala @@ -0,0 +1,9 @@ +package com.agilebits + +package onepassword { + +case class Video ( + tags: Vector[Tag] +) + +} diff --git a/core/data/tests/can_generate_slice_of_user_type/output.swift b/core/data/tests/can_generate_slice_of_user_type/output.swift new file mode 100644 index 00000000..6c9a5fe3 --- /dev/null +++ b/core/data/tests/can_generate_slice_of_user_type/output.swift @@ -0,0 +1,9 @@ +import Foundation + +public struct Video: Codable { + public let tags: [Tag] + + public init(tags: [Tag]) { + self.tags = tags + } +} diff --git a/core/data/tests/can_generate_slice_of_user_type/output.ts b/core/data/tests/can_generate_slice_of_user_type/output.ts new file mode 100644 index 00000000..0b82b7f1 --- /dev/null +++ b/core/data/tests/can_generate_slice_of_user_type/output.ts @@ -0,0 +1,4 @@ +export interface Video { + tags: Tag[]; +} + diff --git a/core/src/language/go.rs b/core/src/language/go.rs index 2ecf0554..16393893 100644 --- a/core/src/language/go.rs +++ b/core/src/language/go.rs @@ -84,6 +84,9 @@ impl Language for Go { SpecialRustType::Array(rtype, len) => { format!("[{}]{}", len, self.format_type(rtype, generic_types)?) } + SpecialRustType::Slice(rtype) => { + format!("[]{}", self.format_type(rtype, generic_types)?) + } SpecialRustType::Option(rtype) => { format!("*{}", self.format_type(rtype, generic_types)?) } diff --git a/core/src/language/kotlin.rs b/core/src/language/kotlin.rs index ac38e6a3..d8633aad 100644 --- a/core/src/language/kotlin.rs +++ b/core/src/language/kotlin.rs @@ -42,6 +42,9 @@ impl Language for Kotlin { SpecialRustType::Array(rtype, _) => { format!("List<{}>", self.format_type(rtype, generic_types)?) } + SpecialRustType::Slice(rtype) => { + format!("List<{}>", self.format_type(rtype, generic_types)?) + } SpecialRustType::Option(rtype) => { format!("{}?", self.format_type(rtype, generic_types)?) } diff --git a/core/src/language/scala.rs b/core/src/language/scala.rs index 9ce0d52b..aaefe7fd 100644 --- a/core/src/language/scala.rs +++ b/core/src/language/scala.rs @@ -83,6 +83,9 @@ impl Language for Scala { SpecialRustType::Array(rtype, _) => { format!("Vector[{}]", self.format_type(rtype, generic_types)?) } + SpecialRustType::Slice(rtype) => { + format!("Vector[{}]", self.format_type(rtype, generic_types)?) + } SpecialRustType::Option(rtype) => { format!("Option[{}]", self.format_type(rtype, generic_types)?) } diff --git a/core/src/language/swift.rs b/core/src/language/swift.rs index a1d86bc1..c9cf26e0 100644 --- a/core/src/language/swift.rs +++ b/core/src/language/swift.rs @@ -177,6 +177,9 @@ impl Language for Swift { SpecialRustType::Array(rtype, _) => { format!("[{}]", self.format_type(rtype, generic_types)?) } + SpecialRustType::Slice(rtype) => { + format!("[{}]", self.format_type(rtype, generic_types)?) + } SpecialRustType::Option(rtype) => { format!("{}?", self.format_type(rtype, generic_types)?) } diff --git a/core/src/language/typescript.rs b/core/src/language/typescript.rs index cffea1f7..68cbe7d2 100644 --- a/core/src/language/typescript.rs +++ b/core/src/language/typescript.rs @@ -41,6 +41,9 @@ impl Language for TypeScript { .join_with(", ") )) } + SpecialRustType::Slice(rtype) => { + Ok(format!("{}[]", self.format_type(rtype, generic_types)?)) + } // We add optionality above the type formatting level SpecialRustType::Option(rtype) => self.format_type(rtype, generic_types), SpecialRustType::HashMap(rtype1, rtype2) => Ok(format!( diff --git a/core/src/rust_types.rs b/core/src/rust_types.rs index e14d13cb..cb8f127d 100644 --- a/core/src/rust_types.rs +++ b/core/src/rust_types.rs @@ -2,7 +2,7 @@ use quote::ToTokens; use std::collections::BTreeSet; use std::str::FromStr; use std::{collections::HashMap, convert::TryFrom}; -use syn::{Expr, ExprLit, Lit, TypeArray}; +use syn::{Expr, ExprLit, Lit, TypeArray, TypeSlice}; use thiserror::Error; use crate::language::SupportedLanguage; @@ -135,6 +135,8 @@ pub enum SpecialRustType { Vec(Box), /// Represents `[T; N]` from the standard library Array(Box, usize), + /// Represents `&[T]` from the standard library + Slice(Box), /// Represents `HashMap` from the standard library HashMap(Box, Box), /// Represents `Option` from the standard library @@ -280,6 +282,12 @@ impl TryFrom<&syn::Type> for RustType { .base10_parse() .map_err(RustTypeParseError::NumericLiteral)?, )), + syn::Type::Slice(TypeSlice { + bracket_token: _, + elem, + }) => Self::Special(SpecialRustType::Slice( + Self::try_from(elem.as_ref())?.into(), + )), _ => { return Err(RustTypeParseError::UnexpectedToken( ty.to_token_stream().to_string(), @@ -372,7 +380,9 @@ impl SpecialRustType { /// Check if this type is equivalent to or contains `ty` in one of its generic parameters. pub fn contains_type(&self, ty: &str) -> bool { match &self { - Self::Vec(rty) | Self::Array(rty, _) | Self::Option(rty) => rty.contains_type(ty), + Self::Vec(rty) | Self::Array(rty, _) | Self::Slice(rty) | Self::Option(rty) => { + rty.contains_type(ty) + } Self::HashMap(rty1, rty2) => rty1.contains_type(ty) || rty2.contains_type(ty), Self::Unit | Self::String @@ -402,6 +412,7 @@ impl SpecialRustType { Self::F32 => "f32", Self::Vec(_) => "Vec", Self::Array(_, _) => "[]", + Self::Slice(_) => "&[]", Self::Option(_) => "Option", Self::HashMap(_, _) => "HashMap", Self::String => "String", @@ -424,7 +435,7 @@ impl SpecialRustType { /// if there are none. pub fn parameters(&self) -> Box + '_> { match &self { - Self::Vec(rtype) | Self::Array(rtype, _) | Self::Option(rtype) => { + Self::Vec(rtype) | Self::Array(rtype, _) | Self::Slice(rtype) | Self::Option(rtype) => { Box::new(std::iter::once(rtype.as_ref())) } Self::HashMap(rtype1, rtype2) => { diff --git a/core/tests/snapshot_tests.rs b/core/tests/snapshot_tests.rs index b8e1e068..99f3887f 100644 --- a/core/tests/snapshot_tests.rs +++ b/core/tests/snapshot_tests.rs @@ -390,6 +390,7 @@ tests! { scala, typescript ]; + can_generate_slice_of_user_type: [swift, kotlin, scala, typescript, go]; can_generate_readonly_fields: [ typescript ];