mirror of https://github.com/telemt/telemt.git
fix exact uptime boundary formatting
This commit is contained in:
parent
e35d69c61f
commit
ac3ce13ac0
|
|
@ -12,6 +12,7 @@ use crate::transport::middle_proxy::{
|
||||||
ProxyConfigData, fetch_proxy_config_with_raw, load_proxy_config_cache, save_proxy_config_cache,
|
ProxyConfigData, fetch_proxy_config_with_raw, load_proxy_config_cache, save_proxy_config_cache,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Resolves the runtime config path relative to the startup working directory.
|
||||||
pub(crate) fn resolve_runtime_config_path(
|
pub(crate) fn resolve_runtime_config_path(
|
||||||
config_path_cli: &str,
|
config_path_cli: &str,
|
||||||
startup_cwd: &std::path::Path,
|
startup_cwd: &std::path::Path,
|
||||||
|
|
@ -25,6 +26,7 @@ pub(crate) fn resolve_runtime_config_path(
|
||||||
absolute.canonicalize().unwrap_or(absolute)
|
absolute.canonicalize().unwrap_or(absolute)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses CLI arguments and handles process-exiting utility commands.
|
||||||
pub(crate) fn parse_cli() -> (String, Option<PathBuf>, bool, Option<String>) {
|
pub(crate) fn parse_cli() -> (String, Option<PathBuf>, bool, Option<String>) {
|
||||||
let mut config_path = "config.toml".to_string();
|
let mut config_path = "config.toml".to_string();
|
||||||
let mut data_path: Option<PathBuf> = None;
|
let mut data_path: Option<PathBuf> = None;
|
||||||
|
|
@ -151,8 +153,22 @@ mod tests {
|
||||||
|
|
||||||
let _ = std::fs::remove_dir(&startup_cwd);
|
let _ = std::fs::remove_dir(&startup_cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format_uptime_includes_exact_unit_boundaries() {
|
||||||
|
assert_eq!(super::format_uptime(60), "1 minute, 0 seconds / 60 seconds");
|
||||||
|
assert_eq!(
|
||||||
|
super::format_uptime(3_600),
|
||||||
|
"1 hour, 0 minutes, 0 seconds / 3600 seconds"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
super::format_uptime(86_400),
|
||||||
|
"1 day, 0 hours, 0 minutes, 0 seconds / 86400 seconds"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Logs Telegram proxy links derived from the active runtime configuration.
|
||||||
pub(crate) fn print_proxy_links(host: &str, port: u16, config: &ProxyConfig) {
|
pub(crate) fn print_proxy_links(host: &str, port: u16, config: &ProxyConfig) {
|
||||||
info!(target: "telemt::links", "--- Proxy Links ({}) ---", host);
|
info!(target: "telemt::links", "--- Proxy Links ({}) ---", host);
|
||||||
for user_name in config
|
for user_name in config
|
||||||
|
|
@ -202,6 +218,7 @@ pub(crate) fn print_proxy_links(host: &str, port: u16, config: &ProxyConfig) {
|
||||||
info!(target: "telemt::links", "------------------------");
|
info!(target: "telemt::links", "------------------------");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Persists the beobachten snapshot payload and creates parent directories when needed.
|
||||||
pub(crate) async fn write_beobachten_snapshot(path: &str, payload: &str) -> std::io::Result<()> {
|
pub(crate) async fn write_beobachten_snapshot(path: &str, payload: &str) -> std::io::Result<()> {
|
||||||
if let Some(parent) = std::path::Path::new(path).parent()
|
if let Some(parent) = std::path::Path::new(path).parent()
|
||||||
&& !parent.as_os_str().is_empty()
|
&& !parent.as_os_str().is_empty()
|
||||||
|
|
@ -211,10 +228,12 @@ pub(crate) async fn write_beobachten_snapshot(path: &str, payload: &str) -> std:
|
||||||
tokio::fs::write(path, payload).await
|
tokio::fs::write(path, payload).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the singular or plural unit label for a numeric value.
|
||||||
pub(crate) fn unit_label(value: u64, singular: &'static str, plural: &'static str) -> &'static str {
|
pub(crate) fn unit_label(value: u64, singular: &'static str, plural: &'static str) -> &'static str {
|
||||||
if value == 1 { singular } else { plural }
|
if value == 1 { singular } else { plural }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Formats process uptime into a human-readable multi-unit string.
|
||||||
pub(crate) fn format_uptime(total_secs: u64) -> String {
|
pub(crate) fn format_uptime(total_secs: u64) -> String {
|
||||||
const SECS_PER_MINUTE: u64 = 60;
|
const SECS_PER_MINUTE: u64 = 60;
|
||||||
const SECS_PER_HOUR: u64 = 60 * SECS_PER_MINUTE;
|
const SECS_PER_HOUR: u64 = 60 * SECS_PER_MINUTE;
|
||||||
|
|
@ -235,23 +254,23 @@ pub(crate) fn format_uptime(total_secs: u64) -> String {
|
||||||
let seconds = remaining % SECS_PER_MINUTE;
|
let seconds = remaining % SECS_PER_MINUTE;
|
||||||
|
|
||||||
let mut parts = Vec::new();
|
let mut parts = Vec::new();
|
||||||
if total_secs > SECS_PER_YEAR {
|
if total_secs >= SECS_PER_YEAR {
|
||||||
parts.push(format!("{} {}", years, unit_label(years, "year", "years")));
|
parts.push(format!("{} {}", years, unit_label(years, "year", "years")));
|
||||||
}
|
}
|
||||||
if total_secs > SECS_PER_MONTH {
|
if total_secs >= SECS_PER_MONTH {
|
||||||
parts.push(format!(
|
parts.push(format!(
|
||||||
"{} {}",
|
"{} {}",
|
||||||
months,
|
months,
|
||||||
unit_label(months, "month", "months")
|
unit_label(months, "month", "months")
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if total_secs > SECS_PER_DAY {
|
if total_secs >= SECS_PER_DAY {
|
||||||
parts.push(format!("{} {}", days, unit_label(days, "day", "days")));
|
parts.push(format!("{} {}", days, unit_label(days, "day", "days")));
|
||||||
}
|
}
|
||||||
if total_secs > SECS_PER_HOUR {
|
if total_secs >= SECS_PER_HOUR {
|
||||||
parts.push(format!("{} {}", hours, unit_label(hours, "hour", "hours")));
|
parts.push(format!("{} {}", hours, unit_label(hours, "hour", "hours")));
|
||||||
}
|
}
|
||||||
if total_secs > SECS_PER_MINUTE {
|
if total_secs >= SECS_PER_MINUTE {
|
||||||
parts.push(format!(
|
parts.push(format!(
|
||||||
"{} {}",
|
"{} {}",
|
||||||
minutes,
|
minutes,
|
||||||
|
|
@ -267,6 +286,7 @@ pub(crate) fn format_uptime(total_secs: u64) -> String {
|
||||||
format!("{} / {} seconds", parts.join(", "), total_secs)
|
format!("{} / {} seconds", parts.join(", "), total_secs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Waits until admission opens or the watch channel closes.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) async fn wait_until_admission_open(admission_rx: &mut watch::Receiver<bool>) -> bool {
|
pub(crate) async fn wait_until_admission_open(admission_rx: &mut watch::Receiver<bool>) -> bool {
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -279,10 +299,12 @@ pub(crate) async fn wait_until_admission_open(admission_rx: &mut watch::Receiver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Identifies EOFs that match the expected empty-handshake shutdown path.
|
||||||
pub(crate) fn is_expected_handshake_eof(err: &crate::error::ProxyError) -> bool {
|
pub(crate) fn is_expected_handshake_eof(err: &crate::error::ProxyError) -> bool {
|
||||||
err.to_string().contains("expected 64 bytes, got 0")
|
err.to_string().contains("expected 64 bytes, got 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Loads the startup proxy-config snapshot, falling back to the on-disk cache when needed.
|
||||||
pub(crate) async fn load_startup_proxy_config_snapshot(
|
pub(crate) async fn load_startup_proxy_config_snapshot(
|
||||||
url: &str,
|
url: &str,
|
||||||
cache_path: Option<&str>,
|
cache_path: Option<&str>,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue