Rust рдореЗрдВ рдкреНрд░рдХреНрд░рд┐рдпрд╛рддреНрдордХ рдореИрдХреНрд░реЛрдЬрд╝ рдПрдХ рдмрд╣реБрдд рд╢рдХреНрддрд┐рд╢рд╛рд▓реА рдХреЛрдб рдЬрдирд░реЗрд╢рди рдЯреВрд▓ рд╣реИ рдЬреЛ рдЖрдкрдХреЛ рдмреЙрдпрд▓рд░рдкреНрд▓реЗрдЯ рдХреЛрдб рдХреЗ рдПрдХ рдЯрди рдХреЛ рд▓рд┐рдЦреЗ рдмрд┐рдирд╛ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ, рдпрд╛ рдХреБрдЫ рдирдИ рдЕрд╡рдзрд╛рд░рдгрд╛рдУрдВ рдХреЛ рд╡реНрдпрдХреНрдд рдХрд░рддрд╛ рд╣реИ, рдЬреИрд╕рд╛ рдХрд┐ рдЯреЛрдХрд░рд╛ рдХреЗ рдбреЗрд╡рд▓рдкрд░реНрд╕ рдиреЗ рдХрд┐рдпрд╛ рдерд╛, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП async_trait
ред
рдлрд┐рд░ рднреА, рдХрдИ рд▓реЛрдЧ рдЗрд╕ рдЙрдкрдХрд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдЪрд┐рдд рд░реВрдк рд╕реЗ рдбрд░рддреЗ рд╣реИрдВ, рдореБрдЦреНрдп рд░реВрдк рд╕реЗ рдЗрд╕ рддрдереНрдп рдХреЗ рдХрд╛рд░рдг рдХрд┐ рд╡рд╛рдХреНрдпрд╡рд┐рдиреНрдпрд╛рд╕ рдХреЗ рдкреЗрдбрд╝ рдФрд░ рд╕реНрдереВрд▓ рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рдХреЛ рдкрд╛рд░реНрд╕ рдХрд░рдирд╛ рдЕрдХреНрд╕рд░ "рд╕реВрд░реНрдпрд╛рд╕реНрдд рдореИрдиреНрдпреБрдЕрд▓" рдореЗрдВ рдмрджрд▓ рдЬрд╛рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╕рдорд╕реНрдпрд╛ рдХреЛ рдмрд╣реБрдд рдХрдо рд╕реНрддрд░ рдкрд░ рд╣рд▓ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рд╣реИред
рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рдореИрдВ рдХреБрдЫ рд╕рд╛рдЭрд╛ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ, рдореЗрд░реА рд░рд╛рдп рдореЗрдВ, рдкреНрд░рдХреНрд░рд┐рдпрд╛рддреНрдордХ рдореИрдХреНрд░реЛрдЬрд╝ рд▓рд┐рдЦрдиреЗ рдореЗрдВ рд╕рдлрд▓ рджреГрд╖реНрдЯрд┐рдХреЛрдг, рдФрд░ рджрд┐рдЦрд╛рддреЗ рд╣реИрдВ рдХрд┐ рдЖрдЬ рдкреНрд░рдХреНрд░рд┐рдпрд╛рддреНрдордХ рдореИрдХреНрд░реЛрдЬрд╝ рдХреЛ рдЕрдкреЗрдХреНрд╖рд╛рдХреГрдд рд╕рд░рд▓ рдФрд░ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд░реВрдк рд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред
рдкреНрд░рд╕реНрддрд╛рд╡рдирд╛
рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╣рдо рдЙрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддреЗ рд╣реИрдВ рдЬрд┐рд╕реЗ рд╣рдо рдореИрдХреНрд░реЛрдЬрд╝ рдХреА рдорджрдж рд╕реЗ рд╣рд▓ рдХрд░реЗрдВрдЧреЗ: рд╣рдо рдХреБрдЫ рд╕рд╛рд░ RPC API рдХреЛ рдПрдХ рд╡рд┐рд╢реЗрд╖рддрд╛ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВрдЧреЗ , рдЬреЛ рддрдм рд╕рд░реНрд╡рд░ рднрд╛рдЧ рдФрд░ рдХреНрд▓рд╛рдЗрдВрдЯ рднрд╛рдЧ рджреЛрдиреЛрдВ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реИ; рдФрд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛рддреНрдордХ рдореИрдХреНрд░реЛрдЬрд╝, рдмрджрд▓реЗ рдореЗрдВ, рдмреЙрдпрд▓рд░рдкреНрд▓реЗрдЯ рдХреЛрдб рдХреЗ рдПрдХ рдЧреБрдЪреНрдЫрд╛ рдХреЗ рдмрд┐рдирд╛ рд╣рдореЗрдВ рдХрд░рдиреЗ рдореЗрдВ рдорджрдж рдХрд░реЗрдВрдЧреЗред рдЗрд╕ рддрдереНрдп рдХреЗ рдмрд╛рд╡рдЬреВрдж рдХрд┐ рд╣рдо рдХреБрдЫ рд╣рдж рддрдХ рдЕрдореВрд░реНрдд рдПрдкреАрдЖрдИ рдХреЛ рд▓рд╛рдЧреВ рдХрд░реЗрдВрдЧреЗ, рдХрд╛рд░реНрдп рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХрд╛рдлреА рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ, рдФрд░, рдЕрдиреНрдп рдмрд╛рддреЛрдВ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдкреНрд░рдХреНрд░рд┐рдпрд╛рддреНрдордХ рдореИрдХреНрд░реЛ рдХреА рдХреНрд╖рдорддрд╛рдУрдВ рдХрд╛ рдкреНрд░рджрд░реНрд╢рди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрджрд░реНрд╢ рд╣реИред
рдПрдкреАрдЖрдИ рдХреЛ рдПрдХ рдмрд╣реБрдд рд╣реА рд╕рд░рд▓ рд╕рд┐рджреНрдзрд╛рдВрдд рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛: 4 рдкреНрд░рдХрд╛рд░ рдХреЗ рдЕрдиреБрд░реЛрдз рд╣реИрдВ:
- рдХреЛрдИ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЗ рд╕рд╛рде рдЕрдиреБрд░реЛрдз рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП
/ping
:ред - рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рд╕рд╛рде рдЕрдиреБрд░реЛрдз рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ, рдЬрд┐рдирдореЗрдВ рд╕реЗ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЛ URL рдХреНрд╡реЗрд░реА рдХреЗ рд░реВрдк рдореЗрдВ рдкреНрд░рд╕рд╛рд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП
/status?name=foo&count=15
:ред - рдорд╛рдкрджрдВрдбреЛрдВ рдХреЗ рдмрд┐рдирд╛ POST рдЕрдиреБрд░реЛрдзред
- JSON рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд╛рд░рд┐рдд рд╣реЛрдиреЗ рд╡рд╛рд▓реЗ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЗ рд╕рд╛рде POST рдЕрдиреБрд░реЛрдзред
рд╕рднреА рдорд╛рдорд▓реЛрдВ рдореЗрдВ, рд╕рд░реНрд╡рд░ рдПрдХ рд╡реИрдз JSON рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рд╕рд╛рде рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХрд░реЗрдЧрд╛ред
рд╕рд░реНрд╡рд░ рдмреИрдХрдПрдВрдб рдХреЗ рд░реВрдк рдореЗрдВ, рд╣рдо рдЯреЛрдХрд░рд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗ warp
ред
рдЖрджрд░реНрд╢ рд░реВрдк рд╕реЗ, рдореИрдВ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ:
#[derive(Debug, FromUrlQuery, Deserialize, Serialize)]
struct Query {
first: String,
second: u64,
}
#[http_api(warp = "serve_ping_interface")]
trait PingInterface {
#[http_api_endpoint(method = "get")]
fn get(&self) -> Result<Query, Error>;
#[http_api_endpoint(method = "get")]
fn check(&self, query: Query) -> Result<bool, Error>;
#[http_api_endpoint(method = "post")]
fn set_value(&self, param: Query) -> Result<(), Error>;
#[http_api_endpoint(method = "post")]
fn increment(&self) -> Result<(), Error>;
}
#[derive(Debug, Default)]
struct ServiceInner {
first: String,
second: u64,
}
#[derive(Clone, Default)]
struct ServiceImpl(Arc<RwLock<ServiceInner>>);
impl ServiceImpl {
fn new() -> Self {
Self::default()
}
fn read(&self) -> RwLockReadGuard<ServiceInner> {
self.0.read().unwrap()
}
fn write(&self) -> RwLockWriteGuard<ServiceInner> {
self.0.write().unwrap()
}
}
impl PingInterface for ServiceImpl {
fn get(&self) -> Result<Query, Error> {
let inner = self.read();
Ok(Query {
first: inner.first.clone(),
second: inner.second,
})
}
fn check(&self, query: Query) -> Result<bool, Error> {
let inner = self.read();
Ok(inner.first == query.first && inner.second == query.second)
}
fn set_value(&self, param: Query) -> Result<(), Error> {
let mut inner = self.write();
inner.first = param.first;
inner.second = param.second;
Ok(())
}
fn increment(&self) -> Result<(), Error> {
self.write().second += 1;
Ok(())
}
}
#[tokio::main]
async fn main() {
let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
serve_ping_interface(ServiceImpl::new(), addr).await
}
, Rust' , , , .
: derive-, - ( serde
), , .
, : http_api
, , http_api_derive
.
FromUrlQuery
, тАФ , , . , - , .
, . URL query. , . :
pub trait FromUrlQuery: Sized {
fn from_query_str(query: &str) -> Result<Self, ParseQueryError>;
}
, . derive :
#[proc_macro_derive(FromUrlQuery, attributes(from_url_query))]
pub fn from_url_query(input: TokenStream) -> TokenStream {
from_url_query::impl_from_url_query(input)
}
тАФ syn
, quote
. Rust
, , .
quote!
, Rust , .
, , , darling
. ( , ).
, ** **. :AST , , .
fn get_field_names(input: &DeriveInput) -> Option<Vec<(Ident, Action)>> {
let data = match &input.data {
Data::Struct(x) => Some(x),
Data::Enum(..) => None,
_ => panic!("Protobuf convert can be derived for structs and enums only."),
};
data.map(|data| {
data.fields
.iter()
.map(|f| {
let mut action = Action::Convert;
for attr in &f.attrs {
match attr.parse_meta() {
Ok(syn::Meta::List(ref meta)) if meta.ident == "protobuf_convert" => {
for nested in &meta.nested {
match nested {
syn::NestedMeta::Meta(syn::Meta::Word(ident))
if ident == "skip" =>
{
action = Action::Skip;
}
_ => {
panic!("Unknown attribute");
}
}
}
}
_ => {
}
}
}
(f.ident.clone().unwrap(), action)
})
.collect()
})
}
fn get_field_names_enum(input: &DeriveInput) -> Option<Vec<Ident>> {
let data = match &input.data {
Data::Struct(..) => None,
Data::Enum(x) => Some(x),
_ => panic!("Protobuf convert can be derived for structs and enums only."),
};
data.map(|data| data.variants.iter().map(|f| f.ident.clone()).collect())
}
fn implement_protobuf_convert_from_pb(field_names: &[(Ident, Action)]) -> impl quote::ToTokens {
let mut to_convert = vec![];
let mut to_skip = vec![];
for (x, a) in field_names {
match a {
Action::Convert => to_convert.push(x),
Action::Skip => to_skip.push(x),
}
}
let getters = to_convert
.iter()
.map(|i| Ident::new(&format!("get_{}", i), Span::call_site()));
let our_struct_names = to_convert.clone();
let our_struct_names_skip = to_skip;
quote! {
fn from_pb(pb: Self::ProtoStruct) -> std::result::Result<Self, _FailureError> {
Ok(Self {
#( #our_struct_names: ProtobufConvert::from_pb(pb.#getters().to_owned())?, )*
#( #our_struct_names_skip: Default::default(), )*
})
}
}
}
darling
, .
, FromUrlQuery
, , . , - :
#[derive(FromUrlQuery)]
struct OptionalQuery {
first: String,
opt_value: Option<u64>,
}
darling
' , .
тАФ FromField
, :
#[derive(Clone, Debug, FromField)]
struct QueryField {
ident: Option<syn::Ident>,
ty: syn::Type,
}
, , , :
#[derive(Clone, Debug, FromField)]
struct QueryField {
ident: Option<syn::Ident>,
ty: syn::Type,
vis: syn::Visibility,
}
тАФ FromDeriveInput
, :
#[derive(Debug, FromDeriveInput)]
#[darling(supports(struct_named))]
struct FromUrlQuery {
ident: syn::Ident,
data: darling::ast::Data<(), QueryField>,
}
, .
let input: DeriveInput = syn::parse(input).unwrap();
let from_url_query = match FromUrlQuery::from_derive_input(&input) {
Ok(parsed) => parsed,
Err(e) => return e.write_errors().into(),
};
.
, URL query serde
. serde
, . Deserialize
, serde_urlencoded
. serde
, .
#[doc(hidden)]
pub mod export {
pub use serde;
pub use serde_derive;
pub use serde_urlencoded;
}
, FromUrlQuery
:
impl FromUrlQuery {
fn serde_wrapper_ident(&self) -> syn::Ident {
let ident_str = format!("{}Serde", self.ident);
syn::Ident::new(&ident_str, proc_macro2::Span::call_site())
}
fn impl_serde_wrapper(&self) -> impl ToTokens {
let fields = self.data.clone().take_struct().unwrap();
let wrapped_fields = fields.iter().map(|field| {
let ident = &field.ident;
let ty = &field.ty;
quote! { #ident: #ty }
});
let from_fields = fields.iter().map(|field| {
let ident = &field.ident;
quote! { #ident: v.#ident }
});
let wrapped_ident = self.serde_wrapper_ident();
let ident = &self.ident;
quote! {
use http_api::export::serde_derive::Deserialize;
#[derive(Deserialize)]
#[serde(crate = "http_api::export::serde")]
struct #wrapped_ident {
#( #wrapped_fields, )*
}
impl From<#wrapped_ident> for #ident {
fn from(v: #wrapped_ident) -> Self {
Self {
#( #from_fields, )*
}
}
}
}
}
}
, , , , , . , , ; , , .
http_api
FromDeriveInput
, darling
' , AST. , , :
:
#[proc_macro_attribute]
pub fn http_api(attr: TokenStream, item: TokenStream) -> TokenStream {
http_api::impl_http_api(attr, item)
}
, : , (, http_api_endpoint
), . , TokenStream
, "cannot find attribute http_api_endpoint
in this scope", . , , , . , , , .
, http_api_endpoint
, , .
#[proc_macro_attribute]
#[doc(hidden)]
pub fn http_api_endpoint(_attr: TokenStream, item: TokenStream) -> TokenStream {
item
}
, , .
, , :
#[http_api_endpoint(method = "#method_type")]
fn #method_name(&self) -> Result<$ResponseType, Error>;
#[http_api_endpoint(method = "#method_type")]
fn #method_name(&self, query: $QueryType) -> Result<$ResponseType, Error>;
HTTP , :
#[derive(Debug)]
enum SupportedHttpMethod {
Get,
Post,
}
impl FromMeta for SupportedHttpMethod {
fn from_string(value: &str) -> Result<Self, darling::Error> {
match value {
"get" => Ok(SupportedHttpMethod::Get),
"post" => Ok(SupportedHttpMethod::Post),
other => Err(darling::Error::unknown_value(other)),
}
}
}
, :
#[derive(Debug, FromMeta)]
struct EndpointAttrs {
method: SupportedHttpMethod,
#[darling(default)]
rename: Option<String>,
}
, syn::Signature
, darling' : , FromMeta
.
http_api_endpoint
. syn::NestedMeta
, (foo = "bar", boo(first, second))
.
fn find_meta_attrs(name: &str, args: &[syn::Attribute]) -> Option<syn::NestedMeta> {
args.as_ref()
.iter()
.filter_map(|a| a.parse_meta().ok())
.find(|m| m.path().is_ident(name))
.map(syn::NestedMeta::from)
}
. ,
тАФ :
fn invalid_method(span: &impl syn::spanned::Spanned) -> darling::Error {
darling::Error::custom(
"API method should have one of `fn foo(&self) -> Result<Bar, Error>` or \
`fn foo(&self, arg: Foo) -> Result<Bar, Error>` form",
)
.with_span(span)
}
impl ParsedEndpoint {
fn parse(sig: &syn::Signature, attrs: &[syn::Attribute]) -> Result<Self, darling::Error> {
let mut args = sig.inputs.iter();
if let Some(arg) = args.next() {
match arg {
syn::FnArg::Receiver(syn::Receiver {
reference: Some(_),
mutability: None,
..
}) => {
}
_ => {
return Err(invalid_method(&arg));
}
}
} else {
return Err(invalid_method(&sig));
}
let arg = args
.next()
.map(|arg| match arg {
syn::FnArg::Typed(arg) => Ok(arg.ty.clone()),
_ => unreachable!("Only first argument can be receiver."),
})
.transpose()?;
let ret = match &sig.output {
syn::ReturnType::Type(_, ty) => Ok(ty.clone()),
_ => Err(invalid_method(&sig)),
}?;
let attrs = find_meta_attrs("http_api_endpoint", attrs)
.map(|meta| EndpointAttrs::from_nested_meta(&meta))
.unwrap_or_else(|| Err(darling::Error::custom("todo")))?;
Ok(Self {
ident: sig.ident.clone(),
arg,
ret,
attrs,
})
}
}
. , , .
, :
#[derive(Debug)]
struct ParsedApiDefinition {
item_trait: syn::ItemTrait,
endpoints: Vec<ParsedEndpoint>,
attrs: ApiAttrs,
}
#[derive(Debug, FromMeta)]
struct ApiAttrs {
warp: syn::Ident,
}
impl ParsedApiDefinition {
fn parse(
item_trait: syn::ItemTrait,
attrs: &[syn::NestedMeta],
) -> Result<Self, darling::Error> {
let endpoints = item_trait
.items
.iter()
.filter_map(|item| {
if let syn::TraitItem::Method(method) = item {
Some(method)
} else {
None
}
})
.map(|method| ParsedEndpoint::parse(&method.sig, method.attrs.as_ref()))
.collect::<Result<Vec<_>, darling::Error>>()?;
let attrs = ApiAttrs::from_list(attrs)?;
Ok(Self {
item_trait,
endpoints,
attrs,
})
}
}
, warp
.
, , , warp
. warp' , Filter
. and
, map
, and_then
, , .
, , GET JSON, - :
pub fn simple_get<F, R, E>(name: &'static str, handler: F) -> JsonReply
where
F: Fn() -> Result<R, E> + Clone + Send + Sync + 'static,
R: ser::Serialize,
E: Reject,
{
warp::get()
.and(warp::path(name))
.and_then(move || {
let handler = handler.clone();
async move {
match handler() {
Ok(value) => Ok(warp::reply::json(&value)),
Err(e) => Err(warp::reject::custom(e)),
}
}
})
.boxed()
}
GET , , :
pub fn query_get<F, Q, R, E>(name: &'static str, handler: F) -> JsonReply
where
F: Fn(Q) -> Result<R, E> + Clone + Send + Sync + 'static,
Q: FromUrlQuery,
R: ser::Serialize,
E: Reject,
{
warp::get()
.and(warp::path(name))
.and(warp::filters::query::raw())
.and_then(move |raw_query: String| {
let handler = handler.clone();
async move {
let query = Q::from_query_str(&raw_query)
.map_err(|_| warp::reject::custom(IncorrectQuery))?;
match handler(query) {
Ok(value) => Ok(warp::reply::json(&value)),
Err(e) => Err(warp::reject::custom(e)),
}
}
})
.boxed()
}
.
and
, ,
or
, , ,
, .
:
use std::net::SocketAddr;
use warp::Filter;
warp::path::param::<u32>()
.or(warp::path::param::<SocketAddr>());
serve_ping_interface
. warp , service , -.
impl ParsedEndpoint {
fn impl_endpoint_handler(&self) -> impl ToTokens {
let path = self.endpoint_path();
let ident = &self.ident;
match (&self.attrs.method, &self.arg) {
(SupportedHttpMethod::Get, None) => {
quote! {
let #ident = http_api::warp_backend::simple_get(#path, {
let out = service.clone();
move || out.#ident()
});
}
}
(SupportedHttpMethod::Get, Some(_arg)) => {
quote! {
let #ident = http_api::warp_backend::query_get(#path, {
let out = service.clone();
move |query| out.#ident(query)
});
}
}
(SupportedHttpMethod::Post, None) => {
quote! {
let #ident = http_api::warp_backend::simple_post(#path, {
let out = service.clone();
move || out.#ident()
});
}
}
(SupportedHttpMethod::Post, Some(_arg)) => {
quote! {
let #ident = http_api::warp_backend::params_post(#path, {
let out = service.clone();
move |params| out.#ident(params)
});
}
}
}
}
}
or
.
impl ToTokens for ParsedApiDefinition {
fn to_tokens(&self, out: &mut proc_macro2::TokenStream) {
let fn_name = &self.attrs.warp;
let interface = &self.item_trait.ident;
let (filters, idents): (Vec<_>, Vec<_>) = self
.endpoints
.iter()
.map(|endpoint| {
let ident = &endpoint.ident;
let handler = endpoint.impl_endpoint_handler();
(handler, ident)
})
.unzip();
let mut tail = idents.into_iter();
let head = tail.next().unwrap();
let serve_impl = quote! {
#head #( .or(#tail) )*
};
let tokens = quote! {
fn #fn_name<T>(
service: T,
addr: impl Into<std::net::SocketAddr>,
) -> impl std::future::Future<Output = ()>
where
T: #interface + Clone + Send + Sync + 'static,
{
use warp::Filter;
#( #filters )*
warp::serve(#serve_impl).run(addr.into())
}
};
out.extend(tokens)
}
}
, derive ,
.
, ,
RPC, , Rust'.
, - HTTP
reqwest
.
рдореИрдХреНрд░реЛрдЬрд╝ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП, рдХреЛрдИ рднреА рдЖрдЧреЗ рдЬрд╛рдиреЗ рдФрд░ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдкреНрд░рдХрд╛рд░реЛрдВ рдХреЗ рд▓рд┐рдП рдУрдкрдиреИрдкреА рдпрд╛ рд╕реНрд╡реИрдЧрд░
рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реЗрд╢рд╛рди рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред рд▓реЗрдХрд┐рди рдореБрдЭреЗ рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рджреВрд╕рд░реЗ рддрд░реАрдХреЗ рд╕реЗ рдЬрд╛рдирд╛ рдмреЗрд╣рддрд░ рд╣реИ рдФрд░ рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдЬрдВрдЧ рдХреЛрдб рдЬрдирд░реЗрдЯрд░ рд▓рд┐рдЦрдирд╛ рд╣реИ, рдЗрд╕рд╕реЗ рдпреБрджреНрдзрд╛рднреНрдпрд╛рд╕ рдХреЗ рд▓рд┐рдП рдЕрдзрд┐рдХ рдЬрдЧрд╣ рдорд┐рд▓реЗрдЧреАред
рдпрджрд┐ рдЖрдк рдЗрд╕ рдЬрдирд░реЗрдЯрд░ рдХреЛ рдмрд┐рд▓реНрдб рдирд┐рд░реНрднрд░рддрд╛ рдХреЗ рд░реВрдк рдореЗрдВ рд▓рд┐рдЦрддреЗ рд╣реИрдВ, рддреЛ рдЖрдк рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ
syn
рдФрд░ quote
рдЗрд╕ рдкреНрд░рдХрд╛рд░, рдЬрдирд░реЗрдЯрд░ рд▓рд┐рдЦрдирд╛ рдмрд╣реБрдд рдЖрд░рд╛рдорджрд╛рдпрдХ рдФрд░ рд╕рд░рд▓ рд╣реЛрдЧрд╛ред рд╣рд╛рд▓рд╛рдБрдХрд┐, рдпрд╣ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рджреВрд░рдЧрд╛рдореА рд╡рд┐рдЪрд╛рд░ рд╣реИ :)
рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдХрд╛рдо рдХрд░ рд░рд╣реЗ рдХреЛрдб, рдЬрд┐рдирдореЗрдВ рд╕реЗ рдЙрджрд╛рд╣рд░рдг рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рджрд┐рдП рдЧрдП рдереЗ, рдЗрд╕
рд▓рд┐рдВрдХ рдкрд░ рдкрд╛рдП рдЬрд╛ рд╕рдХрддреЗ рд╣реИрдВ ред
рдзреНрдпрд╛рди рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдкрдХреЛ рдзрдиреНрдпрд╡рд╛рдж!