use crate::clock::{Clock, StaticClock, SystemClock};
use crate::error::{ErrorTrace, ServiceError};
use chrono::{DateTime, Utc};
use std::borrow::Cow;
use std::panic::Location;
use serde::{Serialize};
use thiserror::Error;
use tokio_util::sync::CancellationToken;
use shiny_configuration::Configuration;
use shiny_configuration::value::serializer::{SerializerError, ValueSerializer};
use crate::dyn_configuration::DynConfigurationKey;
pub struct Context {
clock: Box<dyn Clock>,
cancellation_token: Option<CancellationToken>,
configuration: Configuration,
}
impl Context {
pub fn new() -> Self {
Self {
clock: Box::new(SystemClock),
cancellation_token: None,
configuration: Configuration::default(),
}
}
pub fn set_cancellation_token(&mut self, cancellation_token: CancellationToken) {
self.cancellation_token = Some(cancellation_token);
}
pub fn is_cancelled(&self) -> bool {
match &self.cancellation_token {
None => false,
Some(token) => token.is_cancelled(),
}
}
pub fn now(&self) -> DateTime<Utc> {
self.clock.now()
}
pub fn set_clock(&mut self, clock: impl Clock + 'static) {
self.clock = Box::new(clock);
}
pub fn set_static_time(&mut self, time: DateTime<Utc>) {
self.set_clock(StaticClock(time));
}
pub fn configuration<T: DynConfigurationKey>(&self, key: T) -> Result<T::Value, ServiceError> {
self.configuration.get(key.path()).map_err(|err| self.internal_error("FAILED_TO_GET_DYN_CONFIG_VALUE").with_error(err))
}
pub fn set_configuration(&mut self, configuration: Configuration) {
self.configuration = configuration
}
pub fn override_configuration<T: DynConfigurationKey>(&mut self, key: T, value: impl Into<T::Value>) -> Result<(), OverrideConfigurationError> {
self.configuration = self.configuration.with_override(key.path(), value.into().serialize(ValueSerializer)?);
Ok(())
}
#[track_caller]
pub fn internal_error(&self, code: impl Into<Cow<'static, str>>) -> ServiceError {
ServiceError::internal_error(code.into(), None, ErrorTrace::Location(Location::caller()))
}
#[track_caller]
pub fn dependency_error(
&self,
dependency_name: impl Into<Cow<'static, str>>,
code: impl Into<Cow<'static, str>>,
) -> ServiceError {
ServiceError::dependency_error(
dependency_name.into(),
code.into(),
None,
ErrorTrace::Location(Location::caller()),
)
}
}
#[derive(Debug, Error)]
#[error(transparent)]
pub struct OverrideConfigurationError(#[from] SerializerError);
pub trait ContextFactory: Send + Sync {
fn create(&self) -> Context;
}