azure_functions/
context.rs

1//! Module for function invocation context.
2use std::{cell::RefCell, env, path::PathBuf};
3
4pub(crate) const UNKNOWN_FUNCTION: &str = "<unknown>";
5
6thread_local!(pub(crate) static CURRENT: RefCell<Context> = RefCell::new(
7    Context{
8        invocation_id: String::new(),
9        function_id: String::new(),
10        function_name: UNKNOWN_FUNCTION
11    }
12));
13
14/// Represents context about an Azure Function invocation.
15#[derive(Debug, Clone)]
16pub struct Context {
17    pub(crate) invocation_id: String,
18    pub(crate) function_id: String,
19    pub(crate) function_name: &'static str,
20}
21
22pub(crate) struct ContextGuard;
23
24impl Drop for ContextGuard {
25    fn drop(&mut self) {
26        Context::clear();
27    }
28}
29
30impl Context {
31    /// Gets the current invocation context.
32    ///
33    /// Returns None if there is no invocation context.
34    pub fn current() -> Option<Self> {
35        let mut current = None;
36
37        CURRENT.with(|c| {
38            let c = c.borrow();
39            if !c.invocation_id.is_empty() {
40                current = Some(c.clone());
41            }
42        });
43
44        current
45    }
46
47    #[must_use]
48    pub(crate) fn set(
49        invocation_id: &str,
50        function_id: &str,
51        function_name: &'static str,
52    ) -> ContextGuard {
53        CURRENT.with(|c| {
54            let mut c = c.borrow_mut();
55            c.invocation_id.replace_range(.., invocation_id);
56            c.function_id.replace_range(.., function_id);
57            c.function_name = function_name;
58        });
59
60        ContextGuard {}
61    }
62
63    pub(crate) fn clear() {
64        CURRENT.with(|c| {
65            let mut c = c.borrow_mut();
66            c.invocation_id.clear();
67            c.function_id.clear();
68            c.function_name = UNKNOWN_FUNCTION;
69        });
70    }
71
72    /// Gets the invocation identifier for the current Azure Function.
73    pub fn invocation_id(&self) -> &str {
74        &self.invocation_id
75    }
76
77    /// Gets the function identifier for the current Azure Function.
78    pub fn function_id(&self) -> &str {
79        &self.function_id
80    }
81
82    /// Gets the name of the current Azure Function.
83    pub fn function_name(&self) -> &str {
84        self.function_name
85    }
86
87    /// Gets the directory for the current Azure Function.
88    pub fn function_directory(&self) -> Option<PathBuf> {
89        self.app_directory().map(|p| p.join(self.function_name))
90    }
91
92    /// Gets the directory for the current Azure Function Application.
93    pub fn app_directory(&self) -> Option<PathBuf> {
94        env::current_exe()
95            .map(|p| p.parent().map(ToOwned::to_owned))
96            .ok()
97            .unwrap_or(None)
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn it_returns_none_without_context() {
107        assert_eq!(Context::current().is_none(), true);
108    }
109
110    #[test]
111    fn it_returns_current_context() {
112        let _guard = Context::set("1234", "5678", "foo");
113
114        let context = Context::current().unwrap();
115
116        assert_eq!(context.invocation_id(), "1234");
117        assert_eq!(context.function_id(), "5678");
118        assert_eq!(context.function_name(), "foo");
119    }
120}