/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ include "thrift/annotation/scope.thrift" package "facebook.com/thrift/annotation/rust" namespace java com.facebook.thrift.annotation.rust_deprecated namespace android com.facebook.thrift.annotation.rust_deprecated namespace py.asyncio facebook_thrift_asyncio.annotation.rust namespace go thrift.annotation.rust namespace py thrift.annotation.rust @scope.Function @scope.EnumValue @scope.Enum @scope.Struct @scope.Field @scope.Typedef @scope.Union @scope.Service struct Name { // # `rust.Name` // // Override the Thrift identifier of the entity with the given name. 1: string name; } @scope.Struct struct Copy { // # `rust.Copy` // // Mark the generated struct `#[derive(Copy)]`. } @scope.Service struct RequestContext { // # `rust.RequestContext` // // Opt-in to request contexts for your service (which can be used to ensure that // only authorized clients can access your service by identity checks against // Hipster ACLs). } @scope.Field struct Arc { // # `rust.Arc` // // Indicates that a field's value should be shared. } @scope.Field struct Box { // # `rust.Box` // // Indicates that a field's value should not be stored on the stack. } @scope.Struct struct Exhaustive { // # `rust.Exhaustive` // // Generated struct definitions generally include an additional member to // force use of `..Default::default()` when initalizing instances. Use this // annotation to prevent this. } @scope.Struct @scope.Typedef struct Ord { // # `rust.Ord` // // Require that the type being defined derive `Ord`. } @scope.Typedef struct NewType { // # `rust.NewType` // // Make a newtype from a typedef. For example, // ``` // @rust.NewType // typedef binary Sha1 // ``` // will result in `pub struct Sha1(pub std::vec::Vec)`. // // Another common idiom is to use `rust.Type` and `rust.NewType` together like // this: // ``` // @rust.NewType // @rust.Type { name = "smallvec::SmallVec<[u8; 20]>" } // typedef binary Sha1 // ``` // in this case we'll get `pub struct Sha1(smallvec::SmallVec[u8; 20])`. } @scope.Field @scope.Typedef struct Type { // # `rust.Type` // // There is a default Rust type associated with each Thrift type. For // example, the default Rust type to represent Thrift `map<>`s is // `std::collections::BTreeMap<>`. // // The `rust.Type` annotation provides an ability to "override" // (substitute) a non-default Rust type in certain circumstances (full // details below). We might say for example, `@rust.Type{name="HashMap"}` // to override an instance of a specific Thrift `map<>`. // // The `rust.Type` annotation can be applied to any type but has no // effect when applied to `string`, `list<>`, `struct` or `enum` types. // // The `name` argument of a `rust.Type` annotation may specify a // "standard" or "nonstandard" type: a name containing a '`::`' is // classified as a nonstandard type whereas, a name without a '`::`' is // classified as standard. // // Standard types that may appear in `@rust.Type` annotations are exactly // types that are (re-)exported from the `fbthrift::builtin_types` // module. For such types, the `fbthift` package provides stock // `fbthrift::Serialize<>` and `fbthrift::Deserialize<>` instances for // them. At the current time the full set of such types is // `std::collections::*`, `bytes::Bytes` and // `ordered_float::OrderedFloat`. // // This is an example of an application of a `@rust.Type` annotation with a // standard type: // ``` // @rust.Type { name = "OrderdedFloat" } // typedef double Double // struct T { 1: Double data; } // `data : fbthrift::builtin_types::OrderedFloat` // ``` // // This is an example of application of a `@rust.Type` annotation with a // nonstandard type: // ``` // @rust.Type { name = "smallvec::SmallVec<[u8; 32]>" } // typedef binary binary_t // struct T { 1: binary_t data; } // `data : smallvec::SmallVec<[u8; 32]>` // ``` // // Nonstandard types, when they appear in `@rust.Type` annotations // applied to Thrift `map<>`, `set<>` or `binary` types will result in // the generation of `fbthrift::Serialize<>` and // `fbthrift::Deserialize<>` instances for those types. The serialization // code makes assumptions about valid expressions and the existence of // trait implementations for such types that are documented below. // // A nonstandard type say can also be applied to `i64`. In this case, the // resulting generated serialization code assumes the existence of // `fbthrift::Serialize<>` and `fbthrift::Deserialize<>` for that // nonstandard type. // // A nonstandard type applied to Thrift `void`, `bool`, `float`, `byte`, // `i16`, `i32`, `double`, and `float` types will not result in the // generation of any serialization code for the nonstandard type // (rendering nonstandard types applied to these types effectively // unsupported at this time). // // "Codegen" errors or bugs resulting from the use of standard types in // valid positions in `@rust.Type` annotations should be considered the // responsibility of the the Rust Thrift maintainers to address. Less // "formal" support should be expected from the Rust Thrift maintainers // when nonstandard types are involved. // // ## `binary` // // The default Rust type for a Thrift `binary` is // `std::vec::Vec`. An example override: // ``` // @rust.Type{name = "smallvec::SmallVec<[u8; 32]>"} // typedef binary binary_t // struct T { // 1: binary_t data; // } // ``` // // If nonstandard `B` models Thrift `binary`, `b : B`, `other: &[u8]` and // `vec : std::vec::Vec` then the following expressions are required // to be valid and the following trait instances must exist: // // | expression | // | :---------------------------------------------------- | // | `let _: &[u8] = b.as_slice()` | // | `let _: B = ::with_capacity(l)` | // | `b.extend_from_slice(other)` | // // | type | traits | // | :------------- | :---------------------------------- | // | `B` | `Debug`, `Default`, | // | | `From>` | // // ## `set` // // The default Rust type for a thrift `set` is // `std::collections::BTreeSet<>`. An example override: // ``` // @rust.Type{name = "sorted_vector_map::SortedVectorSet"} // typedef set set_t // struct T { // 1: set_t data; // data : sorted_vector_map::SortedVectorSet // } // ``` // // If nonstandard `S` models thrift `set`, `K` is the Rust element type, // `k : K`, `l : usize`, `s : S` and `'a` a lifetime, required valid // expressions and trait implementations are as follows: // // | expression | // | :---------------------------------------------------- | // | `for _ in &s { ... }` | // | `let _: usize = s.len()` | // | `let mut _: T = >::with_capacity(l);` | // | `s.insert(k)` | // // | type | traits | // | :------------- | :---------------------------------- | // | `S` | `Debug`, `Default` | // | `&'a S` | `IntoIterator` | // // ## `map` // // The default rust type for a thrift `map` is // `std::collections::BTreeMap<>`. An example override: // ``` // @rust.Type{name = "sorted_vector_map::SortedVectorMap"} // typedef map map_t // struct T { // 1: map_t data; // data: sorted_vector_map::SortedVectorMap // } // ``` // // If nonstandard `T` models thrift `map`, `K` and `V` are the Rust map // key and value types respectively, `k : K`, `v : V`, `l : usize`, `m : // T` and `'a` a lifetime, required valid expressions and trait // implementations are: // // | expression | // | :---------------------------------------------------- | // | `for (key, val) in &m { ... }` | // | `let _: usize = m.len()` | // | `let mut _: T = >::with_capacity(l);` | // | `m.insert(k, v)` | // // | type | traits // | :------------- | :---------------------------------- // | `T` | `Debug`, `Default` // | `&'a T` | `IntoIterator` 1: string name; } // # `rust.Serde` // // Selectively enable/disable derivation of serde serialization functions for a // given type. @scope.Struct struct Serde { 1: bool enabled; } // # `rust.Mod` // // Use this annotation to explicitly set a non-default name for the module that // contains the services exceptions and error types. @scope.Service struct Mod { 1: string name; } @scope.Field @scope.Typedef @scope.Struct struct Adapter { // A fully qualified path to a struct that implements `fbthrift::adapter::ThriftTypeAdapter`. // // This will transform the type of this field to that struct's `AdaptedType`, and the corresponding // `ThriftTypeAdapter` methods will be called in the serialization and deserialization paths. // // Example: // If you have a Thrift struct like: // ``` // struct Foo { // @rust.Adapter{ // name = "fbthrift_adapters::DurationSecondsAdapter" // } // 1: i64 duration_secs; // } // ``` // // The generated Rust struct's `duration_secs` field will be set to the type // `::AdaptedType` // (which is `std::time::Duration`) and marshalling the `i64` to and from `Duration` will be handled // in the serialization/deserialization path with the methods defined on the `ThriftTypeAdapter` impl. // // The name provided here must be a valid Rust path, i.e. the `fbthrift_adapters` crate must be added // to the Thrift library's `rust_deps`. Alternatively, you define the adapter in a file added to the // Thrift library's `rust_include_srcs`, and use the `crate::` prefix in your adapter name. // // If `<>` is present at the end of the name, we will treat the name as a generic and fill it in with // the original unadapted type. // For example: // ``` // struct Foo { // @rust.Adapter{ // name = "fbthrift_adapters::DurationSecondsAdapter<>" // } // 1: i64 duration_secs; // } // ``` // will use `fbthrift_adapters::DurationSecondsAdapter` as the adapter. // // If the adapter name starts with `crate::` and this `@rust.Adapter` is applied transitively with // @scope.Transitive, `crate::` will be replaced with the name of the crate in which the // transitive annotation is defined. 1: string name; } @scope.Enum @scope.Struct struct Derive { // List of additional derives to apply to the generated struct. // // Example: // ``` // @rust.Derive{ derives = ["Foo"] } // struct SomeStruct { // 1: string some_field; // } // ``` // will generated the Rust struct // ``` // #[derive(Foo)] // struct SomeStruct { // some_field: String, // } // ``` // // If the derive starts with `crate::` and this `@rust.Derive` is applied transitively with // @scope.Transitive, `crate::` will be replaced with the name of the crate in which the // transitive annotation is defined. 1: list derives; } @scope.Function @scope.Service struct ServiceExn { // If `true`, this allows returning an `anyhow::Error` from within a Thrift server handler method, // and that `anyhow::Error` will be turned into an `ApplicationException`. This is similar in behavior // to what happens if you throw an unhandled exception type in Python Thrift server or C++ Thrift server. // // The `ApplicationException` returned will have the error code `Unknown` and message // `format!("{:#}", anyhow_err)`. // // NOTE: it is generally considered bad practice to use this because it eliminates the ability // to match on specific error types on the client side. When possible, it is recommended you // always return structured error types (though it is more verbose). This annotation is provided // solely for convenience and should not be used services where error type matching is needed. // // Example, the following Thrift: // ``` // service Foo { // @rust.ServiceExn{ anyhow_to_application_exn = true } // void bar(); // } // ``` // would allow for the following Rust server impl: // ``` // #[async_trait] // impl Foo for FooServerImpl { // async fn bar() -> Result<(), BarExn> { // if some_condition { // Err(anyhow::anyhow!("some error"))? // } // // // Note you must always convert to `anyhow::Error` first for non-`anyhow::Error` // // types. // some_client_call.await.context("failed some_client_call")?; // // // you can still return a structured exn type if desired // return Err(BarExn::ie(...)); // } // } // ``` // // You can also use this annotation on the service definition itself to have it apply to all // methods on the service, e.g. // ``` // @rust.ServiceExn{ anyhow_to_application_exn = true } // service Foo { // // Both `bar` and `baz` will support `anyhow::Error` -> `ApplicationException`. // void bar(); // void baz(); // } // ``` 1: bool anyhow_to_application_exn; }