<div align="center">
<img height="150" src=".github/logo.png" alt="Specta Logo"></img>
<h1>Specta</h1>
<p><b>Easily export your Rust types to other languages</b></p>
<a href="https://discord.com/invite/5M6fpszrry"><img src="https://img.shields.io/discord/1011665225809924136?style=flat-square" alt="Discord"></a>
<a href="https://crates.io/crates/specta"><img src="https://img.shields.io/crates/d/specta?style=flat-square" alt="Crates.io"></a>
<a href="https://crates.io/crates/specta"><img src="https://img.shields.io/crates/v/specta.svg?style=flat-square"
alt="crates.io" /></a>
<a href="https://docs.rs/specta"><img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs" /></a>
<a href="/LICENSE.md"><img src="https://img.shields.io/crates/l/specta?style=flat-square" alt="License"></a>
</div>
<br>
## Features
- Export structs and enums to multiple languages
- Get function types to use in libraries like [tauri-specta](https://github.com/specta-rs/tauri-specta)
- Supports wide range of common crates in Rust ecosystem
- Supports type inference - can determine type of `fn demo() -> impl Type`
## Language Support
| **TypeScript** | ✅ **Stable** | [`specta-typescript`](https://crates.io/crates/specta-typescript) | Full type support, generics, unions |
| **Swift** | ✅ **Stable** | [`specta-swift`](https://crates.io/crates/specta-swift) | Idiomatic Swift, custom Codable, Duration support |
| **Rust** | 🚧 **Partial** | [`specta-rust`](https://crates.io/crates/specta-rust) | Rust source, modules, generics; structural wire-only shapes error |
| **OpenAPI** | 🚧 **Partial** | [`specta-openapi`](https://crates.io/crates/specta-openapi) | OpenAPI 3.0 schemas, generics, JSON and YAML |
| **Go** | 🚧 **Partial** | [`specta-go`](https://crates.io/crates/specta-go) | Structs, generics, string enums, layouts, Serde |
| **Kotlin** | 🚧 **Partial** | [`specta-kotlin`](https://crates.io/crates/specta-kotlin) | Data/sealed classes, generics, optional Kotlinx |
| **JSON Schema** | 🚧 **Partial** | [`specta-jsonschema`](https://crates.io/crates/specta-jsonschema) | Draft 7/2019-09/2020-12, generics, Serde |
| **Zod** | 🚧 **Partial** | [`specta-zod`](https://crates.io/crates/specta-zod) | Zod 4 schemas, inferred types, generics, layouts |
| **Valibot** | 🚧 **Partial** | [`specta-valibot`](https://crates.io/crates/specta-valibot) | Valibot schemas, inferred types, generics, layouts |
| **Python** | 🚧 **Partial** | [`specta-python`](./specta-python) | Python 3.13 TypedDicts and type hints |
| **C#** | 🚧 **Partial** | `specta-csharp` | Records, enums, generics, namespaces, file layouts |
| **Java** | 🚧 **Partial** | `specta-java` | Java 17 records, enums, and sealed interfaces |
### Legend
- ✅ **Stable**: Production-ready with comprehensive test coverage
- 🚧 **Partial**: Basic functionality implemented, complex types in progress
## Implementation Status
The Specta ecosystem is actively developed with varying levels of completeness:
- **Production Ready (2)**: TypeScript and Swift exporters are fully functional with comprehensive test coverage
- **Partially Implemented (10)**: Rust, OpenAPI, Go, C#, Java, Kotlin, JSON Schema, Zod, Valibot, and Python have broad type support but are still stabilizing
- **Planned (0)**: All currently listed exporters have an initial implementation
For the most up-to-date status of each exporter, check the individual crate documentation and issue trackers.
## Ecosystem
Specta can be used in your application either directly or through a library which simplifies the process of using it.
- [rspc](https://github.com/specta-rs/rspc) - Easily building end-to-end typesafe APIs
- [tauri-specta](https://github.com/specta-rs/tauri-specta) - Typesafe Tauri commands and events
- [TauRPC](https://github.com/MatsDK/TauRPC) - Tauri extension to give you a fully-typed IPC layer.
## Usage
Add the [`specta`](https://docs.rs/specta) crate along with any Specta language exporter crate:
```bash
# Core Specta library
cargo add specta
# Language exporters (choose one or more)
cargo add specta_typescript # TypeScript (stable)
cargo add specta_swift # Swift (stable)
cargo add specta_rust # Rust (partial)
cargo add specta_openapi # OpenAPI 3.0 schemas (JSON or YAML)
cargo add specta_java # Java 17 (partial)
cargo add specta_go # Go (partial)
cargo add specta-python # Python 3.13 type hints (partial)
cargo add specta_kotlin # Kotlin (partial)
cargo add specta_jsonschema # JSON Schema (partial)
cargo add specta_zod # Zod schemas (partial)
cargo add specta-valibot # Valibot schemas (partial)
cargo add specta-csharp # C# (partial)
```
Then you can use Specta like following:
### TypeScript Example
```rust
use specta::{Type, Types};
use specta_typescript::Typescript;
#[derive(Type)]
pub struct TypeOne {
pub a: String,
pub b: GenericType<i32>,
#[serde(rename = "cccccc")]
pub c: MyEnum,
}
#[derive(Type)]
pub struct GenericType<A> {
pub my_field: String,
pub generic: A,
}
#[derive(Type)]
pub enum MyEnum {
A,
B,
C,
}
fn main() {
let types = Types::default()
// You don't need to specify `GenericType` or `MyEnum` because they are referenced by `TypeOne`
.register::<TypeOne>();
Typescript::default()
.export_to("./bindings.ts", &types)
.unwrap();
// if you need more control over file saving
assert_eq!(
Typescript::default().export(&types).unwrap(),
r#"// This file has been generated by Specta. DO NOT EDIT.
export type GenericType<A> = { my_field: string; generic: A };
export type TypeOne = { a: string; b: GenericType<number>; cccccc: MyEnum };
"#
);
}
```
### Multi-Language Export Example
You can export the same types to multiple languages:
```rust
use specta::{Type, Types};
use specta_typescript::Typescript;
use specta_swift::Swift;
#[derive(Type)]
pub struct User {
pub id: u32,
pub name: String,
pub email: Option<String>,
}
fn main() {
let types = Types::default()
.register::<User>();
// Export to TypeScript (stable)
Typescript::default()
.export_to("./types.ts", &types)
.unwrap();
// Export to Swift (stable)
Swift::default()
.export_to("./Types.swift", &types)
.unwrap();
// Note: Other exporters are in development
}
```
A common use case is to export all types for which `specta::Type` is derived into a single file:
```rust
//! NOTE: This example requires the `export` feature on the `specta` crate
use specta::Type;
use specta_typescript::Typescript;
#[derive(Type)]
pub enum MyEither<L, R> {
Left(L),
Right(R),
}
#[derive(Type)]
pub struct GenericType<A> {
pub my_field: String,
pub generic: A,
}
#[derive(Type)]
pub enum MyEnum {
A,
B,
C,
}
#[derive(Type)]
#[specta(collect = false)]
pub struct DontExportMe {
field: String,
}
fn main() {
Typescript::default()
.export_to("./bindings.ts", &specta::export())
.unwrap();
}
```
Check out the [docs](https://docs.rs/specta) for more information.
## Motivation
This library was originally created to power the type exporting functionality of [rspc](https://rspc.dev),
but after building it we realized that it could be useful for other projects as well so we decided to move it into a dedicated library.
A huge thanks to [Brendonovich](https://github.com/brendonovich) for doing a heap of early development on this library.
## Releasing
Publishing to [crates.io](https://crates.io/crates/specta) uses
[crates.io trusted publishing](https://crates.io/docs/trusted-publishing): the
`release` workflow exchanges its GitHub OIDC identity for a short-lived crates.io
token, so **no long-lived `CARGO_REGISTRY_TOKEN` is stored** in this repository or
in Actions secrets.
**One-time setup** — a crate owner adds a trusted publisher in the crates.io UI
(the crate's **Settings → Trusted Publishing**) for every crate in the workspace.
These values must match `.github/workflows/release.yml` exactly, or the token
exchange is refused:
| Owner | `specta-rs` |
| Repository | `specta` |
| Workflow filename | `release.yml` |
| Environment | `release` |
The workflow publishes the whole workspace, so each of the following crates needs
its own trusted publisher: `specta`, `specta-macros`, `specta-serde`,
`specta-util`, `specta-typescript`, `specta-swift`, `specta-openapi`,
`specta-jsonschema`, `specta-kotlin`, `specta-csharp`, `specta-go`,
`specta-python`, `specta-java`, `specta-zod`, `specta-valibot`, `specta-rust` and
`specta-rescript`.
A trusted publisher can only be configured once a crate exists on crates.io, and
the initial publish requires an API token. `specta-valibot`, `specta-rust` and
`specta-rescript` have never been released, so publish each of them once from your
machine before adding its trusted publisher.
Adding required reviewers to the `release` GitHub environment is recommended: it
turns publishing into an approved action rather than something any tag push does.
**Cutting a release:**
1. Bump the `version` of every workspace crate (the tag names the `specta` crate
version) and merge to `main`. The exporters pin `specta` to an exact version,
so each one has to be republished whenever `specta` changes, and Cargo refuses
to publish a version that already exists on crates.io.
2. Tag the merge commit and push the tag:
```bash
git tag v2.0.0-rc.27
git push origin v2.0.0-rc.27
```
The `release` workflow then verifies the release (the tag matches the `specta`
crate version, and the workspace builds and tests), publishes every crate to
crates.io, and creates the GitHub release.