Skip to content

Commit 64b4bab

Browse files
Merge pull request #274 from 1Password/darrell/support-cfg-attr-typeshare
support cfg_attr typeshare definitions
2 parents fe267ac + c762c72 commit 64b4bab

7 files changed

Lines changed: 375 additions & 47 deletions

File tree

annotation/src/lib.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
extern crate proc_macro;
44
use proc_macro::TokenStream;
55
use quote::ToTokens;
6-
use syn::{parse, Attribute, Data, DeriveInput, Fields};
6+
use syn::{parse, punctuated::Punctuated, Attribute, Data, DeriveInput, Fields, Meta, Token};
77

88
/// Marks a type as a type shared across the FFI boundary using typeshare.
99
///
@@ -50,11 +50,27 @@ pub fn typeshare(_attr: TokenStream, item: TokenStream) -> TokenStream {
5050
}
5151
}
5252

53+
const CONFIG_ATTRIBUTE_NAME: &str = "typeshare";
54+
55+
fn is_typeshare_attribute(attribute: &Attribute) -> bool {
56+
let has_cfg_attr = || {
57+
if attribute.path().is_ident("cfg_attr") {
58+
if let Ok(meta) =
59+
attribute.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
60+
{
61+
return meta.into_iter().any(
62+
|meta| matches!(meta, Meta::List(meta_list) if meta_list.path.is_ident(CONFIG_ATTRIBUTE_NAME)),
63+
);
64+
}
65+
}
66+
false
67+
};
68+
attribute.path().is_ident(CONFIG_ATTRIBUTE_NAME) || has_cfg_attr()
69+
}
70+
5371
fn strip_configuration_attribute(item: &mut DeriveInput) {
5472
fn remove_configuration_from_attributes(attributes: &mut Vec<Attribute>) {
55-
const CONFIG_ATTRIBUTE_NAME: &str = "typeshare";
56-
57-
attributes.retain(|x| x.path().to_token_stream().to_string() != CONFIG_ATTRIBUTE_NAME);
73+
attributes.retain(|attribute| !is_typeshare_attribute(attribute));
5874
}
5975

6076
fn remove_configuration_from_fields(fields: &mut Fields) {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/// Example of a type that is conditionally typeshared
2+
/// based on a feature "typeshare-support". This does not
3+
/// conditionally typeshare but allows a conditionally
4+
/// typeshared type to generate typeshare types when behind
5+
/// a `cfg_attr` condition.
6+
#[cfg_attr(feature = "typeshare-support", typeshare)]
7+
pub struct TestStruct1 {
8+
field: String,
9+
}
10+
11+
#[cfg_attr(feature = "typeshare-support", typeshare(transparent))]
12+
#[derive(Debug, Default, PartialEq, Eq, Clone, Hash)]
13+
#[repr(transparent)]
14+
pub struct Bytes(Vec<u8>);
15+
16+
#[derive(Debug, Deserialize, Serialize)]
17+
#[serde(rename_all = "camelCase")]
18+
#[cfg_attr(
19+
feature = "typeshare-support",
20+
typeshare(
21+
swift = "Equatable, Hashable",
22+
swiftGenericConstraints = "R: Equatable & Hashable"
23+
)
24+
)]
25+
pub struct TestStruct2<R> {
26+
field_1: String,
27+
field_2: R,
28+
}
29+
30+
#[cfg_attr(
31+
feature = "typeshare-support",
32+
typeshare(kotlin = "JvmInline", redacted)
33+
)]
34+
pub struct TestStruct3(String);
35+
36+
#[cfg_attr(feature = "typeshare-support", typeshare)]
37+
pub struct TestStruct4 {
38+
#[cfg_attr(feature = "typeshare-support", typeshare(serialized_as = "I54"))]
39+
pub field: i64,
40+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.agilebits.onepassword
2+
3+
import kotlinx.serialization.Serializable
4+
import kotlinx.serialization.SerialName
5+
6+
typealias Bytes = List<UByte>
7+
8+
@Serializable
9+
@JvmInline
10+
value class TestStruct3(
11+
private val value: String
12+
) {
13+
fun unwrap() = value
14+
15+
override fun toString(): String = "***"
16+
}
17+
18+
/// Example of a type that is conditionally typeshared
19+
/// based on a feature "typeshare-support". This does not
20+
/// conditionally typeshare but allows a conditionally
21+
/// typeshared type to generate typeshare types when behind
22+
/// a `cfg_attr` condition.
23+
@Serializable
24+
data class TestStruct1 (
25+
val field: String
26+
)
27+
28+
@Serializable
29+
data class TestStruct2<R> (
30+
val field1: String,
31+
val field2: R
32+
)
33+
34+
@Serializable
35+
data class TestStruct4 (
36+
val field: Long
37+
)
38+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import Foundation
2+
3+
public typealias Bytes = [UInt8]
4+
5+
public typealias TestStruct3 = String
6+
7+
/// Example of a type that is conditionally typeshared
8+
/// based on a feature "typeshare-support". This does not
9+
/// conditionally typeshare but allows a conditionally
10+
/// typeshared type to generate typeshare types when behind
11+
/// a `cfg_attr` condition.
12+
public struct TestStruct1: Codable {
13+
public let field: String
14+
15+
public init(field: String) {
16+
self.field = field
17+
}
18+
}
19+
20+
public struct TestStruct2<R: Codable & Equatable & Hashable>: Codable, Equatable, Hashable {
21+
public let field1: String
22+
public let field2: R
23+
24+
public init(field1: String, field2: R) {
25+
self.field1 = field1
26+
self.field2 = field2
27+
}
28+
}
29+
30+
public struct TestStruct4: Codable {
31+
public let field: Int64
32+
33+
public init(field: Int64) {
34+
self.field = field
35+
}
36+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export type Bytes = number[];
2+
3+
export type TestStruct3 = string;
4+
5+
/**
6+
* Example of a type that is conditionally typeshared
7+
* based on a feature "typeshare-support". This does not
8+
* conditionally typeshare but allows a conditionally
9+
* typeshared type to generate typeshare types when behind
10+
* a `cfg_attr` condition.
11+
*/
12+
export interface TestStruct1 {
13+
field: string;
14+
}
15+
16+
export interface TestStruct2<R> {
17+
field1: string;
18+
field2: R;
19+
}
20+
21+
export interface TestStruct4 {
22+
field: number;
23+
}
24+

0 commit comments

Comments
 (0)