Возвращёна реализация сериализации в json для IResponse

Добавлены типы для экстракции данных из запросов средствами actix-web

Добавлен экстрактор для получения пользователя по токену доступа передаваемому в запросе

Добавлен макрос для автоматической реализации ResponseError для ошибок экстракторов

Добавлен эндпоинт users/me

Из главного проекта исключена зависимость actix-http посредством переноса части тестового функционала в отдельный crate
This commit is contained in:
2025-03-26 08:05:22 +04:00
parent ab1cbd795e
commit f703cc8326
24 changed files with 2022 additions and 34 deletions

1
actix-macros/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

7
actix-macros/Cargo.lock generated Normal file
View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "actix-utility-macros"
version = "0.1.0"

12
actix-macros/Cargo.toml Normal file
View File

@@ -0,0 +1,12 @@
[package]
name = "actix-macros"
version = "0.1.0"
edition = "2024"
[dependencies]
syn = "2.0.100"
quote = "1.0.40"
proc-macro2 = "1.0.94"
[lib]
proc-macro = true

79
actix-macros/src/lib.rs Normal file
View File

@@ -0,0 +1,79 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::{ToTokens, quote};
use syn::Attribute;
fn find_status_code(attrs: &Vec<Attribute>) -> Option<proc_macro2::TokenStream> {
attrs
.iter()
.find_map(|attr| -> Option<proc_macro2::TokenStream> {
if !attr.path().is_ident("status_code") {
return None;
}
let meta = attr.meta.require_name_value().ok()?;
let code = meta.value.to_token_stream().to_string();
let trimmed_code = code.trim_matches('"');
if let Ok(numeric_code) = trimmed_code.parse::<u16>() {
Some(quote! { actix_web::http::StatusCode::from_u16(#numeric_code).unwrap() })
} else {
let string_code: proc_macro2::TokenStream =
trimmed_code.to_string().parse().unwrap();
Some(quote! { #string_code })
}
})
}
fn impl_rem(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let variants = if let syn::Data::Enum(data) = &ast.data {
&data.variants
} else {
panic!("Only enums are supported");
};
let mut status_code_arms: Vec<proc_macro2::TokenStream> = variants
.iter()
.map(|v| -> Option<proc_macro2::TokenStream> {
let status_code = find_status_code(&v.attrs)?;
let variant_name = &v.ident;
Some(quote! { #name::#variant_name => #status_code, })
})
.filter(|v| v.is_some())
.map(|v| v.unwrap())
.collect();
if status_code_arms.len() < variants.len() {
let status_code = find_status_code(&ast.attrs)
.unwrap_or_else(|| quote! { actix_web::http::StatusCode::INTERNAL_SERVER_ERROR });
status_code_arms.push(quote! { _ => #status_code });
}
TokenStream::from(quote! {
impl actix_web::ResponseError for #name {
fn status_code(&self) -> actix_web::http::StatusCode {
match self {
#(#status_code_arms)*
}
}
fn error_response(&self) -> actix_web::HttpResponse<BoxBody> {
actix_web::HttpResponse::build(self.status_code()).json(crate::utility::error::ResponseErrorMessage::new(self.clone()))
}
}
})
}
#[proc_macro_derive(ResponseErrorMessage, attributes(status_code))]
pub fn rem_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
impl_rem(&ast)
}