netsblox_vm/
std_util.rs

1//! A collection of helper types which depend on the standard library.
2//! 
3//! Note that this module is only available with the [`std`](crate) feature flag.
4
5use std::sync::{Arc, Mutex};
6
7use crate::real_time::*;
8use crate::runtime::*;
9use crate::json::*;
10use crate::vecmap::*;
11use crate::compact_str::*;
12use crate::*;
13
14pub struct NetsBloxContext {
15    pub base_url: CompactString,
16    pub client_id: CompactString,
17    pub default_service_host: CompactString,
18
19    pub project_name: CompactString,
20    pub project_id: CompactString,
21    pub role_name: CompactString,
22    pub role_id: CompactString,
23}
24pub struct RpcRequest<C: CustomTypes<S>, S: System<C>> {
25    pub host: Option<CompactString>,
26    pub service: CompactString,
27    pub rpc: CompactString,
28    pub args: VecMap<CompactString, Json, false>,
29    pub key: AsyncKey<Result<C::Intermediate, CompactString>>,
30}
31pub struct ReplyEntry {
32    pub expiry: OffsetDateTime,
33    pub value: Option<Json>,
34}
35
36struct ClockCache {
37    value: Mutex<OffsetDateTime>,
38    precision: Precision,
39}
40
41/// A clock with optional coarse granularity.
42pub struct Clock {
43    utc_offset: UtcOffset,
44    cache: Option<ClockCache>,
45}
46impl Clock {
47    /// Creates a new [`Clock`] with the specified [`UtcOffset`] and (optional) cache [`Precision`] (see [`Clock::read`]).
48    pub fn new(utc_offset: UtcOffset, cache_precision: Option<Precision>) -> Self {
49        let mut res = Self { utc_offset, cache: None };
50        if let Some(precision) = cache_precision {
51            res.cache = Some(ClockCache { value: Mutex::new(res.update()), precision });
52        }
53        res
54    }
55    /// Reads the current time with the specified level of precision.
56    /// If caching was enabled by [`Clock::new`], requests at or below the cache precision level will use the cached timestamp.
57    /// In any other case, the real current time is fetched by [`Clock::update`] and the result is stored in the cache if caching is enabled.
58    pub fn read(&self, precision: Precision) -> OffsetDateTime {
59        match &self.cache {
60            Some(cache) if precision <= cache.precision => *cache.value.lock().unwrap(),
61            _ => self.update(),
62        }
63    }
64    /// Reads the real world time and stores the result in the cache if caching was enabled by [`Clock::new`].
65    pub fn update(&self) -> OffsetDateTime {
66        let t = OffsetDateTime::now_utc().to_offset(self.utc_offset);
67        if let Some(cache) = &self.cache {
68            *cache.value.lock().unwrap() = t;
69        }
70        t
71    }
72}
73
74/// A [`Key`] type which wraps a shared [`AsyncResult`].
75#[derive(Educe)]
76#[educe(Clone)]
77pub struct AsyncKey<T>(Arc<Mutex<AsyncResult<T>>>);
78impl<T> AsyncKey<T> {
79    /// Equivalent to [`AsyncResult::new`] to create a new shared [`AsyncResult`].
80    pub fn new() -> Self {
81        Self(Arc::new(Mutex::new(AsyncResult::new())))
82    }
83    /// Equivalent to [`AsyncResult::poll`] on the shared [`AsyncResult`].
84    pub fn poll(&self) -> AsyncResult<T> {
85        self.0.lock().unwrap().poll()
86    }
87}
88impl<T> Key<T> for AsyncKey<T> {
89    /// Equivalent to [`AsyncResult::complete`] on the shared [`AsyncResult`]
90    fn complete(self, value: T) {
91        assert!(self.0.lock().unwrap().complete(value).is_ok())
92    }
93}