diff --git a/Cargo.lock b/Cargo.lock index 7f6713615..e44fe5006 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,7 +179,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -190,7 +190,7 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -210,7 +210,7 @@ dependencies = [ "manyhow", "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -226,7 +226,7 @@ dependencies = [ "proc-macro2", "quote", "quote-use", - "syn 2.0.77", + "syn", ] [[package]] @@ -393,7 +393,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.77", + "syn", "which", ] @@ -569,7 +569,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -665,7 +665,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.77", + "syn", ] [[package]] @@ -676,7 +676,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -703,7 +703,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -878,9 +878,9 @@ name = "fpx-macros" version = "0.1.0" dependencies = [ "attribute-derive", - "proc-macro-error", + "manyhow", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -959,7 +959,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -1654,7 +1654,7 @@ dependencies = [ "manyhow-macros", "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -1968,7 +1968,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2005,31 +2005,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.77", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", + "syn", ] [[package]] @@ -2093,7 +2069,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2106,7 +2082,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2186,7 +2162,7 @@ dependencies = [ "proc-macro-utils 0.8.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2473,7 +2449,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.77", + "syn", ] [[package]] @@ -2544,7 +2520,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2555,7 +2531,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2628,7 +2604,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2728,7 +2704,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn", ] [[package]] @@ -2737,16 +2713,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.77" @@ -2791,7 +2757,7 @@ checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2811,7 +2777,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -2907,7 +2873,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -3180,7 +3146,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] @@ -3311,7 +3277,7 @@ dependencies = [ "lazy_format", "proc-macro2", "quote", - "syn 2.0.77", + "syn", "thiserror", ] @@ -3433,7 +3399,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn", "wasm-bindgen-shared", ] @@ -3467,7 +3433,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3731,7 +3697,7 @@ dependencies = [ "async-trait", "proc-macro2", "quote", - "syn 2.0.77", + "syn", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-macro-support", @@ -3784,7 +3750,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn", ] [[package]] diff --git a/fpx-lib/src/api/handlers/spans.rs b/fpx-lib/src/api/handlers/spans.rs index bd8668f11..130844e27 100644 --- a/fpx-lib/src/api/handlers/spans.rs +++ b/fpx-lib/src/api/handlers/spans.rs @@ -1,5 +1,6 @@ use crate::api::errors::{ApiServerError, CommonError}; use crate::api::models::Span; +use crate::data::models::HexEncodedId; use crate::data::{BoxedStore, DbError}; use axum::extract::{Path, State}; use axum::Json; @@ -12,14 +13,10 @@ use tracing::error; #[tracing::instrument(skip_all)] pub async fn span_get_handler( State(store): State, - Path((trace_id, span_id)): Path<(String, String)>, + Path((trace_id, span_id)): Path<(HexEncodedId, HexEncodedId)>, ) -> Result, ApiServerError> { let tx = store.start_readonly_transaction().await?; - hex::decode(&trace_id) - .map_err(|_| ApiServerError::ServiceError(SpanGetError::InvalidTraceId))?; - hex::decode(&span_id).map_err(|_| ApiServerError::ServiceError(SpanGetError::InvalidSpanId))?; - let span = store.span_get(&tx, &trace_id, &span_id).await?; Ok(Json(span.into())) @@ -32,14 +29,6 @@ pub enum SpanGetError { #[api_error(status_code = StatusCode::NOT_FOUND)] #[error("Span not found")] SpanNotFound, - - #[api_error(status_code = StatusCode::BAD_REQUEST)] - #[error("Trace ID is invalid")] - InvalidTraceId, - - #[api_error(status_code = StatusCode::BAD_REQUEST)] - #[error("Span ID is invalid")] - InvalidSpanId, } impl From for ApiServerError { @@ -57,13 +46,10 @@ impl From for ApiServerError { #[tracing::instrument(skip_all)] pub async fn span_list_handler( State(store): State, - Path(trace_id): Path, + Path(trace_id): Path, ) -> Result>, ApiServerError> { let tx = store.start_readonly_transaction().await?; - hex::decode(&trace_id) - .map_err(|_| ApiServerError::ServiceError(SpanListError::InvalidTraceId))?; - let spans = store.span_list_by_trace(&tx, &trace_id).await?; let spans: Vec<_> = spans.into_iter().map(Into::into).collect(); @@ -73,11 +59,7 @@ pub async fn span_list_handler( #[derive(Debug, Serialize, Deserialize, Error, ApiError)] #[serde(tag = "error", content = "details", rename_all = "camelCase")] #[non_exhaustive] -pub enum SpanListError { - #[api_error(status_code = StatusCode::BAD_REQUEST)] - #[error("Trace ID is invalid")] - InvalidTraceId, -} +pub enum SpanListError {} impl From for ApiServerError { fn from(err: DbError) -> Self { @@ -89,15 +71,10 @@ impl From for ApiServerError { #[tracing::instrument(skip_all)] pub async fn span_delete_handler( State(store): State, - Path((trace_id, span_id)): Path<(String, String)>, + Path((trace_id, span_id)): Path<(HexEncodedId, HexEncodedId)>, ) -> Result> { let tx = store.start_readonly_transaction().await?; - hex::decode(&trace_id) - .map_err(|_| ApiServerError::ServiceError(SpanDeleteError::InvalidTraceId))?; - hex::decode(&span_id) - .map_err(|_| ApiServerError::ServiceError(SpanDeleteError::InvalidSpanId))?; - store.span_delete(&tx, &trace_id, &span_id).await?; Ok(StatusCode::NO_CONTENT) @@ -106,15 +83,7 @@ pub async fn span_delete_handler( #[derive(Debug, Serialize, Deserialize, Error, ApiError)] #[serde(tag = "error", content = "details", rename_all = "camelCase")] #[non_exhaustive] -pub enum SpanDeleteError { - #[api_error(status_code = StatusCode::BAD_REQUEST)] - #[error("Trace ID is invalid")] - InvalidTraceId, - - #[api_error(status_code = StatusCode::BAD_REQUEST)] - #[error("Trace ID is invalid")] - InvalidSpanId, -} +pub enum SpanDeleteError {} impl From for ApiServerError { fn from(err: DbError) -> Self { diff --git a/fpx-lib/src/api/handlers/traces.rs b/fpx-lib/src/api/handlers/traces.rs index dce512aa3..30bc8b48f 100644 --- a/fpx-lib/src/api/handlers/traces.rs +++ b/fpx-lib/src/api/handlers/traces.rs @@ -1,5 +1,6 @@ use crate::api::errors::{ApiServerError, CommonError}; use crate::api::models::TraceSummary; +use crate::data::models::HexEncodedId; use crate::data::{BoxedStore, DbError}; use axum::extract::{Path, State}; use axum::Json; @@ -44,17 +45,14 @@ impl From for ApiServerError { #[tracing::instrument(skip_all)] pub async fn traces_get_handler( State(store): State, - Path(trace_id): Path, + Path(trace_id): Path, ) -> Result, ApiServerError> { let tx = store.start_readonly_transaction().await?; - hex::decode(&trace_id) - .map_err(|_| ApiServerError::ServiceError(TraceGetError::InvalidTraceId))?; - // Retrieve all the spans that are associated with the trace let spans = store.span_list_by_trace(&tx, &trace_id).await?; - let trace = TraceSummary::from_spans(trace_id, spans).ok_or(TraceGetError::NotFound)?; + let trace = TraceSummary::from_spans(trace_id.into(), spans).ok_or(TraceGetError::NotFound)?; Ok(Json(trace)) } @@ -63,10 +61,6 @@ pub async fn traces_get_handler( #[serde(tag = "error", content = "details", rename_all = "camelCase")] #[non_exhaustive] pub enum TraceGetError { - #[api_error(status_code = StatusCode::BAD_REQUEST)] - #[error("Trace ID is invalid")] - InvalidTraceId, - #[api_error(status_code = StatusCode::NOT_FOUND)] #[error("Trace was not found")] NotFound, @@ -82,13 +76,10 @@ impl From for ApiServerError { #[tracing::instrument(skip_all)] pub async fn traces_delete_handler( State(store): State, - Path(trace_id): Path, + Path(trace_id): Path, ) -> Result> { let tx = store.start_readonly_transaction().await?; - hex::decode(&trace_id) - .map_err(|_| ApiServerError::ServiceError(TraceDeleteError::InvalidTraceId))?; - // Retrieve all the spans that are associated with the trace store.span_delete_by_trace(&tx, &trace_id).await?; @@ -98,11 +89,7 @@ pub async fn traces_delete_handler( #[derive(Debug, Serialize, Deserialize, Error, ApiError)] #[serde(tag = "error", content = "details", rename_all = "camelCase")] #[non_exhaustive] -pub enum TraceDeleteError { - #[api_error(status_code = StatusCode::BAD_REQUEST)] - #[error("Trace ID is invalid")] - InvalidTraceId, -} +pub enum TraceDeleteError {} impl From for ApiServerError { fn from(err: DbError) -> Self { diff --git a/fpx-lib/src/api/models/otel.rs b/fpx-lib/src/api/models/otel.rs index af9e7a2c4..c9c794347 100644 --- a/fpx-lib/src/api/models/otel.rs +++ b/fpx-lib/src/api/models/otel.rs @@ -1,3 +1,4 @@ +use crate::data::models::HexEncodedId; use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; use opentelemetry_proto::tonic::common::v1::{any_value, KeyValue, KeyValueList}; use opentelemetry_proto::tonic::trace::v1::span::{Event, Link}; @@ -17,9 +18,9 @@ fn parse_time_nanos(nanos: u64) -> time::OffsetDateTime { #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct Span { - pub trace_id: String, - pub span_id: String, - pub parent_span_id: Option, + pub trace_id: HexEncodedId, + pub span_id: HexEncodedId, + pub parent_span_id: Option, pub name: String, pub trace_state: Option, @@ -74,14 +75,14 @@ impl Span { let parent_span_id = if span.parent_span_id.is_empty() { None } else { - Some(hex::encode(span.parent_span_id)) + Some(span.parent_span_id.into()) }; let events: Vec<_> = span.events.into_iter().map(Into::into).collect(); let links: Vec<_> = span.links.into_iter().map(Into::into).collect(); - let trace_id = hex::encode(span.trace_id); - let span_id = hex::encode(span.span_id); + let trace_id = span.trace_id.into(); + let span_id = span.span_id.into(); let name = span.name; let trace_state = Some(span.trace_state); @@ -295,14 +296,17 @@ impl From for AttributeValue #[serde(rename_all = "camelCase")] pub struct TraceSummary { /// The trace id - pub trace_id: String, + pub trace_id: HexEncodedId, /// The spans that are part of this trace pub spans: Vec, } impl TraceSummary { - pub fn from_spans(trace_id: String, spans: Vec) -> Option { + pub fn from_spans( + trace_id: HexEncodedId, + spans: Vec, + ) -> Option { if spans.is_empty() { return None; } diff --git a/fpx-lib/src/data.rs b/fpx-lib/src/data.rs index ff64e16e5..a399ade7e 100644 --- a/fpx-lib/src/data.rs +++ b/fpx-lib/src/data.rs @@ -1,3 +1,4 @@ +use crate::data::models::HexEncodedId; use crate::events::ServerEvents; use async_trait::async_trait; use std::sync::Arc; @@ -48,14 +49,14 @@ pub trait Store: Send + Sync { async fn span_get( &self, tx: &Transaction, - trace_id: &str, - span_id: &str, + trace_id: &HexEncodedId, + span_id: &HexEncodedId, ) -> Result; async fn span_list_by_trace( &self, tx: &Transaction, - trace_id: &str, + trace_id: &HexEncodedId, ) -> Result>; async fn span_create(&self, tx: &Transaction, span: models::Span) -> Result; @@ -71,13 +72,17 @@ pub trait Store: Send + Sync { ) -> Result>; /// Delete all spans with a specific trace_id. - async fn span_delete_by_trace(&self, tx: &Transaction, trace_id: &str) -> Result>; + async fn span_delete_by_trace( + &self, + tx: &Transaction, + trace_id: &HexEncodedId, + ) -> Result>; /// Delete a single span. async fn span_delete( &self, tx: &Transaction, - trace_id: &str, - span_id: &str, + trace_id: &HexEncodedId, + span_id: &HexEncodedId, ) -> Result>; } diff --git a/fpx-lib/src/data/models.rs b/fpx-lib/src/data/models.rs index 28f32a81d..2d1545c42 100644 --- a/fpx-lib/src/data/models.rs +++ b/fpx-lib/src/data/models.rs @@ -1,19 +1,23 @@ use crate::api; use crate::api::models::SpanKind; use crate::data::util::{Json, Timestamp}; -use serde::Deserialize; +use serde::de::{Error, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use std::fmt::Formatter; +use std::ops::Deref; +use std::str::FromStr; /// A computed value based on the span objects that are present. #[derive(Clone, Debug, Deserialize)] pub struct Trace { - pub trace_id: String, + pub trace_id: HexEncodedId, } #[derive(Clone, Debug, Deserialize, PartialEq)] pub struct Span { - pub trace_id: String, - pub span_id: String, - pub parent_span_id: Option, + pub trace_id: HexEncodedId, + pub span_id: HexEncodedId, + pub parent_span_id: Option, pub name: String, pub kind: SpanKind, @@ -51,10 +55,12 @@ impl From for Span { let end_time = span.end_time.into(); let inner = Json(span); + // these .unwrap are safe as these are guaranteed to be valid as they come from the api `Span` Self { - trace_id, - span_id, - parent_span_id, + trace_id: HexEncodedId::new(trace_id).unwrap(), + span_id: HexEncodedId::new(span_id).unwrap(), + parent_span_id: parent_span_id + .map(|parent_span_id| HexEncodedId::new(parent_span_id).unwrap()), name, kind, start_time, @@ -63,3 +69,128 @@ impl From for Span { } } } + +#[derive(Clone, Debug, Serialize, PartialEq)] +pub struct HexEncodedId(String); + +impl HexEncodedId { + pub fn new(input: impl Into) -> Result { + let id = HexEncodedId(input.into()); + id.validate()?; + + Ok(id) + } + + pub fn validate(&self) -> Result<(), hex::FromHexError> { + hex::decode(&self.0).map(|_| ()) + } + + pub fn into_inner(self) -> String { + self.0 + } + + pub fn as_inner(&self) -> &str { + &self.0 + } + + pub fn as_mut(&mut self) -> &mut str { + &mut self.0 + } +} + +impl From for String { + fn from(value: HexEncodedId) -> Self { + value.0 + } +} + +impl FromStr for HexEncodedId { + type Err = hex::FromHexError; + + fn from_str(s: &str) -> Result { + HexEncodedId::new(s) + } +} + +impl Deref for HexEncodedId { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From> for HexEncodedId { + fn from(value: Vec) -> Self { + // .unwrap() is safe because we literally encode it to hex in the exact same line + Self::new(hex::encode(value)).unwrap() + } +} + +#[cfg(feature = "libsql")] +impl From for libsql::Value { + fn from(value: HexEncodedId) -> Self { + value.into_inner().into() + } +} + +#[cfg(feature = "libsql")] +impl From<&HexEncodedId> for libsql::Value { + fn from(value: &HexEncodedId) -> Self { + value.as_inner().into() + } +} + +#[cfg(feature = "wasm-bindgen")] +impl From for wasm_bindgen::JsValue { + fn from(value: HexEncodedId) -> Self { + (&value).into() + } +} + +#[cfg(feature = "wasm-bindgen")] +impl From<&HexEncodedId> for wasm_bindgen::JsValue { + fn from(value: &HexEncodedId) -> Self { + wasm_bindgen::JsValue::from_str(value.as_inner()) + } +} + +struct HexEncodedIdVisitor; + +impl<'de> Visitor<'de> for HexEncodedIdVisitor { + type Value = HexEncodedId; + + fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { + formatter.write_str("a hex-represented string") + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + HexEncodedId::new(v).map_err(Error::custom) + } + + fn visit_borrowed_str(self, v: &'de str) -> Result + where + E: Error, + { + HexEncodedId::new(v).map_err(Error::custom) + } + + fn visit_string(self, v: String) -> Result + where + E: Error, + { + HexEncodedId::new(v).map_err(Error::custom) + } +} + +impl<'de> Deserialize<'de> for HexEncodedId { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_string(HexEncodedIdVisitor) + } +} diff --git a/fpx-macros/Cargo.toml b/fpx-macros/Cargo.toml index 782423944..447065a4c 100644 --- a/fpx-macros/Cargo.toml +++ b/fpx-macros/Cargo.toml @@ -13,6 +13,6 @@ proc-macro = true [dependencies] attribute-derive = "0.10.1" -syn = { version = "2.0.77", features = ["full"] } -proc-macro-error = "1.0.4" +manyhow = "0.11.4" quote = "1.0.37" +syn = { version = "2.0.77", features = ["full"] } diff --git a/fpx-macros/src/lib.rs b/fpx-macros/src/lib.rs index 6fec741a3..e774a7f93 100644 --- a/fpx-macros/src/lib.rs +++ b/fpx-macros/src/lib.rs @@ -1,8 +1,8 @@ use attribute_derive::FromAttr; +use manyhow::{manyhow, Emitter, ErrorMessage}; use proc_macro::TokenStream; -use proc_macro_error::{abort_call_site, proc_macro_error}; use quote::quote; -use syn::{parse_macro_input, Data, DeriveInput, Expr}; +use syn::{Data, DeriveInput, Expr}; #[derive(FromAttr)] #[attribute(ident = api_error)] @@ -11,27 +11,31 @@ struct ApiErrorAttribute { status_code: Expr, } +#[manyhow] #[proc_macro_derive(ApiError, attributes(api_error))] -#[proc_macro_error] -pub fn derive_api_error(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); +pub fn derive_api_error(input: TokenStream, emitter: &mut Emitter) -> manyhow::Result { + let input: DeriveInput = syn::parse(input)?; let Data::Enum(data) = &input.data else { - abort_call_site!("`ApiError` derive is only supported on enums"); + emitter.emit(ErrorMessage::call_site( + "`ApiError` derive is only supported on enums", + )); + return Err(emitter.into_result().unwrap_err()); }; let struct_ident = &input.ident; let mut variants = vec![]; if data.variants.is_empty() { - return (quote! { + return Ok((quote! { + #[automatically_derived] impl crate::api::errors::ApiError for #struct_ident { fn status_code(&self) -> http::StatusCode { http::StatusCode::INTERNAL_SERVER_ERROR } } }) - .into(); + .into()); } for variant in &data.variants { @@ -45,7 +49,8 @@ pub fn derive_api_error(input: TokenStream) -> TokenStream { }); } - (quote! { + Ok((quote! { + #[automatically_derived] impl crate::api::errors::ApiError for #struct_ident { fn status_code(&self) -> http::StatusCode { match self { @@ -54,5 +59,5 @@ pub fn derive_api_error(input: TokenStream) -> TokenStream { } } }) - .into() + .into()) } diff --git a/fpx-workers/src/data.rs b/fpx-workers/src/data.rs index 52dfa289a..33949d6c4 100644 --- a/fpx-workers/src/data.rs +++ b/fpx-workers/src/data.rs @@ -1,4 +1,5 @@ use axum::async_trait; +use fpx_lib::data::models::HexEncodedId; use fpx_lib::data::sql::SqlBuilder; use fpx_lib::data::{models, DbError, Result, Store, Transaction}; use serde::Deserialize; @@ -83,8 +84,8 @@ impl Store for D1Store { async fn span_get( &self, _tx: &Transaction, - trace_id: &str, - span_id: &str, + trace_id: &HexEncodedId, + span_id: &HexEncodedId, ) -> Result { SendFuture::new(async { self.fetch_one( @@ -99,7 +100,7 @@ impl Store for D1Store { async fn span_list_by_trace( &self, _tx: &Transaction, - trace_id: &str, + trace_id: &HexEncodedId, ) -> Result> { SendFuture::new(async { self.fetch_all(self.sql_builder.span_list_by_trace(), &[trace_id.into()]) @@ -115,7 +116,7 @@ impl Store for D1Store { ) -> Result { SendFuture::new(async { let parent_span = match span.parent_span_id { - Some(val) => val.into(), + Some(val) => val.into_inner().into(), None => JsValue::null(), }; @@ -146,7 +147,7 @@ impl Store for D1Store { &self, _tx: &Transaction, // Future improvement could hold sort fields, limits, etc - ) -> Result> { + ) -> Result> { SendFuture::new(async { let traces = self .fetch_all(self.sql_builder.traces_list(None), &[]) @@ -158,7 +159,11 @@ impl Store for D1Store { } /// Delete all spans with a specific trace_id. - async fn span_delete_by_trace(&self, _tx: &Transaction, trace_id: &str) -> Result> { + async fn span_delete_by_trace( + &self, + _tx: &Transaction, + trace_id: &HexEncodedId, + ) -> Result> { SendFuture::new(async { let prepared_statement = self .database @@ -188,8 +193,8 @@ impl Store for D1Store { async fn span_delete( &self, _tx: &Transaction, - trace_id: &str, - span_id: &str, + trace_id: &HexEncodedId, + span_id: &HexEncodedId, ) -> Result> { SendFuture::new(async { let prepared_statement = self diff --git a/fpx/src/data.rs b/fpx/src/data.rs index d114abc69..b70da418e 100644 --- a/fpx/src/data.rs +++ b/fpx/src/data.rs @@ -1,6 +1,6 @@ use anyhow::Context; use async_trait::async_trait; -use fpx_lib::data::models::Span; +use fpx_lib::data::models::{HexEncodedId, Span}; use fpx_lib::data::sql::SqlBuilder; use fpx_lib::data::{DbError, Result, Store, Transaction}; use libsql::{params, Builder, Connection}; @@ -116,7 +116,12 @@ impl Store for LibsqlStore { Ok(()) } - async fn span_get(&self, _tx: &Transaction, trace_id: &str, span_id: &str) -> Result { + async fn span_get( + &self, + _tx: &Transaction, + trace_id: &HexEncodedId, + span_id: &HexEncodedId, + ) -> Result { let span = self .connection .query(&self.sql_builder.span_get(), (trace_id, span_id)) @@ -127,7 +132,11 @@ impl Store for LibsqlStore { Ok(span) } - async fn span_list_by_trace(&self, _tx: &Transaction, trace_id: &str) -> Result> { + async fn span_list_by_trace( + &self, + _tx: &Transaction, + trace_id: &HexEncodedId, + ) -> Result> { let spans = self .connection .query(&self.sql_builder.span_list_by_trace(), params!(trace_id)) @@ -182,7 +191,11 @@ impl Store for LibsqlStore { } /// Delete all spans with a specific trace_id. - async fn span_delete_by_trace(&self, _tx: &Transaction, trace_id: &str) -> Result> { + async fn span_delete_by_trace( + &self, + _tx: &Transaction, + trace_id: &HexEncodedId, + ) -> Result> { let rows_affected = self .connection .execute(&self.sql_builder.span_delete_by_trace(), params!(trace_id)) @@ -195,8 +208,8 @@ impl Store for LibsqlStore { async fn span_delete( &self, _tx: &Transaction, - trace_id: &str, - span_id: &str, + trace_id: &HexEncodedId, + span_id: &HexEncodedId, ) -> Result> { let rows_affected = self .connection diff --git a/fpx/src/data/tests.rs b/fpx/src/data/tests.rs index cefc3a8f4..32f98a4e4 100644 --- a/fpx/src/data/tests.rs +++ b/fpx/src/data/tests.rs @@ -1,6 +1,6 @@ use crate::data::LibsqlStore; use fpx_lib::api::models::{AttributeMap, SpanKind}; -use fpx_lib::data::models::Span; +use fpx_lib::data::models::{HexEncodedId, Span}; use fpx_lib::data::Store; use test_log::test; @@ -21,8 +21,8 @@ async fn span_successful() { .await .expect("unable to create transaction"); - let trace_id = String::from("2b76e003e3cff12e054bcd0ca6879ee4"); - let span_id = String::from("a6c0ed7c2f81e7c8"); + let trace_id = HexEncodedId::new("2b76e003e3cff12e054bcd0ca6879ee4").unwrap(); + let span_id = HexEncodedId::new("a6c0ed7c2f81e7c8").unwrap(); let now = time::OffsetDateTime::now_utc(); let inner_span: fpx_lib::api::models::Span = fpx_lib::api::models::Span { @@ -33,7 +33,7 @@ async fn span_successful() { kind: Some(SpanKind::Internal), start_time: now, end_time: now, - trace_state: Some(String::from("")), + trace_state: Some(String::new()), flags: Some(0), scope_name: None, scope_version: None,