shors 0.12.6

Transport layer for cartridge + tarantool-module projects.
Documentation
use crate::tarantool::tlua;
use crate::tarantool::tlua::PushGuard;
use crate::tarantool::tlua::{TuplePushError, Void};
use crate::tarantool::tuple::Encode;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::Arc;

#[macro_use]
pub mod http;
pub mod rpc;

/// Context useful for store metadata (like request id) through request path in distributed system.
/// Features like tracing require context for for forwarding trace metadata between instances.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Context {
    data: HashMap<String, Arc<String>>,
}

impl Context {
    #[allow(clippy::new_without_default)]
    pub fn new() -> Self {
        Self {
            data: HashMap::default(),
        }
    }

    /// Create background context - with random generated request_id.
    pub fn background() -> Self {
        Self {
            data: HashMap::from([(
                "request_id".to_string(),
                Arc::new(format!(
                    "background {}",
                    crate::tarantool::uuid::Uuid::random(),
                )),
            )]),
        }
    }

    pub fn put(&mut self, key: impl Into<String>, value: impl Into<String>) {
        self.data.insert(key.into(), Arc::new(value.into()));
    }

    pub fn get(&self, key: &str) -> Option<&str> {
        self.data.get(key).map(|val| val.as_str())
    }

    pub fn endpoint_path(&self) -> &str {
        self.data
            .get("path")
            .map(|s| s.as_str())
            .unwrap_or("unknown")
    }

    pub fn enable_tracing(&mut self) {
        self.put("tracing", "on")
    }

    pub fn tracing_enabled(&self) -> bool {
        self.get("tracing").map(|v| v == "on").unwrap_or(false)
    }
}

impl Encode for Context {}

impl<L> tlua::Push<L> for Context
where
    L: tlua::AsLua + Debug,
{
    type Err = TuplePushError<Void, TuplePushError<Void, Void>>;

    fn push_to_lua(&self, lua: L) -> Result<PushGuard<L>, (Self::Err, L)> {
        HashMap::from([(
            "data",
            self.data
                .iter()
                .map(|(k, v)| (k.as_str(), v.as_str()))
                .collect::<HashMap<_, _>>(),
        )])
        .push_to_lua(lua)
    }
}

impl<L> tlua::PushOne<L> for Context where L: tlua::AsLua + Debug {}

mod otel {
    use super::Context;
    use opentelemetry::propagation::{Extractor, Injector};
    use opentelemetry::{global, Context as OTELContext};

    impl Injector for Context {
        fn set(&mut self, key: &str, value: String) {
            self.put(key, value);
        }
    }

    impl Extractor for Context {
        fn get(&self, key: &str) -> Option<&str> {
            self.get(key)
        }

        fn keys(&self) -> Vec<&str> {
            self.data.keys().map(|k| k.as_str()).collect()
        }
    }

    impl Context {
        pub fn inject(&mut self, ctx: &OTELContext) {
            global::get_text_map_propagator(|propagator| {
                propagator.inject_context(ctx, self);
            });
        }

        pub fn extract(&self) -> OTELContext {
            global::get_text_map_propagator(|propagator| propagator.extract(self))
        }
    }
}