Thrift for Android that saves you methods

Related tags

Utility android thrift
Overview

Thrifty

Build status Android Arsenal codecov

Thrifty is an implementation of the Apache Thrift software stack for Android, which uses 1/4 of the method count taken by the Apache Thrift compiler.

Thrift is a widely-used cross-language service-definition software stack, with a nifty interface definition language from which to generate types and RPC implementations. Unfortunately for Android devs, the canonical implementation generates very verbose and method-heavy Java code, in a manner that is not very Proguard-friendly.

Like Square's Wire project for Protocol Buffers, Thrifty does away with getters and setters (and is-setters and set-is-setters) in favor of public final fields. It maintains some core abstractions like Transport and Protocol, but saves on methods by dispensing with Factories and server implementations and only generating code for the protocols you actually need.

Thrifty was born in the Outlook for Android codebase; before Thrifty, generated thrift classes consumed 20,000 methods. After Thrifty, the thrift method count dropped to 5,000.

Usage

In build.gradle:

repositories {
  mavenCentral()

  // For snapshot builds
  maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
}

dependencies {
  implementation 'com.microsoft.thrifty:thrifty-runtime:2.1.1'
}

On the command line:

java -jar thrifty-compiler.jar --out=path/to/output file_one.thrift file_two.thrift file_n.thrift

Building

./gradlew build

Testing

./gradlew check

Contributing

We welcome contributions at all levels. Contributions could be as simple as bug reports and feature suggestions, typo fixes, additional tests, bugfixes, even new features. If you wish to contribute code, please be sure to read our Contributing Guide.

Differences with Apache Thrift

Thrifty structs and clients are 100% compatible with Apache Thrift services.

The major differences are:

  • Thrifty structs are immutable.
  • Thrifty structs are always valid, once built via a builder.
  • 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.
  • TupleProtocol is unsupported at present.
  • Server-specific features from Apache's implementation are not duplicated in Thrifty.

Guide To Thrifty

Thrift is a language-agnostic remote-procedure-call (RPC) definition toolkit. Services, along with a rich set of structured data, are defined using the Thrift Interface Definition Language (IDL). This IDL is then compiled into one or more target languages (e.g. Java), where it can be used as-is to invoke RPC methods on remote services.

Thrifty is an alternate implementation of Thrift targeted at Android usage. Its benefits over the standard Apache implementation are its greatly reduced method count and its increased type-safety. By generating immutable classes that are validated before construction, consuming code doesn't have to constantly check if required data is set or not.

Interface Definition Language

The Thrift IDL is a simple and standardized way to define data, data structures, and services:

// Let's call this example.thrift

namespace java com.foo.bar

struct Query {
  1: required string text,
  2: optional i64 resultsNewerThan
}

struct SearchResult {
  1: required string url,
  2: required list<string> keywords = [], // A list of keywords related to the result
  3: required i64 lastUpdatedMillis // The time at which the result was last checked, in unix millis
}

service Google {
  list<SearchResult> search(1: Query query)
}

For an authoritative source on Thrift IDL, Thrift: The Missing Guide is an excellent introduction.

Generating Code

Use thrifty-compiler to compile IDL into Java classes:

java -jar thrifty-compiler.jar --out=path/to/output example.thrift

The example file will result in the following files being generated:

path/to/output/
  - com/foo/bar/
    - Google.java
    - GoogleClient.java
    - Query.java
    - SearchResult.java

The interesting files here are, of course, our domain objects Query and SearchResult.

The latter looks like this:

package com.foo.bar;

import android.support.annotation.NonNull;
import com.microsoft.thrifty.Adapter;
import com.microsoft.thrifty.Struct;
import com.microsoft.thrifty.StructBuilder;
import com.microsoft.thrifty.TType;
import com.microsoft.thrifty.ThriftField;
import com.microsoft.thrifty.protocol.FieldMetadata;
import com.microsoft.thrifty.protocol.ListMetadata;
import com.microsoft.thrifty.protocol.Protocol;
import com.microsoft.thrifty.util.ProtocolUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public final class SearchResult implements Struct {
  public static final Adapter<SearchResult, Builder> ADAPTER = new SearchResultAdapter();

  @ThriftField(
      fieldId = 1,
      isRequired = true
  )
  @NonNull
  public final String url;

  /**
   * A list of keywords related to the result
   */
  @ThriftField(
      fieldId = 2,
      isRequired = true
  )
  @NonNull
  public final List<String> keywords;

  /**
   * The time at which the result was last checked, in unix millis
   */
  @ThriftField(
      fieldId = 3,
      isRequired = true
  )
  @NonNull
  public final Long lastUpdatedMillis;

  private SearchResult(Builder builder) {
    this.url = builder.url;
    this.keywords = Collections.unmodifiableList(builder.keywords);
    this.lastUpdatedMillis = builder.lastUpdatedMillis;
  }

  @Override
  public boolean equals(Object other) {
    if (this == other) return true;
    if (other == null) return false;
    if (!(other instanceof SearchResult)) return false;
    SearchResult that = (SearchResult) other;
    return (this.url == that.url || this.url.equals(that.url))
        && (this.keywords == that.keywords || this.keywords.equals(that.keywords))
        && (this.lastUpdatedMillis == that.lastUpdatedMillis || this.lastUpdatedMillis.equals(that.lastUpdatedMillis));
  }

  @Override
  public int hashCode() {
    int code = 16777619;
    code ^= this.url.hashCode();
    code *= 0x811c9dc5;
    code ^= this.keywords.hashCode();
    code *= 0x811c9dc5;
    code ^= this.lastUpdatedMillis.hashCode();
    code *= 0x811c9dc5;
    return code;
  }

  @Override
  public String toString() {
    return "SearchResult{url=" + this.url + ", keywords=" + this.keywords + ", lastUpdatedMillis=" + this.lastUpdatedMillis + "}";
  }

  @Override
  public void write(Protocol protocol) {
    ADAPTER.write(protocol, this);
  }

  public static final class Builder implements StructBuilder<SearchResult> {
    private String url;

    /**
     * A list of keywords related to the result
     */
    private List<String> keywords;

    /**
     * The time at which the result was last checked, in unix millis
     */
    private Long lastUpdatedMillis;

    public Builder() {
      this.keywords = new ArrayList<String>();
    }

    public Builder(SearchResult struct) {
      this.url = struct.url;
      this.keywords = struct.keywords;
      this.lastUpdatedMillis = struct.lastUpdatedMillis;
    }

    public Builder url(String url) {
      if (url == null) {
        throw new NullPointerException("Required field 'url' cannot be null");
      }
      this.url = url;
      return this;
    }

    public Builder keywords(List<String> keywords) {
      if (keywords == null) {
        throw new NullPointerException("Required field 'keywords' cannot be null");
      }
      this.keywords = keywords;
      return this;
    }

    public Builder lastUpdatedMillis(Long lastUpdatedMillis) {
      if (lastUpdatedMillis == null) {
        throw new NullPointerException("Required field 'lastUpdatedMillis' cannot be null");
      }
      this.lastUpdatedMillis = lastUpdatedMillis;
      return this;
    }

    @Override
    public SearchResult build() {
      if (this.url == null) {
        throw new IllegalStateException("Required field 'url' is missing");
      }
      if (this.keywords == null) {
        throw new IllegalStateException("Required field 'keywords' is missing");
      }
      if (this.lastUpdatedMillis == null) {
        throw new IllegalStateException("Required field 'lastUpdatedMillis' is missing");
      }
      return new SearchResult(this);
    }

    @Override
    public void reset() {
      this.url = null;
      this.keywords = new ArrayList<String>();
      this.lastUpdatedMillis = null;
    }
  }

  private static final class SearchResultAdapter implements Adapter<SearchResult, Builder> {
    // Uninteresting but important serialization code
  }
}

The struct itself is immutable and has a minimal number of methods. It can be constructed only with the assistance of a nested Builder, which validates that all required fields are set. Finally, an Adapter implementation (whose body is omitted here because it is long and mechanical) that handles reading and writing SearchResult structs to and from Protocols.

Finally and separately, note Google and GoogleClient - the former is an interface, and the latter is an autogenerated implementation.

You may notice the similarity to protobuf classes generated by Wire - this is intentional! The design principles codified there - immutable data, build-time validation, preferring fields over methods, separating data representation from serialization logic - lead to better, safer code, and more breathing room for Android applications.

Using Generated Code

Given the example above, the code to invoke Google.search() might be:

// Transports define how bytes move to and from their destination
SocketTransport transport = new SocketTransport.Builder("thrift.google.com", 80).build();
transport.connect();

// Protocols define the mapping between structs and bytes
Protocol protocol = new BinaryProtocol(transport);

// Generated clients do the plumbing
Google client = new GoogleClient(protocol, new AsyncClientBase.Listener() {
    @Override
    public void onTransportClosed() {

    }

    @Override
    public void onError(Throwable error) {
        throw new AssertionError(error);
    }
});

Query query = new Query.Builder()
    .text("thrift vs protocol buffers")
    .build();

// RPC clients are asynchronous and callback-based
client.search(query, new ServiceMethodCallback<List<SearchResult>>() {
    @Override
    public void onSuccess(List<SearchResult> response) {
        // yay
    }

    @Override
    public void onError(Throwable error) {
        Log.e("GoogleClient", "Search error: " + error);
    }
});

Extensibility

Every project has its own requirements, and no one style of boilerplate can fill them all. Thrifty offers a small but powerful plugin model that you can implement, using the standard Java SPI mechanism, which will allow one to customize each generated Java class before it is written out to disk. Read more about it in the thrifty-compiler-plugins README. You can see a worked example in thrifty-example-postprocessor.

Hiding PII with Redaction and Obfuscation

Personally-Identifiable Information (PII) is an inevitability in most systems, and often there are legal consequences if it is not handled carefully. Thrifty allows you to avoid logging PII contained in generated classes by supporting both total redaction and obfuscation. It is as simple as adding annotations to your Thrift IDL:

struct User {
  1: required string email (obfuscated)
  2: required string ssn (redacted)
}

The difference between redaction and obfuscation is small but important. In .toString(), redacted fields are totally replaced with the string "<REDACTED>" - no information survives. This meets the goal of not leaking PII, but has the consequence that sometimes debugging can be difficult. obfuscated fields, on the other hand, are treated differently. Their values are hashed, and this hash is printed. This allows one to distinguish between unique values in log files, without compromising user privacy.

The Thrift annotations (thrifty.redacted) and (thrifty.obfuscated) are also accepted by the compiler.

The Thrift example above leads to code similar to the following:

public final class User implements Struct {
  @ThriftField(
    fieldId = 1,
    required = true)
  @Obfuscated
  public final String email;

  @ThriftField(
    fieldId = 2,
    required = true)
  @Redacted
  public final String ssn;

  // more code

  @Override
  public String toString() {
    return "User{email=" + ObfuscationUtil.hash(this.email) + ", ssn=<REDACTED>}";
  }

  // more code
}

Obfuscated fields that are collections are not hashed; instead, their type is printed, along with the collection size, e.g. map<i32, string>(size=5).

Close readers will note that the compiler will also respond to @redacted and @obfuscated in field documentation; this is currently valid but not supported and subject to change in future releases. It is a legacy from the time before Thrifty implemented Thrift annotations.

Kotlin Support

Thrifty supports generated Kotlin code that is (almost) as small as its Java counterpart. Instead of classes with final fields, Thrifty Kotlin structs are represented as data class structures whose members are annotated as @JvmField. This produces classes which, when compiled, are nearly as small as their Java counterparts; the Query class above looks like this in Kotlin:

data class Query(
  @JvmField
  @ThriftField(fieldId = 1, required = true)
  val text: String,

  @JvmField
  @ThriftField(fieldId = 2, optional = true)
  val resultsNewerThan: Long?
) : Struct {

  override fun write(protocol: Protocol) {
    ADAPTER.write(protocol, this)
  }

  private class QueryAdapter : Adapter<Query> {
    override fun read(protocol: Protocol): Query {
      // deserialization code as above
    }

    override fun write(protcol: Protocol, struct: Query) {
      // serialization code as above
    }
  }

  companion object {
    @JvmField val ADAPTER: Adapter<Query> = QueryAdapter()
  }
}

Notice that, thanks to data classes, there are no longer custom toString, equals, or hashCode methods. Because we can accurately model required/optional in the Kotlin type system, we no longer require builders to enforce struct validity - if you can instantiate a Thrifty Kotlin struct, it is valid by definition.

Also note that redacted and obfuscated fields are still supported, and will result in a custom toString implementation.

How To Enable Kotlin

Add --lang=kotlin to your thrifty-compiler.jar invocation, and add the com.microsoft.thrifty:thrifty-runtime-ktx:2.1.1 dependency.

Kotlin-specific command-line options

There are a few new command-line options to control Kotlin code generation:

java -jar thrifty-compiler.jar \
    --lang=kotlin \
    --kt-coroutine-clients \
    --kt-file-per-type \
    --omit-file-comments \
    --kt-struct-builders \
    ...

The new option --lang=kotlin accepts either java or kotlin; use the latter to generate Kotlin code.

By default, standard callback-based service clients will be generated:

interface Google {
  fun search(query: Query, callback: ServiceMethodCallback<List<SearchResult>>)
}

If, instead, you wish to have a coroutine-based client, specify --kt-coroutine-clients:

interface Google {
  suspend fun search(query: Query): List<SearchResult>
}

Builders are no longer necessary, and are not included by default. For compatibility with existing code, you can use the --kt-struct-builders flag, which will result in Java-style classes with Builders.

The final new flag is --kt-file-per-type. Thrifty's convention is to generate a single Kotlin file per distinct JVM namespace. For particularly large .thrift inputs, this is suboptimal. 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.

--omit-file-comments suppresses the comment that prefixes each autogenerated Java file.

Thanks

Thrifty owes an enormous debt to Square and the Wire team; without them, this project would not exist. Thanks! An equal debt is owed to Facebook and Apache for developing and opening Thrift to the world.


Copyright © Microsoft Corporation

Comments
  • Timeline for 3.0 release

    Timeline for 3.0 release

    I'm checking in to see if there's a rough timeline for a 3.0 release. We're going to need to fork thrifty to apply (yet another) enum generation change (it's too crazy to submit to mainline), and ideally I'd make the change on 3.0 than make it on 2.1.2 and then again on 3.0

    Thanks again for all the hard work!

    opened by shashachu 17
  • Add API to configure which specific thrift files get compiled

    Add API to configure which specific thrift files get compiled

    Adds an API to allow configuring include and exclude patterns on sourceDir entries in the Gradle plugin extension.

    @jparise please take a look and let me know whether this addresses your use-case. You're welcome to comment on anything here but probably the most interesting thing will be the readme changes which document the new API, and its direct implementation in ThriftyExtension.kt should you be so inclined.

    Fixes #365

    opened by benjamin-bader 17
  • Add option to not throw on unknown enum values

    Add option to not throw on unknown enum values

    Fixes #320

    This is just a WIP which adds support only in the java codegen. If the approach seems good, tests will be added and also the support for kotlin codegen

    opened by guptadeepanshu 17
  • Options to ignore certain struct fields/properties during code generation?

    Options to ignore certain struct fields/properties during code generation?

    Hello, I'm looking to ignore struct fields of certain types based on type names (these fields are not used in Java generated code)

    If I try to do this in the implementation of KotlinTypeProcessor, e.g. calling TypeSpec.Builder.propertySpecs.removeIf { }, it's too late.

    I can do this easily in the StructType constructor:

    internal constructor(element: StructElement, namespaces: Map<NamespaceScope, String>)
                : super(UserElementMixin(element, namespaces)) {
            this.structType = element.type
            this.fields = element.fields.filter { /* CONDITION */ }.map { Field(it, namespaces) }
        }
    

    But I want to make this more generic for everyone. How do I pass an option from ThriftyCompiler so it can be picked up here?

    Thanks,

    opened by theta3 16
  • Consider @Generated annotations

    Consider @Generated annotations

    The @Generated annotation is a signal that a Java source file has been generated programatically. It is used by some static analysis tools, and some code review interfaces automatically "collapse" diffs touching generated files.

    It also provides a standard way to include metadata, such as a timestamp. Thrifty currently writes this information as a file comment.

    There might be a small cost to including this additional annotation, but I'm not experienced enough in those aspects of today's JVMs to estimate it.

    opened by jparise 15
  • Fix builder class generating name clash if union contains field named

    Fix builder class generating name clash if union contains field named "error"

    Unfortunately, there seems to be an upstream bug in addition to the missing fully qualified reference to kotlin.error in the code generator.

    Or do you have an idea why my fix would not work?

    opened by luqasn 14
  • Add Server support

    Add Server support

    Excited to have this in a shareable state and very interested in hearing your feedback :)

    I created separate commits for refactorings that were required for the new code that then gets added "append-only" in one larger commit to make it easier to gauge the impact (rather non-impact) on the existing code.

    One thing that is not perfect yet is that the synthetic arg and result structs are generated on the same level as the normal ones, opening the possibility for a name clash. Ideally, I would want them nested inside the ServerCall class, but that required changes to the existing data class/union generators, so I skipped it for now.

    opened by luqasn 13
  • Introducing flag to add a required builder constructor

    Introducing flag to add a required builder constructor

    Currently, the only enforcement of required properties in a struct is in the ".build()" final methods of the StructBuilder. This change adds a flag to build a struct builder constructor consisting of the required fields that have not yet been statically defined in thrift. This provides a compile time enforcement of required properties for a struct.

    Additionally, to fully enforce the required builder paradigm, we need to add an option to get rid of the empty constructor within the builder. The combination of these two flags should create a struct that will always have its required properties populated in addition to the null validation on build().

    opened by kivarsha 12
  • Publish release 2.0?

    Publish release 2.0?

    There are a bunch of new features in 2.0 - especially androidx support. Can a final version of 2.0 please be released?

    I know that rc has been available for a long time, but a final build will be appreciated.

    opened by ghost 12
  • Add newer implementation of gradle-wrapper

    Add newer implementation of gradle-wrapper

    The gradlewrapper.jar is rather old (4.3.1).

    I updated it to a newer version (4.10.2). Even tough the wrapper is newer, the used gradle-version for building is determined by gradle.properties

    This shouldn't add new problems, I suppose

    opened by beatbrot 12
  • Rules around comment parsing are unclear

    Rules around comment parsing are unclear

    The following struct fails to parse:

    struct SomeRequest {
        /** Here's a comment. */
        1: required UUID clientUuid
    
        /**
         * Here's a longer comment.
         * One two lines.
         */
        2: optional string someOtherField
    
        /**
         * Here's another longer comment.
         * Also on two lines.
         */
        3: optional UUID someField
    
        /** Here's another single line that works fine. */
        4: optional UUID adminUuid
    }
    

    This fails with the following error:

    trailing comment must be closed on the same line
    

    Could you explain where the issue is in these comments? It seems like the formatting is fine, and the error message isn't really clear since I see several tests with multiline comments like this.

    opened by ZacSweers 12
  • Deprecated Gradle APIs in use, will break in Gradle 8

    Deprecated Gradle APIs in use, will break in Gradle 8

    They all appear to be from Dokka, as documented in this issue from 2021.

    This issue is just a reminder to come back and look at this in advance of Gradle 8.

    opened by benjamin-bader 1
  • Add http transport

    Add http transport

    addresses https://github.com/microsoft/thrifty/issues/370

    Implementation based on the original thrift http transport with a few changes:

    • apache http client variant removed
      • it was weird to have multiple different implementations in the same class
      • I did not want to introduce an additional dependency
      • okio is already a dependency, so maybe we want to base the transport on that?
    • tried to make the code a bit more kotlin idiomatic and safer

    Even though there are no tests yet I wanted to collect some early feedback, especially on okio vs. HTTPUrlConnection. This needs to live in the jvm variant due to the availability of HTTPUrlConnection.

    opened by luqasn 2
  • Investigate fuzz testing with Jazzer

    Investigate fuzz testing with Jazzer

    This newly-open-sourced JVM fuzz tester looks really interesting for testing the thrifty-schema loader: https://github.com/CodeIntelligenceTesting/jazzer

    The challenge with fuzz testing is providing correct random mutations; for our purposes we'd need to generate known-good thrift and mutate it in syntactically-valid ways. Definitely a non-trivial project, but it could be a fun and moderately valuable way to improve the parser and linker.

    opened by benjamin-bader 0
  • Add Android support to Gradle plugin

    Add Android support to Gradle plugin

    Sadly, I forgot to test the gradle plugin on actual Android projects, and (surprise!) it doesn't work.

    We need to hook in to the various Android variant collections and register a thrifty task for each.

    bug 
    opened by benjamin-bader 0
  • Proposal: Add UserType to process method of TypeProcessor for more flexible code generation

    Proposal: Add UserType to process method of TypeProcessor for more flexible code generation

    I'm writing a TypeProcessor that adds a method in generated code for a thrift union type. Since the processor only gets the generated class as input parameter, it's rather hard to figure out if the generated Thrifty code reflects a union in thrift.

    I came up with the following which seems rather fragile to me:

    boolean isUnion = type.typeSpecs.stream()
            .filter(e -> "Builder".equals(e.name))
            .findFirst()
            .flatMap(t -> t.methodSpecs.stream().filter(m -> "build".equals(m.name)).findFirst())
            .map(m -> m.code.toString().contains("Invalid union;"))
            .orElse(false);
    

    Would it be a good idea to add the userType as a second argument in the process method? It would make code generation in general a lot easier since generated code can be based upon the thrift schema AST instead of the generated code.

    I can draft a PR if this seems a good idea. Changing the method will break the TypeProcessor api.

    opened by timvlaer 2
Releases(3.1.0)
  • 3.1.0(Dec 13, 2022)

    3.1.0 (released 13 December 2022)

    • Add: Support default field values in struct-typed constants (#507)
    • Add: Implement Java codegen for struct-typed constants (#503)
    • Add: Sort constants in Schema by "dependency order" (#502)
    • Add: Added Constant#referencedConstants list (#499)
    • Add: new Gradle plugin support for TypeProcessors (#494)
    • Change: Bump Okio to 3.2.0 (#510)
    • Change: Bump Kotlin to 1.7.20 (#495)
    • Fix: avoid afterEvaluate in thrifty-gradle-plugin (#492)

    3.1.0-RC02 (released 7 June 2022)

    • Add preliminary support for TypeProcessor plugins to thrifty-gradle-plugin (#490)
    • Add "generated" comments to Kotlin files (thanks @shashachu) (#486)
    • Add Kotlin codegen for struct-value constants (#482)
    • Maybe break? Removed deprecated @JvmDefault annotations from thrifty-runtime (#481)

    3.1.0-RC01 (released 13 April 2022)

    • Add struct-valued constant validator to thirfty-schema (thanks @janarajan) (#467)
    • Add server support (thanks @luqasn) (#447)
    • Bump Kotlin to 1.6.20
    Source code(tar.gz)
    Source code(zip)
  • 3.1.0-RC02(Jun 9, 2022)

    3.1.0-RC02 (released 7 June 2022)

    • Add preliminary support for TypeProcessor plugins to thrifty-gradle-plugin (#490)
    • Add "generated" comments to Kotlin files (thanks @shashachu) (#486)
    • Add Kotlin codegen for struct-value constants (#482)
    • Maybe break? Removed deprecated @JvmDefault annotations from thrifty-runtime (#481)

    3.1.0-RC01 (released 13 April 2022)

    • Add struct-valued constant validator to thirfty-schema (thanks @janarajan) (#467)
    • Add server support (thanks @luqasn) (#447)
    • Bump Kotlin to 1.6.20
    Source code(tar.gz)
    Source code(zip)
  • 3.0.0(Aug 8, 2021)

    The major update in this release is the transition of Thrifty from a Java project to a Kotlin Multiplatform project. Among other changes, one thing to note is that the runtime artifact thrifty-runtime now refers to an MPP artifact. The correct artifact to reference in your existing Android or Java projects is thrifty-runtime-jvm.

    • BREAK: All support for @Generated annotations has been removed (#402)
    • BREAK: thrifty-runtime ported to Kotlin Multiplatform (#401)
    • BREAK: thrifty-runtime-ktx is gone, and has been merged into thrifty-runtime (#397)
    • BREAK: thrifty-runtime ported to Kotlin (#391)
    • BREAK: Minimum supported JDK is now version 8 (#391)
    • BREAK: Fields whose names are "soft" or "modifier" Kotlin keywords now have an underscore suffix (thanks @luqasn) (#446)
    • Add Okio-based convenience APIs to thrifty-runtime (#408)
    • Add big-enum mode to enable enums with large numbers of members (Thanks @shashachu) (#421)
    • Change: Kotlin structs are builderless by default (#414)
    • Change: Gradle plugin defaults to Kotlin (#442)
    • Change: thrifty-compiler defaults to generating Kotlin (#451)
    • Fix: Empty structs use literal class name for hashCode (#415)
    • Fix: Location in thrifty-schema should always be an include root (#416)
    • Fix: Make @JvmStatic annotations opt-in (#417)
    • Fix: Including sibling .thrift files now works (#434)
    • Fix: Unions with fields named error (thanks @luqasn) (#444)
    Source code(tar.gz)
    Source code(zip)
  • 3.0.0-RC01(Jun 28, 2021)

  • 1.0.0(Nov 2, 2018)

    ...finally!

    Not much has changed since RC2; a few "quality-of-life" improvements contributed by @jparise, and a new compiler option to specify the type of @Generated annotation added to generated types (the usual javax.annotation.Generated type no longer exists in JDK 9+).

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-RC1(Aug 16, 2018)

    This release sees most of the compiler rewritten in Kotlin, and the addition of Kotlin codegen. A host of bugfixes and smaller features accompanies.

    As the version number implies, this is an API-breaking update. Users of existing generated code shouldn't see any issues, but those building against the compiler's APIs (e.g. thrifty-schema) will find that they are broken.

    BREAK:

    • thrifty-schema extensively reworked into idiomatic Kotlin.
    • ThriftException is now unchecked, and generated Adapters do not declare that they throw it.
    • Deprecated Loader methods accepting java.io.File are removed.

    Add:

    • Kotlin codegen! Including both callback- and coroutine-based RPC clients.
    • Java builders now have nullability annotations
    • Java union fields are now @Nullable

    Fix:

    • FramedTransport now works properly for reads spanning more than one frame
    • ClientBase now properly reads messageEnd
    • Validation of double consts is fixed (thanks, @jparise)
    • Codegen for map-of-enums fields is fixed (thanks, @jparise)
    • Include-path scanning fixed when no individual .thrift files are given (thanks, @hzsweers)
    Source code(tar.gz)
    Source code(zip)
  • 0.4.3(Jan 8, 2018)

    0.4.3 (released 8 January 2018)

    • #156: Add JSON protocol support
    • #153: Fix reading bool value in CompactProtocol
    • #152: Add UUID for distinguishing parsed elements from those modified via newBuilder
    • #147: Enable synchronous service calls
    Source code(tar.gz)
    Source code(zip)
Owner
Microsoft
Open source projects and samples from Microsoft
Microsoft
Generate helper methods for compose navigation using KSP

Compose NavGen Generate helper methods for compose navigation using KSP. ?? You can try it now, but it's still under development. ?? TODO Support defa

Kenji Abe 6 Feb 5, 2022
Small Android library to help you incorporate MVP, Passive View and Presentation Model patterns in your app

DroidMVP About DroidMVP is a small Android library to help you incorporate the MVP pattern along with Passive View and Presentation Model (yes, those

Andrzej Chmielewski 225 Nov 29, 2022
adds an option to the Android Sharesheet that allows you to save files to your device.

Save On Device adds an option to the Android Sharesheet that allows you to save files to your device. Download Get the app from the Google Play Store

null 24 Nov 29, 2022
A small library which will save you from writing the same intent creation code again and again for the most simple tasks

Android Intents A small library which will save you from writing the same intent creation code again and again for the most simple tasks. I found myse

MarvinLabs 420 Nov 20, 2022
An easy-to-use, cross-platform measurement tool that pulls data out of CD pipelines and analysis the four key metrics for you.

Maintained by SEA team, ThoughtWorks Inc. Read this in other languages: English, 简体中文 Table of Contents About the Project Usage How to Compute Contrib

Thoughtworks 277 Jan 7, 2023
Utility tool to make you a computer ninja.

Cmd Window Never spend 6 minutes doing something by hand when you can spend 6 hours failing to automate it - Zhuowej Zhang What is this about? This to

Marcin Radoszewski 3 Feb 1, 2022
compaKTset is a small library aimed at providing you with the most memory efficient Set implementation for any particular data type of your choosing.

compaKTset is a small library aimed at providing you with the most memory efficient Set implementation for any particular data type of your choosing.

Ignat Beresnev 3 Nov 16, 2021
Tell you how to manage your blackhorse

Tell you how to manage your blackhorse

Andong LIAO 19 Nov 9, 2022
Recover deleted messages for whatsapp and Facebook lets you recover messages that sender deleted

Recover deleted messages for whatsapp and Facebook lets you recover messages that sender deleted. It also recover deleted media Images, Audio, Video etc

S M Khurram Khan 6 Jul 17, 2022
A command line utility to help you investigate the sensitive data associated with Macie findings.

Macie Finding Data Reveal This project contains a command line utility to help you investigate the sensitive data associated with Macie findings.

AWS Samples 8 Nov 16, 2022
Android Shared preference wrapper than encrypts the values of Shared Preferences. It's not bullet proof security but rather a quick win for incrementally making your android app more secure.

Secure-preferences - Deprecated Please use EncryptedSharedPreferences from androidx.security in preferenced to secure-preference. (There are no active

Scott Alexander-Bown 1.5k Dec 24, 2022
Android library which makes it easy to handle the different obstacles while calling an API (Web Service) in Android App.

API Calling Flow API Calling Flow is a Android library which can help you to simplify handling different conditions while calling an API (Web Service)

Rohit Surwase 19 Nov 9, 2021
Gesture detector framework for multitouch handling on Android, based on Android's ScaleGestureDetector

Android Gesture Detectors Framework Introduction Since I was amazed Android has a ScaleGestureDetector since API level 8 but (still) no such thing as

null 1.1k Nov 30, 2022
Use Android as Rubber Ducky against another Android device

Use Android as Rubber Ducky against another Android device

null 1.4k Jan 9, 2023
Android Utilities Library build in kotlin Provide user 100 of pre defined method to create advanced native android app.

Android Utilities Library build in kotlin Provide user 100 of pre defined method to create advanced native android app.

Shahid Iqbal 4 Nov 29, 2022
A util for setting status bar style on Android App.

StatusBarUtil A util for setting status bar style on Android App. It can work above API 19(KitKat 4.4). 中文版点我 Sample Download StatusBarUtil-Demo Chang

Jaeger 8.8k Jan 6, 2023
A logger with a small, extensible API which provides utility on top of Android's normal Log class.

This is a logger with a small, extensible API which provides utility on top of Android's normal Log class. I copy this class into all the little apps

Jake Wharton 9.8k Dec 30, 2022
Java implementation of a Disk-based LRU cache which specifically targets Android compatibility.

Disk LRU Cache A cache that uses a bounded amount of space on a filesystem. Each cache entry has a string key and a fixed number of values. Each key m

Jake Wharton 5.7k Dec 31, 2022
a simple cache for android and java

ASimpleCache ASimpleCache 是一个为android制定的 轻量级的 开源缓存框架。轻量到只有一个java文件(由十几个类精简而来)。 1、它可以缓存什么东西? 普通的字符串、JsonObject、JsonArray、Bitmap、Drawable、序列化的java对象,和 b

Michael Yang 3.7k Dec 14, 2022