215 lines
8.8 KiB
Rust
215 lines
8.8 KiB
Rust
// file: crates/ksp-app-solprices-desk/src/splash.rs
|
|
// version: 1
|
|
|
|
//! Common splash settings and frontend event contracts for SOL Prices Desk.
|
|
|
|
use ts_rs::TS; // rust-rules: trait-import
|
|
|
|
const DEFAULT_SPLASH_FADE_IN_MS: u32 = 300;
|
|
const DEFAULT_SPLASH_FADE_OUT_MS: u32 = 300;
|
|
const DEFAULT_SPLASH_MINIMUM_MS: u64 = 1200;
|
|
const ENV_SPLASH_FADE_IN_MS: &str = "KSP_DESK_SPLASH_FADE_IN_MS";
|
|
const ENV_SPLASH_FADE_OUT_MS: &str = "KSP_DESK_SPLASH_FADE_OUT_MS";
|
|
const ENV_SPLASH_MINIMUM_MS: &str = "KSP_DESK_SPLASH_MINIMUM_MS";
|
|
const MAX_SPLASH_FADE_MS: u32 = 10_000;
|
|
const MAX_SPLASH_MINIMUM_MS: u64 = 60_000;
|
|
|
|
/// Runtime timings used by the common desk splash lifecycle.
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub(crate) struct SplashSettings {
|
|
fade_in_ms: u32,
|
|
minimum_ms: u64,
|
|
fade_out_ms: u32,
|
|
fade_in_source: ksp_config_lib::ConfigEnvironmentSource,
|
|
minimum_source: ksp_config_lib::ConfigEnvironmentSource,
|
|
fade_out_source: ksp_config_lib::ConfigEnvironmentSource,
|
|
}
|
|
|
|
impl SplashSettings {
|
|
/// Resolves splash timings through the Config-owned environment snapshot.
|
|
pub(crate) fn load() -> ksp_core_lib::Result<Self> {
|
|
let environment = ksp_config_lib::ConfigEnvironment::load();
|
|
let environment = match environment {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let fade_in = environment.resolve_variable(ENV_SPLASH_FADE_IN_MS, std::option::Option::Some("300"));
|
|
let fade_in = match fade_in {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let minimum = environment.resolve_variable(ENV_SPLASH_MINIMUM_MS, std::option::Option::Some("1200"));
|
|
let minimum = match minimum {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let fade_out = environment.resolve_variable(ENV_SPLASH_FADE_OUT_MS, std::option::Option::Some("300"));
|
|
let fade_out = match fade_out {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let fade_in_ms = parse_u32_setting(ENV_SPLASH_FADE_IN_MS, fade_in.value(), MAX_SPLASH_FADE_MS);
|
|
let fade_in_ms = match fade_in_ms {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let minimum_ms = parse_u64_setting(ENV_SPLASH_MINIMUM_MS, minimum.value(), MAX_SPLASH_MINIMUM_MS);
|
|
let minimum_ms = match minimum_ms {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let fade_out_ms = parse_u32_setting(ENV_SPLASH_FADE_OUT_MS, fade_out.value(), MAX_SPLASH_FADE_MS);
|
|
let fade_out_ms = match fade_out_ms {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
return std::result::Result::Ok(Self {
|
|
fade_in_ms,
|
|
minimum_ms,
|
|
fade_out_ms,
|
|
fade_in_source: fade_in.source(),
|
|
minimum_source: minimum.source(),
|
|
fade_out_source: fade_out.source(),
|
|
});
|
|
}
|
|
|
|
/// Returns safe in-memory timings used when the managed environment cannot be resolved.
|
|
#[must_use]
|
|
pub(crate) const fn fallback() -> Self {
|
|
return Self {
|
|
fade_in_ms: DEFAULT_SPLASH_FADE_IN_MS,
|
|
minimum_ms: DEFAULT_SPLASH_MINIMUM_MS,
|
|
fade_out_ms: DEFAULT_SPLASH_FADE_OUT_MS,
|
|
fade_in_source: ksp_config_lib::ConfigEnvironmentSource::Fallback,
|
|
minimum_source: ksp_config_lib::ConfigEnvironmentSource::Fallback,
|
|
fade_out_source: ksp_config_lib::ConfigEnvironmentSource::Fallback,
|
|
};
|
|
}
|
|
|
|
/// Returns the configured fade-in duration.
|
|
#[must_use]
|
|
pub(crate) const fn fade_in_ms(self) -> u32 {
|
|
return self.fade_in_ms;
|
|
}
|
|
|
|
/// Returns the minimum fully-visible splash duration.
|
|
#[must_use]
|
|
pub(crate) const fn minimum_ms(self) -> u64 {
|
|
return self.minimum_ms;
|
|
}
|
|
|
|
/// Returns the configured fade-out duration.
|
|
#[must_use]
|
|
pub(crate) const fn fade_out_ms(self) -> u32 {
|
|
return self.fade_out_ms;
|
|
}
|
|
|
|
/// Returns the safe provenance code for the fade-in duration.
|
|
#[must_use]
|
|
pub(crate) const fn fade_in_source(self) -> &'static str {
|
|
return environment_source_code(self.fade_in_source);
|
|
}
|
|
|
|
/// Returns the safe provenance code for the minimum duration.
|
|
#[must_use]
|
|
pub(crate) const fn minimum_source(self) -> &'static str {
|
|
return environment_source_code(self.minimum_source);
|
|
}
|
|
|
|
/// Returns the safe provenance code for the fade-out duration.
|
|
#[must_use]
|
|
pub(crate) const fn fade_out_source(self) -> &'static str {
|
|
return environment_source_code(self.fade_out_source);
|
|
}
|
|
|
|
/// Returns the expected additive backend lifecycle from readiness through fade-out.
|
|
#[must_use]
|
|
pub(crate) fn expected_backend_lifecycle_ms(self) -> u64 {
|
|
return u64::from(self.fade_in_ms) + self.minimum_ms + u64::from(self.fade_out_ms);
|
|
}
|
|
}
|
|
|
|
/// Command emitted by Rust to the splash frontend.
|
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_solprices_desk/splash/SplashOrderDto.ts")]
|
|
pub(crate) struct SplashOrderDto {
|
|
/// Splash action name.
|
|
pub(crate) action: String,
|
|
/// Optional status text.
|
|
pub(crate) message: std::option::Option<String>,
|
|
/// Optional message severity used by the bottom message feed.
|
|
pub(crate) status: std::option::Option<String>,
|
|
/// Optional animation duration in milliseconds.
|
|
pub(crate) duration_ms: std::option::Option<u32>,
|
|
}
|
|
|
|
impl SplashOrderDto {
|
|
/// Creates a new `SplashOrderDto` value.
|
|
#[must_use]
|
|
pub(crate) fn new(action: &str, message: std::option::Option<&str>, status: std::option::Option<&str>, duration_ms: std::option::Option<u32>) -> Self {
|
|
return Self {
|
|
action: action.to_owned(),
|
|
message: message.map(std::string::ToString::to_string),
|
|
status: status.map(std::string::ToString::to_string),
|
|
duration_ms,
|
|
};
|
|
}
|
|
}
|
|
|
|
const fn environment_source_code(source: ksp_config_lib::ConfigEnvironmentSource) -> &'static str {
|
|
return match source {
|
|
ksp_config_lib::ConfigEnvironmentSource::Process => "process",
|
|
ksp_config_lib::ConfigEnvironmentSource::DotEnv => "dotenv",
|
|
ksp_config_lib::ConfigEnvironmentSource::Fallback => "fallback",
|
|
};
|
|
}
|
|
|
|
fn parse_u64_setting(variable_name: &str, value: &str, maximum: u64) -> ksp_core_lib::Result<u64> {
|
|
let parsed = value.parse::<u64>();
|
|
let parsed = match parsed {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => {
|
|
return std::result::Result::Err(
|
|
ksp_core_lib::Error::new(crate::ERROR_CODE_SPLASH_SETTING_INVALID, "KSP desk splash duration is not a valid unsigned integer")
|
|
.with_context("variable_name", variable_name)
|
|
.with_source(error),
|
|
);
|
|
},
|
|
};
|
|
if parsed > maximum {
|
|
return std::result::Result::Err(
|
|
ksp_core_lib::Error::new(crate::ERROR_CODE_SPLASH_SETTING_INVALID, "KSP desk splash duration exceeds the allowed bound")
|
|
.with_context("variable_name", variable_name)
|
|
.with_context("maximum_ms", maximum.to_string()),
|
|
);
|
|
}
|
|
return std::result::Result::Ok(parsed);
|
|
}
|
|
|
|
fn parse_u32_setting(variable_name: &str, value: &str, maximum: u32) -> ksp_core_lib::Result<u32> {
|
|
let parsed = value.parse::<u32>();
|
|
let parsed = match parsed {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => {
|
|
return std::result::Result::Err(
|
|
ksp_core_lib::Error::new(crate::ERROR_CODE_SPLASH_SETTING_INVALID, "KSP desk splash duration is not a valid unsigned integer")
|
|
.with_context("variable_name", variable_name)
|
|
.with_source(error),
|
|
);
|
|
},
|
|
};
|
|
if parsed > maximum {
|
|
return std::result::Result::Err(
|
|
ksp_core_lib::Error::new(crate::ERROR_CODE_SPLASH_SETTING_INVALID, "KSP desk splash duration exceeds the allowed bound")
|
|
.with_context("variable_name", variable_name)
|
|
.with_context("maximum_ms", maximum.to_string()),
|
|
);
|
|
}
|
|
return std::result::Result::Ok(parsed);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "../unit_tests/splash.rs"]
|
|
mod tests;
|