Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 22 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
# Typeshare

| Crate | Status |
|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Crate | Status |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| typeshare | [![crates.io version](https://img.shields.io/crates/v/typeshare.svg)](https://crates.io/crates/typeshare) [![crate documentation](https://docs.rs/typeshare/badge.svg)](https://docs.rs/typeshare) ![MSRV](https://img.shields.io/badge/rustc-stable-blue.svg) [![crates.io downloads](https://img.shields.io/crates/d/typeshare.svg)](https://crates.io/crates/typeshare) |
| typeshare-core | [![crates.io version](https://img.shields.io/crates/v/typeshare-core.svg)](https://crates.io/crates/typeshare-core) [![crate documentation](https://docs.rs/typeshare-core/badge.svg)](https://docs.rs/typeshare-core) ![MSRV](https://img.shields.io/badge/rustc-stable-blue.svg) [![crates.io downloads](https://img.shields.io/crates/d/typeshare-core.svg)](https://crates.io/crates/typeshare-core) |
| typeshare-annotation | [![crates.io version](https://img.shields.io/crates/v/typeshare-annotation.svg)](https://crates.io/crates/typeshare-annotation) [![crate documentation](https://docs.rs/typeshare-annotation/badge.svg)](https://docs.rs/typeshare-annotation) ![MSRV](https://img.shields.io/badge/rustc-stable-blue.svg) [![crates.io downloads](https://img.shields.io/crates/d/typeshare-annotation.svg)](https://crates.io/crates/typeshare-annotation) |
| typeshare-cli | [![crates.io version](https://img.shields.io/crates/v/typeshare-cli.svg)](https://crates.io/crates/typeshare-cli) ![MSRV](https://img.shields.io/badge/rustc-stable-blue.svg) [![crates.io downloads](https://img.shields.io/crates/d/typeshare-cli.svg)](https://crates.io/crates/typeshare-cli) |


_One tool to rule the types,_

_One tool to FFI them,_
Expand All @@ -16,22 +15,19 @@ _One tool to parse your Rust,_

_And in the darkness, compile them_ 💍

Do you like manually managing types that need to be passed through an FFI layer, so that your code doesn't archaically break at runtime? Be honest, nobody does. Typeshare is here to take that burden away from you! Leveraging the power of the `serde` library, Typeshare is a tool that converts your Rust types into their equivalent forms in Swift, Kotlin, Typescript, and a growing family of other language implementations, keeping your cross-language codebase in sync. With automatic implementation for serialization and deserialization on both sides of the FFI, Typeshare does all the heavy lifting for you. It can even handle generics and convert effortlessly between standard libraries in different languages!

Do you like manually managing types that need to be passed through an FFI layer, so that your code doesn't archaically break at runtime? Be honest, nobody does. Typeshare is here to take that burden away from you! Leveraging the power of the `serde` library, Typeshare is a tool that converts your
Rust types into their equivalent forms in Swift, Go**, Python**, Kotlin, Scala and Typescript, keeping
your cross-language codebase in sync. With automatic implementation for serialization and deserialization on both sides of the FFI, Typeshare does all the heavy lifting for you. It can even handle generics and convert effortlessly between standard libraries in different languages!

**A few caveats. See [here](#a-quick-refresher-on-supported-languages) for more details.
\*\*A few caveats. See [here](#what-about-other-languages) for more details.

## Installation


To install the CLI (Command Line Interface):

```
cargo install typeshare-cli
cargo install typeshare2-cli
```

💡Note that the console command will be `typeshare`, not `typeshare-cli`.
💡Note that the console command will be `typeshare2`, not `typeshare2-cli`.

In your `Cargo.toml`, under `[dependencies]`:

Expand All @@ -40,16 +36,17 @@ typeshare = "1.0.0"
```

## Using Typeshare

We've put together a book that documents (almost) everything you can do.

📚[Read the Typeshare book here!](https://1password.github.io/typeshare)

To generate FFI definitions for a target language, run the `typeshare` command and specify the directory containing your rust code, the language you would like to generate for, and the file to which your generated definitions will be written:
To generate FFI definitions for a target language, run the `typeshare2` command and specify the directory containing your rust code, the language you would like to generate for, and the file to which your generated definitions will be written:

```
typeshare ./my_rust_project --lang=kotlin --output-file=my_kotlin_definitions.kt
typeshare ./my_rust_project --lang=swift --output-file=my_swift_definitions.swift
typeshare ./my_rust_project --lang=scala --output-file=my_scala_definitions.scala
typeshare ./my_rust_project --lang=typescript --output-file=my_typescript_definitions.ts
typeshare2 ./my_rust_project --lang=kotlin --output-file=my_kotlin_definitions.kt
typeshare2 ./my_rust_project --lang=swift --output-file=my_swift_definitions.swift
typeshare2 ./my_rust_project --lang=typescript --output-file=my_typescript_definitions.ts
```

### Annotating Types
Expand All @@ -73,6 +70,7 @@ enum MyEnum {
MyNumber(u32),
}
```

```typescript
// Generated Typescript definitions

Expand All @@ -82,30 +80,24 @@ export interface MyStruct {
}

export type MyEnum =
| { type: "MyVariant", content: boolean }
| { type: "MyOtherVariant", content: undefined }
| { type: "MyNumber", content: number };
| { type: "MyVariant"; content: boolean; }
| { type: "MyOtherVariant"; content: undefined; }
| { type: "MyNumber"; content: number; };
```

## Getting Help

Are you getting weird deserialization issues? Did our procedural macro throw a confusing error at you? Are you trying to contribute and our existing codebase is confusing? (probably true) Did you have another problem not enumerated in this reductive list? Please open an issue in this repository and the 1Password team would be happy to help! That's what we're here for!

## A Quick Refresher on Supported Languages
## What about other languages?

- Kotlin
- Scala
- Swift
- Typescript
- Go**
- Python** (see list of limitations [here](https://github.com/1Password/typeshare/issues/217))
The official `typeshare2` binary supports Swift, Kotlin, and Typescript, but there are unofficial implementations out there for other languages. Feel free to add your own to this list (check out the [app](./app/README.md) directory for details on how to create your own typeshare).

- [typeshare-java](https://crates.io/crates/typeshare-java)

If there is a language that you want Typeshare to generate definitions for, you can either:
1. Open an issue in this repository requesting your language of choice.
2. Implement support for that language and open a PR with your implementation. We would be eternally grateful! 🙏
### The language I want isn't supported

** Right now, Go and Python support is experimental. Enable the `go` or `python` features, respectively, when installing typeshare-cli if you want to use these.
Typeshare is designed to make it as easy as possible to implement your own languages. Take a look at the [app](./app/README.md) directory for details on how to get started.

## Credits

Expand Down
2 changes: 1 addition & 1 deletion app/driver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ use typeshare_python::Python;
typeshare_binary! { Python, Golang }
```

This creates an `fn main` that uses these languages, plus `typeshare-engine`, to implements a full typeshare CLI.
This creates a `fn main` that uses these languages, plus `typeshare-engine`, to implement a full typeshare CLI.
4 changes: 2 additions & 2 deletions app/engine/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# typeshare-engine

This crate includes all of the actual implementations of typeshare functionality. It should be a dependency of anyone trying to USE typeshare as a library. It depends ONLY on typeshare-model, not on any language crates. It exports functions that make use of the trait in typeshare-model.
This crate includes all of the actual implementations of typeshare functionality. It should be a dependency of anyone trying to USE typeshare as a library. It exports functions that make use of the trait in typeshare-model.

Currently, the public API of typeshare-engine is considered fairly unstable. Feel free to use it, but we expect updates to usually be published as major versions. You only need to depend on `typeshare-engine` if you want to use typeshare as a _library_; if you're implementing your own language, you only need to depend on `typeshare-model`, and if you're creating a typeshare binary, you only also need `typeshare-driver`.
Currently, the public API of typeshare-engine is considered fairly unstable. Feel free to use it, but we expect updates to usually be published as major versions. You only need to depend on `typeshare-engine` if you want to use typeshare as a _library_; if you're implementing your own language, you only need to depend on [typeshare-model](../model), and if you're creating a typeshare binary, you only also need [typeshare-driver](../driver)
2 changes: 1 addition & 1 deletion app/model/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# typeshare-model

This crate is the base dependency for almost everything else. It includes especially the types and traits necessary for a single language implementation (that is, typeshare-swift could depend ONLY on typeshare-model). It can include utility functionality that might be necessary for a language implementation, but ideally not much in the way of major implementation stuff.
This crate is the core dependency for all implementations of specific languages for typeshare. It defines the [`Language`](https://docs.rs/typeshare-model/latest/typeshare_model/trait.Language.html) trait, along with a handful of supporting types, which a language implementation must implement. Check out the official implementations for [Swift](../langs/swift/src/lib.rs), [Kotlin](../langs/kotlin/src/lib.rs), and [Typescript](../langs/typescript/src/lib.rs) for examples.
4 changes: 2 additions & 2 deletions app/model/src/language.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ language to emit appropriate import statements for its own language.
language.write_imports(&mut file, crate_name, computed_imports)
```

6. For EACE typeshared item in being typeshared, we call `write_enum`,
6. For EACH typeshared item in being typeshared, we call `write_enum`,
`write_struct`, `write_type_alias`, or `write_const`, as appropriate.

```ignore
Expand Down Expand Up @@ -181,7 +181,7 @@ pub trait Language<'config>: Sized + Sync + Debug {
`serde`.

It is important that this type include `#[serde(default)]` or something
equivelent, so that a config can be loaded with default setting even
equivalent, so that a config can be loaded with default setting even
if this language isn't present in the config file.

The `serialize` implementation for this type should NOT skip keys, if
Expand Down
8 changes: 5 additions & 3 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

- [Overview](./overview.md)
- [Installation](./installation.md)
- [Language Implementation](./new-languages.md)
- [Testing your Typeshare](./testing.md)
- [Usage](./usage/usage.md)
- [Annotations](./usage/annotations.md)
- [Configuration](./usage/configuration.md)
- [Target OS](./usage/target_os.md)
- [Annotations](./usage/annotations.md)
- [Configuration](./usage/configuration.md)
- [Target OS](./usage/target_os.md)
- [Contributing](./contributing.md)
2 changes: 2 additions & 0 deletions docs/src/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Thank you! If you would like to contribute to the project, go to [the GitHub repository](https://github.com/1Password/typeshare). There, you can open issues to report bugs and request features.

If you want to implement your own language, you can do so as a separate crate! Check out the walkthrough [here](./new-languages.md).

If you want to contribute code, please send a [GitHub Pull Request](https://github.com/1Password/typeshare/pull/new/master) to the repository with a clear list of your changes and the reasons for the changes. Before contributing, you should probably familiarize yourself with the internal documentation, if you haven't already. Make sure to look through the READMEs for each part of Typeshare you plan to contribute to, and double-check the Rust documentation on `docs.rs` if you want to know more about the API. And feel free to open an issue if you want to ask the 1Password team directly, or if the documentation is unclear.

For larger changes, please open an RFC issue first in order to discuss the broader impacts of the proposed change with the other maintainers.
9 changes: 7 additions & 2 deletions docs/src/installation.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
# Installation

The easiest way to install the Typeshare CLI is with `cargo`. Just run the following command:
Identify which typeshare implements the language(s) you'd like to use, and install it with `cargo install`. The official typeshare binary supports Kotlin, Swift, and Typescript:

```
cargo install typeshare-cli
cargo install typeshare2-cli
```

There are also third party crates implementing other languages using typeshare, such as:

- Java: `cargo install typeshare-java`

Once you have the CLI installed, you then need to annotate the rust types that you want to generate FFI definitions for. In order to be able to use the `#[typeshare]` annotation, you will need to add `typeshare` as a dependency to your project's `Cargo.toml`.

```toml
Expand Down
115 changes: 115 additions & 0 deletions docs/src/new-languages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Making your own typeshare

Typeshare is designed to make it easy to implement your own languages as a separate binary. This document provides a basic walkthrough for how to create your own langauge implementation.

## Project Structure

There are two key components to making your own Typeshare implementation:
- `typeshare-model`
- `typeshare-driver`

In a typical Typeshare project, each language implementation will be its own crate, with the `typeshare-driver` bringing your implementations together into a single CLI binary.

```
typeshare-binary
├── typeshare-language-1
│ ├── src
│ │ └── lib.rs
│ └── cargo.toml
├── typeshare-language-2
│ ├── src
│ │ └── lib.rs
│ └── cargo.toml
├── src
│ └── main.rs
└── cargo.toml
```

## Start the Project

Set up the binary:

```bash
cargo new my-typeshare-binary
cd my-typeshare-binary
```

This is where the `typeshare-driver`'s main macro for setting up the binary will be. Add the `typeshare-driver` dependency
```bash
cargo add typeshare-driver
```
You will also need to add the dependency `log`

```bash
cargo add log
```

Add a crate for your language implementation (or as many as you want! Up to 16):
```bash
cargo new my-typeshare-language --lib
cd my-typeshare-language
```
Once you have your language crate, add the `typeshare-model` dependency:

```bash
cargo add typeshare-model
```
Some other dependencies you will need to implement your own language include:
- `thiserror`
- `serde` with the feature `derive`
- `joinery`
- `itertools`
- `anyhow`

So make sure to add these to your language crate as well

## Implementing a Language

Once your project is set up, you need to implement the Language trait from `typeshare-model` inside your langugae crate.

There a few functions that have to be implemented:
- `new_from_config`: This instantiates your language struct using configuration from a `typeshare.toml` file or the command line
- `output_filename_for_crate`: This is used in multi-file mode for setting up consistent naming for the files.
- `write_*`: These functions are your implementations of how various types should be handled. These implementations should typically call `format_type`, which is used to format Rust types into strings for writing to the Typeshare generated file.
- `format_special_type`: This is where you add custom implementations of special types for each language that is called from within `format_type`.

Additional functions that are optional but common to implement include:
- `mapped_type`: This allows you to create specific custom handling for specific types.
- `begin_file`, `end_file`, `write_additional_files` to add other per-file or per-directory handling, such as information about the file generation in comments at the top of the file, or other custom handling.

See the function documentation for more detailed information and look through our implementations of Typeshare for [Kotlin](../../app/langs/kotlin/), [Swift](../../app/langs/swift/), and [Typescript](../../app/langs/typescript/) for examples.

Once the Language trait has been implemented, your Typeshare is ready to be built!

## Building Your Typeshare

In `main.rs` once your language has been implemented, all you have to do is pass them into the `typeshare_driver` macro:
```rust
use typeshare_driver::typeshare_binary;
use typeshare_language_1::YourLanguage1;
use typeshare_language_2::YourLanguage2;

typeshare_binary! { YourLanguage1, YourLanguage2 }
```
Now, running `cargo build` will build your Typeshare with your language implementations and create a fully functional CLI tool.

## Using Your Typeshare

In a project where you want to use Typeshare, add `typeshare` to your `cargo.toml` and add the Typeshare annotations to any types you want to share (see more about using annotations [here](./usage/annotations.md)). Set up a `typeshare.toml` at the root of your project with any configuration information for your implementation.

The general usage of a Typeshare CLI binary is:
```bash
typeshare-binary --lang <your-language-1> --output-file <path-to-your-output-file> <path-to-directory-to-run-typeshare-on>
```
or
```bash
typeshare-binary --lang <your-language-1> --output-folder <path-to-your-output-folder> <path-to-directory-to-run-typeshare-on>
```
depending if you want the generated code to be outputted into a folder or a file.

To run your Typeshare for multiple languages, you rerun the command with each desired language.

To see all possible languages in a Typeshare and learn about other possible commands run:
```bash
typeshare-binary --help
```
Loading
Loading