рдкреНрд░рдХреНрд░рд┐рдпрд╛рддреНрдордХ рдореИрдХреНрд░реЛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЬрдВрдЧ рдореЗрдВ рдПрдХ рдПрдкреАрдЖрдИ рд▓рд┐рдЦрдирд╛

Rust рдореЗрдВ рдкреНрд░рдХреНрд░рд┐рдпрд╛рддреНрдордХ рдореИрдХреНрд░реЛрдЬрд╝ рдПрдХ рдмрд╣реБрдд рд╢рдХреНрддрд┐рд╢рд╛рд▓реА рдХреЛрдб рдЬрдирд░реЗрд╢рди рдЯреВрд▓ рд╣реИ рдЬреЛ рдЖрдкрдХреЛ рдмреЙрдпрд▓рд░рдкреНрд▓реЗрдЯ рдХреЛрдб рдХреЗ рдПрдХ рдЯрди рдХреЛ рд▓рд┐рдЦреЗ рдмрд┐рдирд╛ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ, рдпрд╛ рдХреБрдЫ рдирдИ рдЕрд╡рдзрд╛рд░рдгрд╛рдУрдВ рдХреЛ рд╡реНрдпрдХреНрдд рдХрд░рддрд╛ рд╣реИ, рдЬреИрд╕рд╛ рдХрд┐ рдЯреЛрдХрд░рд╛ рдХреЗ рдбреЗрд╡рд▓рдкрд░реНрд╕ рдиреЗ рдХрд┐рдпрд╛ рдерд╛, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП async_traitред


рдлрд┐рд░ рднреА, рдХрдИ рд▓реЛрдЧ рдЗрд╕ рдЙрдкрдХрд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдЪрд┐рдд рд░реВрдк рд╕реЗ рдбрд░рддреЗ рд╣реИрдВ, рдореБрдЦреНрдп рд░реВрдк рд╕реЗ рдЗрд╕ рддрдереНрдп рдХреЗ рдХрд╛рд░рдг рдХрд┐ рд╡рд╛рдХреНрдпрд╡рд┐рдиреНрдпрд╛рд╕ рдХреЗ рдкреЗрдбрд╝ рдФрд░ рд╕реНрдереВрд▓ рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рдХреЛ рдкрд╛рд░реНрд╕ рдХрд░рдирд╛ рдЕрдХреНрд╕рд░ "рд╕реВрд░реНрдпрд╛рд╕реНрдд рдореИрдиреНрдпреБрдЕрд▓" рдореЗрдВ рдмрджрд▓ рдЬрд╛рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╕рдорд╕реНрдпрд╛ рдХреЛ рдмрд╣реБрдд рдХрдо рд╕реНрддрд░ рдкрд░ рд╣рд▓ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рд╣реИред


рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рдореИрдВ рдХреБрдЫ рд╕рд╛рдЭрд╛ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ, рдореЗрд░реА рд░рд╛рдп рдореЗрдВ, рдкреНрд░рдХреНрд░рд┐рдпрд╛рддреНрдордХ рдореИрдХреНрд░реЛрдЬрд╝ рд▓рд┐рдЦрдиреЗ рдореЗрдВ рд╕рдлрд▓ рджреГрд╖реНрдЯрд┐рдХреЛрдг, рдФрд░ рджрд┐рдЦрд╛рддреЗ рд╣реИрдВ рдХрд┐ рдЖрдЬ рдкреНрд░рдХреНрд░рд┐рдпрд╛рддреНрдордХ рдореИрдХреНрд░реЛрдЬрд╝ рдХреЛ рдЕрдкреЗрдХреНрд╖рд╛рдХреГрдд рд╕рд░рд▓ рдФрд░ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд░реВрдк рд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред


рдкреНрд░рд╕реНрддрд╛рд╡рдирд╛


рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╣рдо рдЙрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддреЗ рд╣реИрдВ рдЬрд┐рд╕реЗ рд╣рдо рдореИрдХреНрд░реЛрдЬрд╝ рдХреА рдорджрдж рд╕реЗ рд╣рд▓ рдХрд░реЗрдВрдЧреЗ: рд╣рдо рдХреБрдЫ рд╕рд╛рд░ RPC API рдХреЛ рдПрдХ рд╡рд┐рд╢реЗрд╖рддрд╛ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВрдЧреЗ , рдЬреЛ рддрдм рд╕рд░реНрд╡рд░ рднрд╛рдЧ рдФрд░ рдХреНрд▓рд╛рдЗрдВрдЯ рднрд╛рдЧ рджреЛрдиреЛрдВ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реИ; рдФрд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛рддреНрдордХ рдореИрдХреНрд░реЛрдЬрд╝, рдмрджрд▓реЗ рдореЗрдВ, рдмреЙрдпрд▓рд░рдкреНрд▓реЗрдЯ рдХреЛрдб рдХреЗ рдПрдХ рдЧреБрдЪреНрдЫрд╛ рдХреЗ рдмрд┐рдирд╛ рд╣рдореЗрдВ рдХрд░рдиреЗ рдореЗрдВ рдорджрдж рдХрд░реЗрдВрдЧреЗред рдЗрд╕ рддрдереНрдп рдХреЗ рдмрд╛рд╡рдЬреВрдж рдХрд┐ рд╣рдо рдХреБрдЫ рд╣рдж рддрдХ рдЕрдореВрд░реНрдд рдПрдкреАрдЖрдИ рдХреЛ рд▓рд╛рдЧреВ рдХрд░реЗрдВрдЧреЗ, рдХрд╛рд░реНрдп рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХрд╛рдлреА рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ, рдФрд░, рдЕрдиреНрдп рдмрд╛рддреЛрдВ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдкреНрд░рдХреНрд░рд┐рдпрд╛рддреНрдордХ рдореИрдХреНрд░реЛ рдХреА рдХреНрд╖рдорддрд╛рдУрдВ рдХрд╛ рдкреНрд░рджрд░реНрд╢рди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрджрд░реНрд╢ рд╣реИред


рдПрдкреАрдЖрдИ рдХреЛ рдПрдХ рдмрд╣реБрдд рд╣реА рд╕рд░рд▓ рд╕рд┐рджреНрдзрд╛рдВрдд рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛: 4 рдкреНрд░рдХрд╛рд░ рдХреЗ рдЕрдиреБрд░реЛрдз рд╣реИрдВ:


  • рдХреЛрдИ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЗ рд╕рд╛рде рдЕрдиреБрд░реЛрдз рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП /ping:ред
  • рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рд╕рд╛рде рдЕрдиреБрд░реЛрдз рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ, рдЬрд┐рдирдореЗрдВ рд╕реЗ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЛ URL рдХреНрд╡реЗрд░реА рдХреЗ рд░реВрдк рдореЗрдВ рдкреНрд░рд╕рд╛рд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП /status?name=foo&count=15:ред
  • рдорд╛рдкрджрдВрдбреЛрдВ рдХреЗ рдмрд┐рдирд╛ POST рдЕрдиреБрд░реЛрдзред
  • JSON рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд╛рд░рд┐рдд рд╣реЛрдиреЗ рд╡рд╛рд▓реЗ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЗ рд╕рд╛рде POST рдЕрдиреБрд░реЛрдзред

рд╕рднреА рдорд╛рдорд▓реЛрдВ рдореЗрдВ, рд╕рд░реНрд╡рд░ рдПрдХ рд╡реИрдз JSON рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рд╕рд╛рде рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХрд░реЗрдЧрд╛ред


рд╕рд░реНрд╡рд░ рдмреИрдХрдПрдВрдб рдХреЗ рд░реВрдк рдореЗрдВ, рд╣рдо рдЯреЛрдХрд░рд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗ warpред


рдЖрджрд░реНрд╢ рд░реВрдк рд╕реЗ, рдореИрдВ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ:


//  :

/// ,   .      URL query,    JSON.
#[derive(Debug, FromUrlQuery, Deserialize, Serialize)]
struct Query {
    first: String,
    second: u64,
}

///  ,      
///  API  warp'.
#[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();
    //          API
    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 :


///        `#[derive(FromUrlQuery)]`,     
///    #[from_url_query(rename = "bar", skip, etc)]
#[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");
                                    }
                                }
                            }
                        }
                        _ => {
                            // Other attributes are ignored
                        }
                    }
                }
                (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,
    //           
    //  .
    //  darling::ast::Data   :   
    // ,     .
    //         ,   
    //   ().
    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 {
    //     ,      
    //  "Serde".
    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 {
        //          
        // ,     `unwrap`  .
        let fields = self.data.clone().take_struct().unwrap();
        //         ,   
        //   Query  SerdeQuery,   .
        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!`
        //         , 
        //  ,    "#"  "$".
        quote! {
            //  serde    .
            use http_api::export::serde_derive::Deserialize;

            #[derive(Deserialize)]
            //    serde   ,   
            //  .
            #[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 {
    //     :    ,  
    //     AST  .
    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 {
    //      ,   `http_api_endpoint`
    //       `http_api` .

    //    `http_api_endpoint`  
    //   , ,  rustc  
    //   .
    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,
    //     ,     
    //  None,       :
    // #[http_api_endpoint(method = "get", rename = "foo")]
    #[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();

        // ,     -   &self   ,
        //    &mut self,   &self: Arc<Self>  
        //  .
        if let Some(arg) = args.next() {
            match arg {
                // `self`  `syn`   Receiver.
                syn::FnArg::Receiver(syn::Receiver {
                    //  `reference`  ,     
                    // `&self`.
                    reference: Some(_),
                    //  `mutability`   ,   
                    //    `mut`.
                    mutability: None,
                    //      .
                    ..
                }) => {
                    //  ,    .
                }
                _ => {
                    //   -  ,  
                    //   .
                    return Err(invalid_method(&arg));
                }
            }
        } else {
            return Err(invalid_method(&sig));
        }

        //    .
        let arg = args
            .next()
            .map(|arg| match arg {
                // `FnArg`    `Typed`,  `Receiver`,  `Receiver`
                //      ,   
                //   .
                syn::FnArg::Typed(arg) => Ok(arg.ty.clone()),
                //      `self`.
                _ => unreachable!("Only first argument can be receiver."),
            })
            // Transpose   ,  
            // `Option<Result<...>>`  `Result<Option<...>>`,   
            //  .
            .transpose()?;

        //    ,     `Typed`,
        //   `Receiver`.
        let ret = match &sig.output {
            syn::ReturnType::Type(_, ty) => Ok(ty.clone()),
            _ => Err(invalid_method(&sig)),
        }?;

        // ,    ,   , 
        //   .
        //   `FromMeta::from_nested_meta`    .
        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'.
    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)?;

        //      HTTP API.
        Ok(Self {
            item_trait,
            endpoints,
            attrs,
        })
    }
}


, warp.


, , , warp . warp' , Filter. and, map, and_then, , .


, , GET JSON, - :


///    ,    
///   warp'
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,
{
    //    ,    GET ,
    //   .
    warp::get()
        //     ,   
        //     {name}
        .and(warp::path(name))
        //     and_then    
        //      JSON 
        .and_then(move || {
            let handler = handler.clone();
            //      , 
            //        async .
            async move {
                match handler() {
                    Ok(value) => Ok(warp::reply::json(&value)),
                    //  warp'     ,
                    //     ,    
                    //       -
                    //   .
                    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))
        //       URL query,   
        //    query.
        .and(warp::filters::query::raw())
        .and_then(move |raw_query: String| {
            let handler = handler.clone();
            async move {
                //         
                // FromUrlQuery      
                //  .
                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     `/:u32` 
// `/:socketaddr`
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;

        //        ,  
        //   warp .
        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();
        //        
        // `a.or(b).or(c).or(d)`,     
        //   ,   .
        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 )*

                //      API  
                // warp .
                warp::serve(#serve_impl).run(addr.into())
            }

        };
        out.extend(tokens)
    }
}


, derive ,
.
, ,
RPC, , Rust'.
, - HTTP
reqwest .


рдореИрдХреНрд░реЛрдЬрд╝ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП, рдХреЛрдИ рднреА рдЖрдЧреЗ рдЬрд╛рдиреЗ рдФрд░ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдкреНрд░рдХрд╛рд░реЛрдВ рдХреЗ рд▓рд┐рдП рдУрдкрдиреИрдкреА рдпрд╛ рд╕реНрд╡реИрдЧрд░
рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реЗрд╢рд╛рди рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред рд▓реЗрдХрд┐рди рдореБрдЭреЗ рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рджреВрд╕рд░реЗ рддрд░реАрдХреЗ рд╕реЗ рдЬрд╛рдирд╛ рдмреЗрд╣рддрд░ рд╣реИ рдФрд░ рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдЬрдВрдЧ рдХреЛрдб рдЬрдирд░реЗрдЯрд░ рд▓рд┐рдЦрдирд╛ рд╣реИ, рдЗрд╕рд╕реЗ рдпреБрджреНрдзрд╛рднреНрдпрд╛рд╕ рдХреЗ рд▓рд┐рдП рдЕрдзрд┐рдХ рдЬрдЧрд╣ рдорд┐рд▓реЗрдЧреАред
рдпрджрд┐ рдЖрдк рдЗрд╕ рдЬрдирд░реЗрдЯрд░ рдХреЛ рдмрд┐рд▓реНрдб рдирд┐рд░реНрднрд░рддрд╛ рдХреЗ рд░реВрдк рдореЗрдВ рд▓рд┐рдЦрддреЗ рд╣реИрдВ, рддреЛ рдЖрдк рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ
synрдФрд░ quoteрдЗрд╕ рдкреНрд░рдХрд╛рд░, рдЬрдирд░реЗрдЯрд░ рд▓рд┐рдЦрдирд╛ рдмрд╣реБрдд рдЖрд░рд╛рдорджрд╛рдпрдХ рдФрд░ рд╕рд░рд▓ рд╣реЛрдЧрд╛ред рд╣рд╛рд▓рд╛рдБрдХрд┐, рдпрд╣ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рджреВрд░рдЧрд╛рдореА рд╡рд┐рдЪрд╛рд░ рд╣реИ :)


рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдХрд╛рдо рдХрд░ рд░рд╣реЗ рдХреЛрдб, рдЬрд┐рдирдореЗрдВ рд╕реЗ рдЙрджрд╛рд╣рд░рдг рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рджрд┐рдП рдЧрдП рдереЗ, рдЗрд╕
рд▓рд┐рдВрдХ рдкрд░ рдкрд╛рдП рдЬрд╛ рд╕рдХрддреЗ рд╣реИрдВ ред


рдзреНрдпрд╛рди рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдкрдХреЛ рдзрдиреНрдпрд╡рд╛рдж!

Source: https://habr.com/ru/post/undefined/


All Articles