{"id":21546463,"url":"https://github.com/microsoft/thrifty","last_synced_at":"2025-05-15T00:11:49.260Z","repository":{"id":37768348,"uuid":"52308103","full_name":"microsoft/thrifty","owner":"microsoft","description":"Thrift for Android that saves you methods","archived":false,"fork":false,"pushed_at":"2024-11-23T02:45:42.000Z","size":2758,"stargazers_count":546,"open_issues_count":15,"forks_count":101,"subscribers_count":42,"default_branch":"master","last_synced_at":"2025-04-07T01:01:36.797Z","etag":null,"topics":["android","thrift"],"latest_commit_sha":null,"homepage":"https://github.com/benjamin-bader/thrifty","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/microsoft.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-02-22T21:42:55.000Z","updated_at":"2025-04-04T02:04:53.000Z","dependencies_parsed_at":"2023-10-12T13:56:37.529Z","dependency_job_id":"54d68e09-d56f-44be-9ae2-c31d9e9fa603","html_url":"https://github.com/microsoft/thrifty","commit_stats":null,"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoft%2Fthrifty","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoft%2Fthrifty/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoft%2Fthrifty/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoft%2Fthrifty/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/microsoft","download_url":"https://codeload.github.com/microsoft/thrifty/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248819379,"owners_count":21166476,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","thrift"],"created_at":"2024-11-24T06:12:13.616Z","updated_at":"2025-04-14T03:57:02.670Z","avatar_url":"https://github.com/microsoft.png","language":"Kotlin","funding_links":[],"categories":["Libs","Utility"],"sub_categories":["\u003cA NAME=\"Utility\"\u003e\u003c/A\u003eUtility"],"readme":"Thrifty\n=======\n\n![Build status](https://github.com/microsoft/thrifty/workflows/Pre-merge%20checks/badge.svg) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Thrifty-green.svg?style=true)](https://android-arsenal.com/details/1/3227)\n[![codecov](https://codecov.io/gh/Microsoft/thrifty/branch/master/graph/badge.svg)](https://codecov.io/gh/Microsoft/thrifty)\n\n# NOTE: This repository is [DEPRECATED](https://github.com/microsoft/thrifty/issues/540)\n## An active fork can be found at https://github.com/benjamin-bader/thrifty\n\nThrifty is an implementation of the Apache Thrift software stack, which uses 1/4 of the method count taken by\nthe Apache Thrift compiler, which makes it especially appealing for use on Android.\n\nThrift is a widely-used cross-language service-definition software stack, with a nifty interface definition language\nfrom which to generate types and RPC implementations.  Unfortunately for Android devs, the canonical implementation\ngenerates very verbose and method-heavy Java code, in a manner that is not very Proguard-friendly.\n\nLike Square's Wire project for Protocol Buffers, Thrifty does away with getters and setters (and is-setters and\nset-is-setters) in favor of public final fields.  It maintains some core abstractions like Transport and Protocol, but\nsaves on methods by dispensing with Factories, omitting server implementations by default and only generating code for the\nprotocols you actually need.\n\nThrifty was born in the Outlook for Android codebase; before Thrifty, generated thrift classes consumed 20,000 methods.\nAfter Thrifty, the thrift method count dropped to 5,000.\n\n### Usage\n\n#### Add the runtime to your project\n\nIn `build.gradle`:\n\n```groovy\nrepositories {\n  mavenCentral()\n\n  // For snapshot builds\n  maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }\n}\n\ndependencies {\n  implementation 'com.microsoft.thrifty:thrifty-runtime-jvm:3.1.0'\n}\n```\n\n#### Generate code from your thrift files\n\nOn the command line:\n\n```bash\njava -jar thrifty-compiler.jar --out=path/to/output file_one.thrift file_two.thrift file_n.thrift\n```\n\nOr, with the Gradle plugin:\n\n```groovy\n\nbuildscript {\n  dependencies {\n    classpath 'com.microsoft.thrifty:thrifty-gradle-plugin:3.1.0'\n  }\n}\n\napply plugin: 'com.microsoft.thrifty'\n\nthrifty {\n  // Optionally configure things, see thrifty-gradle-plugin/README.md\n  // for all the details\n}\n\n```\n\n### Building\n\n```bash\n./gradlew build\n```\n\n### Testing\n\n```bash\n./gradlew check\n```\n\n### Contributing\n\nWe welcome contributions at all levels.  Contributions could be as simple as bug reports and feature suggestions,\ntypo fixes, additional tests, bugfixes, even new features.  If you wish to contribute code, please be sure to read our\n[Contributing Guide](CONTRIBUTING.md).\n\n### Differences with Apache Thrift\n\nThrifty structs and clients are 100% compatible with Apache Thrift services.\n\nThe major differences are:\n\n- Thrifty structs are immutable.\n- Thrifty structs are always valid, once built.\n- Fields that are neither required nor optional (i.e., \"default\") are treated as optional; a struct with an unset default field may still be serialized.\n- TupleProtocol is unsupported at present.\n- Server-specific features are only supported for Kotlin code generation and currently considered experimental\n\n## Guide To Thrifty\n\nThrift is a language-agnostic remote-procedure-call (RPC) definition toolkit.  Services, along with a rich set of\nstructured data, are defined using the Thrift Interface Definition Language (IDL).  This IDL is then compiled into\none or more target languages (e.g. Java), where it can be used as-is to invoke RPC methods on remote services.\n\nThrifty is an alternate implementation of Thrift targeted at Android usage.  Its benefits over the standard Apache\nimplementation are its greatly reduced method count and its increased type-safety.  By generating immutable classes\nthat are validated before construction, consuming code doesn't have to constantly check if required data is set or not.\n\n#### Interface Definition Language\n\nThe Thrift IDL is a simple and standardized way to define data, data structures, and services:\n\n```thrift\n// Let's call this example.thrift\n\nnamespace java com.foo.bar\n\nstruct Query {\n  1: required string text,\n  2: optional i64 resultsNewerThan\n}\n\nstruct SearchResult {\n  1: required string url,\n  2: required list\u003cstring\u003e keywords = [], // A list of keywords related to the result\n  3: required i64 lastUpdatedMillis // The time at which the result was last checked, in unix millis\n}\n\nservice Google {\n  list\u003cSearchResult\u003e search(1: Query query)\n}\n```\n\nFor an authoritative source on Thrift IDL, [Thrift: The Missing Guide](https://diwakergupta.github.io/thrift-missing-guide/) is an excellent introduction.\n\n#### Generating Code\n\nUse `thrifty-compiler` to compile IDL into Java classes:\n\n```bash\njava -jar thrifty-compiler.jar --kt-file-per-type --out=path/to/output example.thrift\n```\n\nThe example file will result in the following files being generated:\n\n```\npath/to/output/\n  - com/foo/bar/\n    - Google.kt\n    - GoogleClient.kt\n    - Query.kt\n    - SearchResult.kt\n```\n\nThe interesting files here are, of course, our domain objects `Query` and `SearchResult`.\n\nThe latter looks like this:\n\n```kotlin\npackage com.foo.bar\n\nimport com.microsoft.thrifty.Struct\nimport com.microsoft.thrifty.TType\nimport com.microsoft.thrifty.ThriftField\nimport com.microsoft.thrifty.kotlin.Adapter\nimport com.microsoft.thrifty.protocol.Protocol\nimport com.microsoft.thrifty.util.ProtocolUtil\nimport kotlin.Long\nimport kotlin.String\nimport kotlin.Unit\nimport kotlin.jvm.JvmField\n\npublic data class SearchResult(\n  @JvmField\n  @ThriftField(fieldId = 1, isRequired = true)\n  public val url: String,\n\n  @JvmField\n  @ThriftField(fieldId = 2, isRequired = true)\n  public val keywords: List\u003cString\u003e,\n\n  @JvmField\n  @ThriftField(fieldId = 3, isRequired = true)\n  public val lastUpdatedMillis: Long\n) : Struct {\n  public override fun write(protocol: Protocol): Unit {\n    ADAPTER.write(protocol, this)\n  }\n\n  private class SearchResultAdapter : Adapter\u003cSearchResult\u003e {\n    // Uninteresting but important serialization code\n  }\n\n  public companion object {\n    @JvmField\n    public val ADAPTER: Adapter\u003cSearchResult\u003e = SearchResultAdapter()\n  }\n}\n```\n\nThe struct itself is immutable and has a minimal number of methods.  It can be constructed only\nwith all required fields (all of them, in this example).  An Adapter implementation (whose body we\nomit here because it is long and mechanical) handles reading and writing `SearchResult` structs to\nand from `Protocols`.\n\nFinally and separately, note `Google` and `GoogleClient` - the former is an interface, and the latter is an autogenerated implementation.\n\nYou may notice the similarity to protobuf classes generated by Wire - this is intentional!\nThe design principles codified there - immutable data, build-time validation, preferring fields over methods,\nseparating data representation from serialization logic - lead to better, safer code, and more breathing room\nfor Android applications.\n\n#### Using Generated Code\n\nGiven the example above, the code to invoke `Google.search()` might be:\n\n```kotlin\n// Transports define how bytes move to and from their destination\nval transport = SocketTransport.Builder(\"thrift.google.com\", 80).build().apply { connect() }\n\n// Protocols define the mapping between structs and bytes\nval protocol = BinaryProtocol(transport)\n\n// Generated clients do the plumbing\nval client = GoogleClient(protocol, object : AsyncClientBase.Listener {\n    override fun onTransportClosed() {\n\n    }\n\n    override fun onError(throwable: Throwable) {\n        throw AssertionError(throwable)\n    }\n})\n\nval query = Query(text = \"thrift vs protocol buffers\")\n\n// RPC clients are asynchronous and callback-based\nclient.search(query, object : ServiceMethodCallback\u003cList\u003cSearchResult\u003e\u003e {\n    override fun onSuccess(response: List\u003cSearchResult\u003e) {\n        // yay\n    }\n\n    override fun onError(throwable: Throwable) {\n        Log.e(\"GoogleClient\", \"Search error: $throwable\")\n    }\n})\n\n// ...unless coroutine clients were generated:\nval results = async { client.search(query) }.await()\n\n```\n\n### Extensibility\n\nEvery project has its own requirements, and no one style of boilerplate can fill them all.  Thrifty offers a small but \npowerful plugin model that you can implement, using the standard Java SPI mechanism, which will allow one to customize\neach generated class before it is written out to disk.  Read more about it in the [thrifty-compiler-plugins README](thrifty-compiler-plugins/README.md).  You can see a worked example in [thrifty-example-postprocessor](thrifty-example-postprocessor).\n\n### Hiding PII with Redaction and Obfuscation\n\nPersonally-Identifiable Information (PII) is an inevitability in most systems, and often there are legal consequences\nif it is not handled carefully.  Thrifty allows you to avoid logging PII contained in generated classes by supporting\nboth total redaction and obfuscation.  It is as simple as adding annotations to your Thrift IDL:\n\n```thrift\nstruct User {\n  1: required string email (obfuscated)\n  2: required string ssn (redacted)\n}\n```\n\nThe difference between redaction and obfuscation is small but important.  In `.toString()`, `redacted` fields are totally replaced\nwith the string `\"\u003cREDACTED\u003e\"` - no information survives.  This meets the goal of not leaking PII, but has the consequence that\nsometimes debugging can be difficult.  `obfuscated` fields, on the other hand, are treated differently.  Their values are hashed,\nand this hash is printed.  This allows one to distinguish between unique values in log files, without compromising user privacy.\n\nThe Thrift annotations `(thrifty.redacted)` and `(thrifty.obfuscated)` are also accepted by the compiler.\n\nThe Thrift example above leads to code similar to the following:\n\n```kotlin\npublic data class User(\n    @JvmField\n    @ThriftField(fieldId = 1, required = true)\n    @Obfuscated\n    public val email: String,\n\n    @JvmField\n    @ThriftField(fieldId = 2, required = true)\n    @Redacted\n    public val ssn: String\n) : Struct {\n  public override fun toString() =\n      \"User(email=${ObfuscationUtil.hash(email)}, ssn=\u003cREDACTED\u003e)\"\n\n  // more code\n}\n```\n\nObfuscated fields that are collections are not hashed; instead, their type is printed, along with the collection size, e.g. `map\u003ci32, string\u003e(size=5)`.\n\nClose readers will note that the compiler will also respond to `@redacted` and `@obfuscated` in field documentation; this is currently valid *but not supported\nand subject to change in future releases*.  It is a legacy from the time before Thrifty implemented Thrift annotations.\n\n## Java Support\n\nThrifty generates Kotlin code by default, but if needed it can also produce Java.  Generated Java code has very slightly\nmore method references than Kotlin.  Instead of data classes, Java structs are final classes with public final fields,\nand are constructable only with dedicated `Builder` types.\n\nTo generate Java classes, pass `--lang=java` to the compiler (if using the command-line compiler), or provide a `java {}` block\nwithin the thrifty Gradle plugin configuration block.\n\n## Language-specific command-line options\n### Kotlin-specific command-line options\n\nThere are a few new command-line options to control Kotlin code generation:\n\n```\njava -jar thrifty-compiler.jar \\\n    --lang=kotlin \\\n    --service-type=coroutine \\\n    --kt-file-per-type \\\n    --omit-file-comments \\\n    --kt-struct-builders \\\n    --experimental-kt-generate-server \\\n    ...\n```\n\nBy default, generated service clients are callback-based:\n\n```kotlin\npublic interface Google {\n  public fun search(query: Query, callback: ServiceMethodCallback\u003cList\u003cSearchResult\u003e\u003e)\n}\n```\n\nIf, instead, you wish to have a coroutine-based client, specify `--service-type=coroutine`:\n\n```kotlin\npublic interface Google {\n  public suspend fun search(query: Query): List\u003cSearchResult\u003e\n}\n```\n\nBuilders are unnecessary, and are not included by default.  For compatibility with older code, you can use the `--kt-struct-builders` flag, which will result in Java-style classes with Builders.\n\nBy default, Thrifty generates one Kotlin file per JVM package.  For larger thrift files, this can be a little hard on the Kotlin compiler.  If you find build times or IDE performance suffering, the `--kt-file-per-type` flag can help.  Outlook Mobile's single, large, Kotlin file took up to one minute just to typecheck, using Kotlin 1.2.51!  For these cases, `--kt-file-per-type` will tell Thrifty to generate one single file per top-level class - just like the Java code.\n\n`--experimental-kt-generate-server` enabled code generation for the server portion of a thrift service. You can\nuse this to implement a thrift server with the same benefits as the kotlin client: no runtime surprises thanks to\nstructs being always valid by having nullability guarantees and unions represented as sealed classes.\nSee [Server Support](#server-support).\n\n#### Server Support\nSupport for generating a Kotlin server implementation was only added very recently, and while it passes the 'official'\n[Java client integration test](https://github.com/apache/thrift/blob/master/lib/java/test/org/apache/thrift/test/TestClient.java),\nyou should consider this code experimental.\n\nThrifty generates a `Processor` implementation that you pass an input `Protocol`, an output `Protocol` and a service handler\nand the code will take care of reading the request, passing it to the handler and returning the correct response to the output.\n\nIf you want to use it, you need to wrap an appropriate communication layer around it, e.g. an HTTP server.\nYou can have a look at the [integration tests](thrifty-integration-tests/src/test/kotlin/com/microsoft/thrifty/integration/conformance/server/TestServer.kt) for a basic example.\n\n### Java-specific command-line options\n\nThrifty can be made to add various kinds of nullability annotations to Java types with the `--nullability-annotation-type` flag.  Valid options are\n`none` (the default), `android-support`, and `androidx`.  Specifying `android-support` will cause generated code to use `@Nullable` and `@NonNull` from\nthe `android.support.annotation` package.  Similarly, specifying `androidx` will use analogous annotations from `androidx.annotation`. \n\n## Thanks\n\nThrifty owes an enormous debt to Square and the Wire team; without them, this project would not exist.  Thanks!\nAn equal debt is owed to Facebook and Apache for developing and opening Thrift to the world.\n\n-------\n\nCopyright © Microsoft Corporation\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmicrosoft%2Fthrifty","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmicrosoft%2Fthrifty","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmicrosoft%2Fthrifty/lists"}