rust_observer_trace_macro 0.1.1

Trace macro dependency for rust observer
Documentation
```rust
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn, FnArg, PatType, Ident, Signature, Type};

fn is_trace_context_type(ty: &Type) -> bool {
    match ty {
        Type::Path(type_path) if type_path.qself.is_none() => {
            type_path.path.is_ident("TraceContext") || type_path.path.is_ident("OptionalTraceContext")
        },
        _ => false,
    }
}

fn find_context_arg(sig: &Signature) -> Ident {
    sig.inputs.iter().rev()
        .filter_map(|arg| {
            if let FnArg::Typed(pat_type) = arg {
                if is_trace_context_type(&pat_type.ty) {
                    if let syn::Pat::Ident(pat_ident) = &*pat_type.pat {
                        return Some(pat_ident.ident.clone());
                    }
                }
            }
            None
        })
        .next()
        .unwrap_or_else(|| Ident::new("ctx", proc_macro2::Span::call_site()))
}

#[proc_macro_attribute]
pub fn trace(attr: TokenStream, item: TokenStream) -> TokenStream {
    let input_fn = parse_macro_input!(item as ItemFn);

    let ctx_ident = find_context_arg(&input_fn.sig);

    let span_type = if attr.is_empty() {
        quote! { rust_observer::tracing::SpanType::Internal }
    } else {
        let span_type_ident = parse_macro_input!(attr as Ident);
        quote! { rust_observer::tracing::SpanType::#span_type_ident }
    };
    

    let name = &input_fn.sig.ident;
    let vis = &input_fn.vis;
    let asyncness = &input_fn.sig.asyncness;
    let output = &input_fn.sig.output;
    let block = &input_fn.block;

    let parsed_inputs: Vec<_> = input_fn.sig.inputs.iter().map(|i| {
        match i {
            FnArg::Typed(PatType { pat, ty, .. }) => quote! { #pat: #ty },
            _ => quote! { #i }
        }
    }).collect();

    let call_args: Vec<_> = input_fn.sig.inputs.iter().map(|i| {
        match i {
            FnArg::Typed(PatType { pat, .. }) => quote! { #pat.clone() },
            _ => quote! { #i }
        }
    }).collect();

    let result = if asyncness.is_some() {
        quote! {
            #vis #asyncness fn #name(#(#parsed_inputs),*) #output {
                let start_ts = rust_observer::chrono::Utc::now();
                let mut #ctx_ident = if #ctx_ident.as_ref().map_or(true, |ctx| ctx.trace_id.is_empty() || ctx.trace_id.len() != 32) {
                    rust_observer::tracing::TraceContext::new_optional(#span_type, stringify!(#name).to_string())
                } else {
                    Some(#ctx_ident.as_ref().unwrap().new_span(#span_type, stringify!(#name).to_string(), start_ts))
                };
                
                let function_name = stringify!(#name);
                // push trace to stdout/gRPC
                rust_observer::logging::info!(format!("Entering function: {function_name}"), #ctx_ident.clone());
                let start = std::time::Instant::now();

                #asyncness fn inner(#(#parsed_inputs),*) #output #block

                let result = inner(#(#call_args),*).await;

                let end_ts = rust_observer::chrono::Utc::now();
                #ctx_ident.as_mut().unwrap().end_span(end_ts);

                let duration = start.elapsed();
                rust_observer::logging::info!(format!("Exiting function: {function_name} took {duration:?}"), #ctx_ident.clone());

                result
            }
        }
    } else {
        quote! {
            #vis fn #name(#(#parsed_inputs),*) #output {
                let start_ts = rust_observer::chrono::Utc::now();
                let mut #ctx_ident = if #ctx_ident.as_ref().map_or(true, |ctx| ctx.trace_id.is_empty() || ctx.trace_id.len() != 32) {
                    rust_observer::tracing::TraceContext::new_optional(#span_type, stringify!(#name).to_string())
                } else {
                    Some(#ctx_ident.as_ref().unwrap().new_span(#span_type, stringify!(#name).to_string(), start_ts))
                };
                let function_name = stringify!(#name);
                
                rust_observer::logging::info!(format!("Entering function: {function_name}"), #ctx_ident.clone());
                let start = std::time::Instant::now();

                let result = (|| {
                    #block
                })();

                let end_ts = rust_observer::chrono::Utc::now();
                #ctx_ident.as_mut().unwrap().end_span(end_ts);

                let duration = start.elapsed();
                rust_observer::logging::info!(format!("Exiting function: {function_name} took {duration:?}"), #ctx_ident.clone());

                result
            }
        }
    };

    result.into()
}

```