Annotations
Annotating Types
Add the #[typeshare]
attribute to any struct or enum you define to generate definitions for that type in the selected output language.
#![allow(unused)] fn main() { #[typeshare] struct MyStruct { my_name: String, my_age: u32, } #[typeshare] enum MyEnum { MyVariant, MyOtherVariant, MyNumber(u32), } }
Annotation arguments
We can add arguments to the #[typeshare]
annotation to modify the generated definitions.
Decorators
It can be used to add decorators like Swift protocols or Kotlin interfaces to the generated output types. For example, we can do
#![allow(unused)] fn main() { #[typeshare(swift = "Equatable, Codable, Comparable, Hashable")] #[serde(tag = "type", content = "content")] pub enum BestHockeyTeams { MontrealCanadiens, Lies(String), } }
and this will produce the following Swift definition:
public enum OPBestHockeyTeams2: Codable, Comparable, Equatable, Hashable {
case montrealCanadiens
case lies(String)
enum CodingKeys: String, CodingKey, Codable {
case montrealCanadiens = "MontrealCanadiens",
lies = "Lies"
}
private enum ContainerCodingKeys: String, CodingKey {
case type, content
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ContainerCodingKeys.self)
if let type = try? container.decode(CodingKeys.self, forKey: .type) {
switch type {
case .montrealCanadiens:
self = .montrealCanadiens
return
case .lies:
if let content = try? container.decode(String.self, forKey: .content) {
self = .lies(content)
return
}
}
}
throw DecodingError.typeMismatch(OPBestHockeyTeams2.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for OPBestHockeyTeams"))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: ContainerCodingKeys.self)
switch self {
case .montrealCanadiens:
try container.encode(CodingKeys.montrealCanadiens, forKey: .type)
case .lies(let content):
try container.encode(CodingKeys.lies, forKey: .type)
try container.encode(content, forKey: .content)
}
}
}
Serialize as Another Type
You can also use the serialized_as
argument to tell Typeshare to treat
the serialized type as another Rust type. This is usually combined with
custom serde attributes.
#![allow(unused)] fn main() { /// Options that you could pick #[typeshare(serialized_as = "String")] #[serde(try_from = "String", into = "String")] pub enum Options { /// Affirmative Response Yes, No, Maybe, /// Sends a string along Cool(String), } }
This would generate the following Kotlin code:
/// Options that you could pick
typealias Options = String
The #[serde]
Attribute
Since Typeshare relies on the serde
crate for handling serialization and deserialization between Rust types and the generated foreign type definitions, we can use the annotations provided by serde
on our Typeshare types. For example, the following Rust definition
#![allow(unused)] fn main() { /// This is a comment. /// Continued lovingly here #[typeshare] #[serde(rename_all = "camelCase")] pub enum Colors { Red = 0, Blue = 1, /// Green is a cool color #[serde(rename = "green-like")] Green = 2, } }
will become the following Typescript definition.
/**
* This is a comment.
* Continued lovingly here
*/
export const enum Colors {
Red = "red",
Blue = "blue",
/** Green is a cool color */
Green = "green-like",
}
Skipping Fields
Within a Rust type, there may be fields or variants that you want Typeshare to ignore. These can be skipped using either the #[serde(skip)]
annotation or the #[typeshare(skip)]
annotation. For example, this Rust type
#![allow(unused)] fn main() { #[typeshare] pub struct MyStruct { a: i32, #[serde(skip)] b: i32, c: i32, #[typeshare(skip)] d: i32, } }
becomes the following Typescript definition.
export interface MyStruct {
a: number;
c: number;
}