Snapshot do log de execução do Ralph Loop que gerou o MVP do framework. Cada seção representa uma user story completada com aprendizados de implementação.
Fonte viva: .ralph/serverust-framework/progress.txt — este documento é uma cópia para publicação. Para atualizações da fonte, edite o original.
Stories completadas: US-001 a US-011 (todas com passes: true).
Padrões reusáveis descobertos durante a implementação, consultar antes de iterar novamente:
- PRD HAZARD: o hook
ctx-rewrite(~/.claude/hooks/ctx-rewrite.sh) envolvejqemctx exec jq, que TRUNCA o output. Comandos tipojq '...' file > /tmp/out && mv /tmp/out filecorrompem o arquivo (escrevem só ~100 linhas truncadas). Para editarprd.jsonuse python3 (json.load/json.dump). Para ler, usepython3 -c '...'ouawk, nuncaReaddireto se > 100 linhas. Appcarrega umContainer(TypeId → Arc<dyn Any+Send+Sync>) como state doRouter<Container>; blanketFromRef<Container> for Arc<T: ?Sized + Send + Sync + 'static>resolveState<Arc<dyn Trait>>em handlers automaticamente.App::provide::<T>(Arc<T>)registra (turbofish necessário paradyn Trait);App::r#override::<T>(...)substitui (raw identifier porqueoverrideé palavra reservada).MethodRouter<Container>é o tipo concreto emRoute; handlers semStateinferemContainerviaHandler<_, S>polimórfico.#[injectable]em struct/enum geraimpl serverust_core::Injectablemarker (sem efeito runtime); aceita#[injectable(static)]como opt-in para hint de dispatch estático (apenas valida o atributo).edition = "2024",rust-version = "1.85"em todos os crates.proc-macro = truedeclarado em[lib]doserverust-macros/Cargo.toml.workspace.resolver = "2"na raiz.- Macros de rota geram unit struct com mesmo nome do handler, implementando
serverust_core::IntoRoute. serverust_core::extract::Jsoné a versão validante (requerT: Validate); demais extractors (Path,Query,State) são re-export direto do axum.serverust_core::__privatere-exportaaxum,http,serde_jsoneutoipapara uso interno das macros.- Versões: axum 0.8, tokio 1, syn 2, quote 1, http 1, serde 1, tower 0.5, validator 0.20, utoipa 5.
- Path params em axum 0.8 usam sintaxe
{param}(não:param). - Testes de integração em
serverust-core/tests/usamtower::ServiceExt::oneshotpara validar rotas sem bind real. ApiErrortrait + derive: variantes anotadas com#[status(N)]e#[message("...")]; derive emiteApiError+IntoResponsesimultaneamente, com payload JSON{ "error": "<message>" }.- Validação:
validation_error_response(&ValidationErrors)constrói o payload 422 padrão{ error: "validation_error", fields: { campo: [mensagens] } }. Routecarregapath+HttpMethod+MethodRouter+Operation;Appacumula tudo emOpenApiStatee materializa/openapi.json+/docs+/redoceminto_router().- Swagger UI e ReDoc servidos via HTML inline carregando assets via CDN (sem
utoipa-swagger-uicrate — evita build script e mantém binário enxuto). - Imports utoipa:
HttpMethod/PathItem/Pathsestão emutoipa::openapi;Operation/OperationBuilder/PathItemBuilderestão emutoipa::openapi::path;ResponseBuilderemutoipa::openapi. ToSchema+Validateem DTOs requer#[schema(min_length=..)]espelhando#[validate(length(min=..))]— utoipa NÃO lê atributos do validator automaticamente.Appexpõe:openapi_info(title, version),register_schema::<T>(),docs(path),redoc(path),route(handler),into_router(),run_http(addr),interceptor(I).- Pipeline: trait
Guard(async check sobre&Parts),Pipe<I>(transform I → Output, retornaResponseem erro) eInterceptor(async wrap req/next) em serverust-core. - Macro
#[guard(Type)]colocada acima de#[get/post/...]injetaGuardCheck<Type>comoFromRequestPartsno início da assinatura — múltiplos#[guard]empilháveis. - Interceptors são aplicados em
into_router()antes das rotas de docs (/openapi.json/docs/redoc) — middleware do usuário não envelopa documentação. clippy::result_large_erré suprimido emserverust-core/src/pipeline.rsporqueResponseé a moeda de erro idiomática do axum.- Inserir extractor sintético na assinatura: usar
syn::FnArgviaparse_quote!+func.sig.inputs.insert(0, ...)para manter o body-consuming extractor (Json<T>) sempre por último. - Runtime dual mora em
serverust-lambda(extensão traitAppRuntime) para não poluirserverust-corecom dependência pesadalambda_http;App::new().run().awaitfunciona viause serverust_lambda::AppRuntime;. detect_runtime(env_value: Option<&str>) -> Runtimeé função pura — evita mutação de env em testes (em Rust 2024std::env::set_varéunsafe).- Fixtures de eventos AWS em
tests/fixtures/*.json+lambda_http::request::from_strpermitem testar roteamento sem boot do runtime. lambda_http::Bodyé#[non_exhaustive]— patterns precisam de arm_.lambda_http::run(router)requer apenas+ Futureno impl trait (não+ Send); boundSendquebra porque axum body dyn não éSync.axum::middleware::from_fn(f)exige fn item type / closure inferido (não aceita fn pointer comPin<Box<dyn Future>>); para expor uma "layer pronta" sem amarrar o tipo, usemacro_rules!que faz o wiring no call site.tracing::Instrument::instrument(span)no future é obrigatório para preservar contexto através de await points;span.enter()com guard só funciona em código síncrono.tracing_subscriber0.3.json(): mensagem fica emfields.message; campos de span pai aparecem emspan.<field>quando.with_current_span(true)está ativo.- EMF mínimo:
{"_aws":{"Timestamp":ms,"CloudWatchMetrics":[{"Namespace","Dimensions":[[]],"Metrics":[{"Name","Unit"}]}]},"<Name>":<value>}— uma linha JSON por evento em stdout. opentelemetry_sdk0.26:TracerProvider(sem prefixoSdk); id generator vai emConfig::default().with_id_generator(...)passado via.with_config(...); propagador X-Ray vem deopentelemetry-aws.- clippy 1.94+:
func.block = Box::new(new_block)em macros proc disparaclippy::replace_box; usar*func.block = new_block. - Features opcionais (
otel,dynamodb) com deps pesadas (aws-sdk,opentelemetry) preservam binário enxuto no build default — documentar em Cargo.toml o que cada feature traz.
- Criado Cargo workspace com 5 membros:
serverust-core,serverust-macros,serverust-cli,serverust-lambda,serverust-telemetry. - Cada crate com
edition = "2024"erust-version = "1.85". serverust-macroscomproc-macro = trueem[lib].- Testes mínimos escritos primeiro (TDD): 1 teste por crate executável.
cargo build --workspaceecargo test --workspacepassando sem erros.- Files:
Cargo.toml,serverust-*/Cargo.toml,serverust-*/src/lib.rs(oumain.rs). - Learnings:
serverust-macrosnão aceita testes unitários diretos (proc-macro crate) — sem testes ali.- Estrutura modular criada e pronta para US-002.
- Implementado
Appbuilder emserverust-core:App::new().route(handler).into_router()/run_http(addr). Route+IntoRoutetrait permitem registrar handlers tipados sem reflection.- Macros
#[get],#[post],#[put],#[patch],#[delete]emserverust-macrostransformamfnem unit struct comIntoRouteimpl. - Extractors re-exportados em
serverust_core::extract:Path,Query,Json,State. - 6 testes de integração validam: GET estático,
Path<u32>,Query<T>,Json<T>POST round-trip, múltiplas rotas compostas,run_httpbind. - Files:
serverust-core/{Cargo.toml, src/lib.rs, src/app.rs, src/route.rs, tests/routes.rs},serverust-macros/{Cargo.toml, src/lib.rs}. - Learnings:
- axum 0.8 mudou sintaxe de path params de
:idpara{id}. quote!/parse_macro_inputfluxo padrão: parseLitStrdo attr,ItemFndo item, gerar struct + implIntoRoutecom fn original aninhada eminto_route().- Função interna shadow-ada dentro de
into_route()permite passar fn aoaxum::routing::METHOD(fn)sem conflito com a struct externa. - dev-dependency
serverust-macros = { path = "../serverust-macros" }evita ciclo do workspace.
- axum 0.8 mudou sintaxe de path params de
- Implementada validação automática via
serverust_core::extract::Json<T>(T:DeserializeOwned + Validate). Falha de validação retorna HTTP 422 com payload padronizado antes do handler ser invocado. - Implementada derive
ApiErroremserverust-macros: lê#[status(N)]e#[message("...")]por variante e emite implApiError+ implIntoResponse, permitindo?em handlersResult<T, E: ApiError>. - 5 testes de integração novos em
serverust-core/tests/validation.rscobrem: happy path, falha de validação com payload estruturado, duas variantes deApiError(404, 409) eIntoResponsedireto. - Existing
routes.rstest atualizado para derivarValidateemCreateUser(consequência do novoJsonvalidante). - Files:
serverust-core/{Cargo.toml, src/{lib.rs,error.rs,validation.rs}, tests/{routes.rs,validation.rs}},serverust-macros/src/lib.rs. - Learnings:
- validator 0.20 expõe
ValidationErrors::field_errors()retornandoHashMap<&'static str, &Vec<ValidationError>>; campomessageéOption<Cow<'static, str>>, cai emcodequando ausente. - Substituir o re-export
extract::Jsonpor um wrapper validante quebra DTOs sem#[derive(Validate)]; solução é derivarValidateem todos os payloads (no-op quando sem regras). - Em axum 0.8 a trait
FromRequesté nativa async (sem#[async_trait]); rejection do tipoResponsedeixa o extrator devolver qualquer status/payload sem cerimônia. - Derive de
proc_macro2sobre enums com variantes Unit/Tuple/Named exige patterns distintos (Self::V,Self::V(..),Self::V { .. }). LitIntem#[status(404)]aceita literal inteiro qualquer; conversão parau16acontece em runtime viaStatusCode::from_u16.
- validator 0.20 expõe
- Integrei utoipa 5 no
serverust-core.Appagora gera OpenAPI 3.1 dinâmico:openapi_info(title, version)customiza Info; defaults:"serverust"/"0.1.0".register_schema::<T: ToSchema>()registra T emcomponents.schemas.docs(path)/redoc(path)customizam paths (defaults/docse/redoc).into_router()injeta automaticamente/openapi.json,/docse/redoc.
- Macros de rota (
#[get/post/put/patch/delete]) agora geram, além doIntoRoute, umautoipa::openapi::path::Operationcomoperation_id= nome da fn e resposta 200 default. /docse/redocservidos via HTML inline carregando Swagger UI e ReDoc via CDN (jsdelivr) — decisão consciente para evitar build script deutoipa-swagger-uie manter binário enxuto.- DTOs precisam de
#[derive(ToSchema)]+ atributos#[schema(...)]espelhando os#[validate(...)]para constraints aparecerem no spec. - 6 testes novos em
serverust-core/tests/openapi.rs. - Files:
serverust-core/{Cargo.toml, src/{lib.rs, app.rs, route.rs, openapi.rs}, tests/openapi.rs},serverust-macros/src/lib.rs. - INFRA FIX (recorrente):
prd.jsoncorrompido novamente pelo marcador de truncamento doReadapós linha 100. Padrão: lerprd.jsonviajqouawk, nunca viaReaddireto se passar de 100 linhas. - Learnings:
- utoipa 5:
Operation/OperationBuilder/PathItemBuilderemutoipa::openapi::path;HttpMethod/PathItem/Paths/ResponseBuilderemutoipa::openapi. PathsBuilder.path()faz merge automático quando a mesma key aparece duas vezes — acumular paths por(path, method)→ group emBTreeMapantes debuild().utoipa-swagger-uicrate baixa assets via build script; para MVP evitamos isso servindo HTML via CDN.
- utoipa 5:
serverust_core::Container(HashMap<TypeId, Arc<dyn Any+Send+Sync>>) é o axum state doApp.App::provide::<T>(Arc<T>)eApp::r#override::<T>(Arc<T>)registram/substituem services.- Blanket
impl<T: ?Sized + Send + Sync + 'static> FromRef<Container> for Arc<T>faz handlers extraíremState<Arc<dyn Trait>>automaticamente. Route.method_routeragora éMethodRouter<Container>(macros existentes inferem viaHandlertrait polimórfica).- Macro
#[injectable](struct/enum, opcional(static)) emiteimpl serverust_core::Injectablecomo marker — registro é explícito via builder. - 4 testes novos em
tests/di.rs: injeção viaState<Arc<dyn Trait>>, override com mock, Singleton compartilhado, marker trait em compile-time. - Files:
serverust-core/src/{container.rs, lib.rs, app.rs, route.rs},serverust-core/tests/di.rs,serverust-macros/src/lib.rs. - Learnings:
TypeId::of::<Arc<dyn Trait>>()é único por trait — permite armazenar múltiplosArc<dyn _>no mesmo HashMap sem colisão.- Orphan rule permite blanket
FromRef<Container>porqueContaineré local. overrideé palavra reservada em Rust; método precisa serr#override.
- Implementadas as 3 primitivas de pipeline em
serverust-core(módulo novopipeline.rs):Guard(async fn check em&Parts→Result<(), Response>) + extractor zero-costGuardCheck<G>(FromRequestParts).Pipe<I>com associated typeOutput;ParseUuidPipe: Pipe<String, Output=Uuid>como exemplo canônico; extractorPipePath<P>aplica o pipe sobrePath<String>.Interceptor(async wrap(Request, Next) -> Response) registrado viaApp::interceptor(I).
- Macro
#[guard(Type)]emserverust-macros: insereGuardCheck<Type>na posição 0 dofunc.sig.inputs. Múltiplos guards empilháveis. - 6 testes novos em
serverust-core/tests/middleware.rscobrindo cada primitiva isolada + composição completa. - Files:
serverust-core/{Cargo.toml, src/{lib.rs, app.rs, pipeline.rs}, tests/middleware.rs},serverust-macros/src/lib.rs. - Learnings:
- Atributos macro empilhados expandem top-down (outermost primeiro). Para
#[guard]modificar a função ANTES de#[get]ver, precisa estar ACIMA da macro de rota. - Inserir extractor sintético em posição 0 é crítico: handlers com body-consuming extractor (
Json<T>) só funcionam se este for o ÚLTIMO param.GuardCheck/PipePathsãoFromRequestPartse devem vir antes. - Testes não devem depender de estado global (
AtomicUsizeestático) — testes rodam em paralelo. Router::layer()afeta apenas rotas registradas antes da chamada — guardar interceptors emVec<RouterMutator>e aplicar eminto_router()antes de adicionar/openapi.json/docs/redoc.
- Atributos macro empilhados expandem top-down (outermost primeiro). Para
Runtimeenum + função puradetect_runtime(Option<&str>) -> Runtime.run_http(app, addr),run_lambda(app),run(app)(despacha por env).- Trait
AppRuntimeadicionaApp::new().run().awaitvia dot-chain (use serverust_lambda::AppRuntime). run_lambdadefineAWS_LAMBDA_HTTP_IGNORE_STAGE_IN_PATH=trueautomaticamente para rotas funcionarem idêntico em REST v1, HTTP v2 e Function URL.- 3 unit tests + 3 integration tests com fixtures JSON (apigw v1 GET, apigw v2 POST, Function URL).
- Files:
serverust-lambda/{Cargo.toml, src/lib.rs, tests/lambda_to_axum.rs, tests/fixtures/*.json},serverust-cli/src/main.rs. - Learnings:
- lambda_http 1.2 + axum 0.8 + http 1: integração direta sem adapter explícito.
- Para v1 (REST API Gateway),
requestContext.pathinclui o prefixo do stage (/prod/hello) — semAWS_LAMBDA_HTTP_IGNORE_STAGE_IN_PATH=trueo axum recebe/prod/helloe devolve 404. + Sendno impl Trait quebrou porque o future delambda_http::run(router)contémdyn HttpBodyque não éSync.lambda_http::request::from_str(json)faz a desambiguação entre v1/v2/Function URL pelos campos presentes.
serverust-telemetry:logger::init()instalatracing-subscriberJSON estruturado (env filter viaRUST_LOG).correlation::extract_or_generate_correlation_id(&HeaderMap)(extraiRoot=deX-Amzn-Trace-Id, fallbackX-Correlation-Id, ou gera no formato X-Ray).- Middleware
correlation_id_middlewareinjeta header e abre span tracing. emf::emit_emf(namespace, name, unit, value)escreve linha JSON única no formato EMF.idempotency: traitIdempotencyStore+IdempotencyRecord+IdempotencyError,InMemoryIdempotencyStore,DynamoDbIdempotencyStoreatrás da featuredynamodb.- Feature
oteladicionaotel::init_xray(service)que configuraXrayIdGenerator+XrayPropagator.
- Macro
#[metric(name, unit, namespace)]emserverust-macros: cronometra função sync/async e emite EMF com tempo em ms. - 14 testes novos.
- Files:
serverust-telemetry/{Cargo.toml, src/{lib.rs,logger.rs,correlation.rs,emf.rs,idempotency.rs,otel.rs}, tests/*},serverust-macros/src/lib.rs. - Learnings:
tracing-subscriber0.3.json(): mensagem fica emfields.message; campos de span pai emspan.<field>quando.with_current_span(true).tracing::Instrument::instrument(span)no future preserva contexto através de await points.axum::middleware::from_fn(f)exige fn item type — solução: async fn público + macrocorrelation_id_layer!()para wiring.- Format EMF mínimo: bloco
_aws.CloudWatchMetrics[].Metrics[]comName/Unit+ campo top-level homônimo com o valor. - Macro
#[metric]precisa diferenciar sync vs async viafunc.sig.asyncness.is_some().
- CLI
serverustemserverust-clicom clap derive (Parser+Subcommand+ValueEnum):new <name>gera scaffold completo (recusa se diretório existe).generate <kind> <name>para 8 kinds:resource,module,controller,service,pipe,guard,interceptor,filter.dev→cargo watch -x run.build [--release]→cargo build [--release].deploy lambda [--arch arm64|x86_64]→cargo lambda deploy.info→ versão da CLI + arch + features.openapi --out <path>→ exporta spec sem subir servidor.
- Crate dividida em
cli/commands/scaffold/templatespara permitir testes sem efeito colateral. - 20 testes novos (parse, command-builders, scaffolding em tempdir).
- Files:
serverust-cli/{Cargo.toml, src/{main.rs, lib.rs, cli.rs, commands.rs, scaffold.rs, templates.rs}, tests/*}. - Learnings:
- clap
ValueEnumcom underscores produz kebab-case default — usar#[value(name = "x86_64", alias = "x86-64")]. [[bin]]+[lib]no mesmoCargo.tomlmantém o binário disponível comoserveruste expõe a lib para tests.#[path]mapeia filename com.(Nest convention) para mod identifier válido em Rust.std::process::Commandpermite validar invocação SEM spawn via.get_program()/.get_args().
- clap
serverust-core/src/config.rs:- Structs:
ServerConfig,LambdaConfig,TelemetryConfig,OpenApiConfig,ServerustConfig. ServerustConfig::load()lêserverust.tomlno profile"default".ServerustConfig::load_for_profile(profile)seleciona perfil (herda dedefault).- Env override via
SERVERUST_*com separador__para campos aninhados.
- Structs:
App::config(ServerustConfig)armazena noContainer; handlers extraem viaState<Arc<ServerustConfig>>.- 8 testes em
tests/config.rsserializados viaENV_MUTEXestático. - Files:
serverust-core/{Cargo.toml, src/config.rs, src/lib.rs, src/app.rs, tests/config.rs},serverust-cli/src/templates.rs. - Learnings:
figment::Toml::file(path).nested()é o modo correto para TOML profile-aware (top-level keys = profile names).- Env tests paralelos com
std::env::set_varcontaminam testes de file-loading —ENV_MUTEXem TODOS que chamamload_from. - figment profile inheritance:
select("dev")herda dodefaultautomaticamente.
- Adicionados
examples/hello-worldeexamples/funds-apicomo membros do workspace. hello-world: binário mínimo para benchmark de cold start.funds-api: CRUD completo de Fundos de Investimento (5 handlers, validação, OpenAPI, DI, 5 integration tests).scripts/bench.sh: mede tamanho stripped (alvo < 10 MB), startup local, cold start Lambda via AWS CLI (alvo < 50 ms, opcional--lambda).- README.md completo (features, requisitos, estrutura, quick start, config, exemplos, deploy, benchmark, CLI).
- Files:
README.md,scripts/bench.sh,examples/hello-world/*,examples/funds-api/*. - Learnings:
- Disk full (100% usage):
cargo buildfalha silenciosamente com exit code 1 e zero output —ctx-rewritehook trunca o output. Solução:cargo cleanlibera otarget/. - Exemplo
funds-apiprecisa de[lib]+[[bin]]noCargo.tomlpara expor lib para integration tests. - Handlers com
Path<u64>exigemaxumnoCargo.tomldo exemplo (não vem re-exportado viaserverust-core).
- Disk full (100% usage):
Fonte: .ralph/serverust-events/progress.txt · Branch: ralph/serverust-events-kafka
Stories: US-1 a US-13 (passes: true)
serverust-eventssegue feature flags opt-in: deps pesadas (rdkafka, aws-*) só compiladas com flag explícita; default leve para preservar invariante de cold start.- Trait async pública usa
async-trait = "0.1"em vez de native async-fn-in-trait — facilita uso como trait object (Arc<dyn Broker>). - Errors públicos via
thiserror::Errorcom variantesString(sem&'static str) para contexto completo. examples/hello-worldé INTOCÁVEL — não adicionar deps; é o benchmark de cold start.- Invariante chave:
cargo tree -p serverust-core | grep -E 'kafka|rdkafka|event'deve retornar vazio. - Testes de integração em
serverust-events/tests/como crates separados (#![cfg(feature = "...")]no topo quando precisam de feature). - Campo ainda não usado mas que será em US futura: marcar com
#[allow(dead_code)]+ comentário citando a US destino. - prd.json corrompeu em todas as iterações na linha 101 (ctx-rewrite hook trunca com marker literal
[... truncado: ...]). Para editar prd.json sempre usarpython3(json.load/json.dump), nuncajqvia Bash.
US-1 — Broker trait + KafkaBroker
expect_errem tipo semDebugquebra build — usarmatchquando o tipo Ok não derivaDebug.KafkaBrokerfeaturekafkaé umbrella de rdkafka + aws-*;kafka-producervirou alias para compatibilidade retroativa.
US-2 — InMemoryBroker
Mutexnão pode ser mantido através deawaitpoints: clonar handlers antes de iterar.#![cfg(feature = "...")]no topo do arquivo de teste emtests/exclui o arquivo inteiro sem a feature — mais limpo que#[cfg]em cada item.- Feature sem deps extras: só o módulo habilitado — não polui a árvore de deps.
US-3 — EventRouter
attach(&dyn Broker)requerB: Broker + ?Sizedno where clause; sem isso,Arc<dyn Broker>falha porSizedimplícito.Fn(T) -> Fut + Send + Sync + 'static+Arc::new(handler)permite clonar handler dentro de cada invocação sem exigirH: Clone.- API com 3 generics (
T,H,Fut) força turbofish<T, _, _>— aceitável, Axum usa o mesmo padrão.
US-4 — Extractors tipados
- Blanket impl
FromMessageparaT: DeserializeOwnedconflita com impls específicas — usar trait separadaFromExtractorpara tipos de metadados/estado resolve o conflito. Arc::<dyn Any + Send + Sync>::downcast::<S>()disponível em stable Rust (≥1.51) — sem necessidade deas_any().- Macro
impl_handler_fn!com#[allow(non_snake_case)]usa nomes de tipo como variáveis no async block sempastecrate. - Ao adicionar campo a
BrokerMessage, buscar construções em todos os testes viagrep "BrokerMessage {".
US-5 — RetryPolicy
impl<B: Broker> Broker for Arc<B>é o padrão idiomático para compartilhamento de broker.tokio::time::sleeprequer a featuretime; adicioná-la como dep base evita#[cfg(feature = ...)]no código de retry.- DLQ:
with_dlq()no router tem precedência sobredead_letter()na policy.
US-6 — Macros #[subscriber] e #[publisher]
- Em proc-macros empilháveis, o outermost roda primeiro e recebe inner attributes via
func.attrs. O outer consome o inner; o inner como proc-macro standalone emitecompile_error!. - Para adiar captura de recurso runtime (broker) em código emitido pelo builder:
Box<dyn FnOnce(Recurso) -> BoxedHandler + Send>no estado da inscrição; resolve emattach. - Quando ItemFn é movido para dentro de
fn register, sua visibilidade precisa virarInherited— itens locais em fn não aceitampub.
US-7 — Detecção de runtime Lambda vs long-running
LambdaBrokerfora de qualquer feature gate: em Lambda o transporte é resolvido pelo runtime AWS, rdkafka não precisa compilar — preserva cold start ARM64.aws_lambda_events::KafkaRecord.headerséVec<HashMap<String, Vec<i8>>>(note osi8); cast*b as u8converte paraVec<u8>.MillisecondTimestampderiva viaDerefparaDateTime<Utc>—timestamp_millis()resolve direto.
US-8 — AsyncAPI
schemars = "0.8"+serde_yaml = "0.9"são leves o suficiente para deps mandatórias sem violar invariante.- AsyncAPI 3.0 só aceita
action: send|receive(diferente do v2 que tinhasubscribe/publish). Command::Info { ... }em vez deCommand::Info(unit) exige atualizar todos os matches existentes — fácil de esquecer.
US-9 — kafka-wallet + docs
OnceLock::setretornaResult<(), T>—expectfalha seTnão implementaDebug; usarif is_err() { panic!(...) }.lambda_runtime::runexigeF::Error: Into<Diagnostic> + Debug;BrokerErrornão implementa — mapear com.map_err(|e| e.to_string()).#[publisher]funciona end-to-end com brokers bidirecionais; em Lambda,LambdaBrokeré sink-only — publicar resultado exige broker separado ou producer direto.