diff --git a/annotation/src/lib.rs b/annotation/src/lib.rs
index 71d9a0d5..79887a4a 100644
--- a/annotation/src/lib.rs
+++ b/annotation/src/lib.rs
@@ -3,7 +3,7 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::ToTokens;
-use syn::{parse, Attribute, Data, DeriveInput, Fields};
+use syn::{parse, punctuated::Punctuated, Attribute, Data, DeriveInput, Fields, Meta, Token};
/// Marks a type as a type shared across the FFI boundary using typeshare.
///
@@ -50,11 +50,27 @@ pub fn typeshare(_attr: TokenStream, item: TokenStream) -> TokenStream {
}
}
+const CONFIG_ATTRIBUTE_NAME: &str = "typeshare";
+
+fn is_typeshare_attribute(attribute: &Attribute) -> bool {
+ let has_cfg_attr = || {
+ if attribute.path().is_ident("cfg_attr") {
+ if let Ok(meta) =
+ attribute.parse_args_with(Punctuated::::parse_terminated)
+ {
+ return meta.into_iter().any(
+ |meta| matches!(meta, Meta::List(meta_list) if meta_list.path.is_ident(CONFIG_ATTRIBUTE_NAME)),
+ );
+ }
+ }
+ false
+ };
+ attribute.path().is_ident(CONFIG_ATTRIBUTE_NAME) || has_cfg_attr()
+}
+
fn strip_configuration_attribute(item: &mut DeriveInput) {
fn remove_configuration_from_attributes(attributes: &mut Vec) {
- const CONFIG_ATTRIBUTE_NAME: &str = "typeshare";
-
- attributes.retain(|x| x.path().to_token_stream().to_string() != CONFIG_ATTRIBUTE_NAME);
+ attributes.retain(|attribute| !is_typeshare_attribute(attribute));
}
fn remove_configuration_from_fields(fields: &mut Fields) {
diff --git a/core/data/tests/cfg_if_attribute_typeshare/input.rs b/core/data/tests/cfg_if_attribute_typeshare/input.rs
new file mode 100644
index 00000000..71208b1a
--- /dev/null
+++ b/core/data/tests/cfg_if_attribute_typeshare/input.rs
@@ -0,0 +1,40 @@
+/// Example of a type that is conditionally typeshared
+/// based on a feature "typeshare-support". This does not
+/// conditionally typeshare but allows a conditionally
+/// typeshared type to generate typeshare types when behind
+/// a `cfg_attr` condition.
+#[cfg_attr(feature = "typeshare-support", typeshare)]
+pub struct TestStruct1 {
+ field: String,
+}
+
+#[cfg_attr(feature = "typeshare-support", typeshare(transparent))]
+#[derive(Debug, Default, PartialEq, Eq, Clone, Hash)]
+#[repr(transparent)]
+pub struct Bytes(Vec);
+
+#[derive(Debug, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+#[cfg_attr(
+ feature = "typeshare-support",
+ typeshare(
+ swift = "Equatable, Hashable",
+ swiftGenericConstraints = "R: Equatable & Hashable"
+ )
+)]
+pub struct TestStruct2 {
+ field_1: String,
+ field_2: R,
+}
+
+#[cfg_attr(
+ feature = "typeshare-support",
+ typeshare(kotlin = "JvmInline", redacted)
+)]
+pub struct TestStruct3(String);
+
+#[cfg_attr(feature = "typeshare-support", typeshare)]
+pub struct TestStruct4 {
+ #[cfg_attr(feature = "typeshare-support", typeshare(serialized_as = "I54"))]
+ pub field: i64,
+}
diff --git a/core/data/tests/cfg_if_attribute_typeshare/output.kt b/core/data/tests/cfg_if_attribute_typeshare/output.kt
new file mode 100644
index 00000000..be62ab47
--- /dev/null
+++ b/core/data/tests/cfg_if_attribute_typeshare/output.kt
@@ -0,0 +1,38 @@
+package com.agilebits.onepassword
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.SerialName
+
+typealias Bytes = List
+
+@Serializable
+@JvmInline
+value class TestStruct3(
+ private val value: String
+) {
+ fun unwrap() = value
+
+ override fun toString(): String = "***"
+}
+
+/// Example of a type that is conditionally typeshared
+/// based on a feature "typeshare-support". This does not
+/// conditionally typeshare but allows a conditionally
+/// typeshared type to generate typeshare types when behind
+/// a `cfg_attr` condition.
+@Serializable
+data class TestStruct1 (
+ val field: String
+)
+
+@Serializable
+data class TestStruct2 (
+ val field1: String,
+ val field2: R
+)
+
+@Serializable
+data class TestStruct4 (
+ val field: Long
+)
+
diff --git a/core/data/tests/cfg_if_attribute_typeshare/output.swift b/core/data/tests/cfg_if_attribute_typeshare/output.swift
new file mode 100644
index 00000000..10bd5e98
--- /dev/null
+++ b/core/data/tests/cfg_if_attribute_typeshare/output.swift
@@ -0,0 +1,36 @@
+import Foundation
+
+public typealias Bytes = [UInt8]
+
+public typealias TestStruct3 = String
+
+/// Example of a type that is conditionally typeshared
+/// based on a feature "typeshare-support". This does not
+/// conditionally typeshare but allows a conditionally
+/// typeshared type to generate typeshare types when behind
+/// a `cfg_attr` condition.
+public struct TestStruct1: Codable {
+ public let field: String
+
+ public init(field: String) {
+ self.field = field
+ }
+}
+
+public struct TestStruct2: Codable, Equatable, Hashable {
+ public let field1: String
+ public let field2: R
+
+ public init(field1: String, field2: R) {
+ self.field1 = field1
+ self.field2 = field2
+ }
+}
+
+public struct TestStruct4: Codable {
+ public let field: Int64
+
+ public init(field: Int64) {
+ self.field = field
+ }
+}
diff --git a/core/data/tests/cfg_if_attribute_typeshare/output.ts b/core/data/tests/cfg_if_attribute_typeshare/output.ts
new file mode 100644
index 00000000..fb2c6aed
--- /dev/null
+++ b/core/data/tests/cfg_if_attribute_typeshare/output.ts
@@ -0,0 +1,24 @@
+export type Bytes = number[];
+
+export type TestStruct3 = string;
+
+/**
+ * Example of a type that is conditionally typeshared
+ * based on a feature "typeshare-support". This does not
+ * conditionally typeshare but allows a conditionally
+ * typeshared type to generate typeshare types when behind
+ * a `cfg_attr` condition.
+ */
+export interface TestStruct1 {
+ field: string;
+}
+
+export interface TestStruct2 {
+ field1: string;
+ field2: R;
+}
+
+export interface TestStruct4 {
+ field: number;
+}
+
diff --git a/core/src/parser.rs b/core/src/parser.rs
index 6cb9868b..a87f1414 100644
--- a/core/src/parser.rs
+++ b/core/src/parser.rs
@@ -11,7 +11,6 @@ use crate::{
target_os_check::accept_target_os,
visitors::{ImportedType, TypeShareVisitor},
};
-use itertools::Either;
use log::debug;
use proc_macro2::Ident;
use std::{
@@ -41,7 +40,7 @@ pub enum DecoratorKind {
impl DecoratorKind {
/// This decorator as a str.
- fn as_str(&self) -> &str {
+ fn as_str(&self) -> &'static str {
match self {
DecoratorKind::Swift => "swift",
DecoratorKind::SwiftGenericConstraints => "swiftGenericConstraints",
@@ -151,7 +150,7 @@ pub fn parse(
) -> Result