Skip to main content

omnyssh_core/utils/
platform.rs

1use std::path::{Path, PathBuf};
2use std::time::{Duration, SystemTime};
3
4/// Rolling log files older than this many days are pruned at startup.
5pub const LOG_RETENTION_DAYS: u64 = 7;
6
7/// Prefix shared by every rolling log file (`omnyssh.log`, `omnyssh.log.YYYY-MM-DD`).
8const LOG_FILE_PREFIX: &str = "omnyssh.log";
9
10/// Returns the path to the user's SSH config file.
11///
12/// - Linux / macOS: `~/.ssh/config`
13/// - Windows:       `%USERPROFILE%\.ssh\config`
14///
15/// Uses the `dirs` crate so we never hardcode `~`.
16pub fn ssh_config_path() -> Option<PathBuf> {
17    dirs::home_dir().map(|h| h.join(".ssh").join("config"))
18}
19
20/// Returns the application config directory.
21///
22/// - Linux:   `~/.config/omnyssh/`
23/// - macOS:   `~/Library/Application Support/omnyssh/`
24/// - Windows: `%APPDATA%\omnyssh\`
25pub fn app_config_dir() -> Option<PathBuf> {
26    dirs::config_dir().map(|d| d.join("omnyssh"))
27}
28
29/// Returns the path to the main application config file.
30pub fn app_config_path() -> Option<PathBuf> {
31    app_config_dir().map(|d| d.join("config.toml"))
32}
33
34/// Returns the path to the hosts config file.
35pub fn hosts_config_path() -> Option<PathBuf> {
36    app_config_dir().map(|d| d.join("hosts.toml"))
37}
38
39/// Returns the path to the snippets config file.
40pub fn snippets_config_path() -> Option<PathBuf> {
41    app_config_dir().map(|d| d.join("snippets.toml"))
42}
43
44/// Removes rolling log files in `log_dir` older than `max_age_days`.
45///
46/// Best-effort and fault-tolerant: a missing directory, an unreadable entry,
47/// or a failed delete is skipped rather than propagated, so log cleanup never
48/// blocks application startup. File age is taken from the filesystem
49/// modification time, which keeps the logic identical on every OS. Only files
50/// whose name starts with `omnyssh.log` are considered, so config and other
51/// data files in the same directory are never touched.
52///
53/// Returns the number of files removed.
54pub fn cleanup_old_logs(log_dir: &Path, max_age_days: u64) -> usize {
55    let entries = match std::fs::read_dir(log_dir) {
56        Ok(entries) => entries,
57        Err(_) => return 0,
58    };
59
60    let max_age = Duration::from_secs(max_age_days * 24 * 60 * 60);
61    let now = SystemTime::now();
62    let mut removed = 0;
63
64    for entry in entries.flatten() {
65        let path = entry.path();
66
67        let is_log_file = path
68            .file_name()
69            .and_then(|name| name.to_str())
70            .map(|name| name.starts_with(LOG_FILE_PREFIX))
71            .unwrap_or(false);
72        if !is_log_file {
73            continue;
74        }
75
76        let modified = match entry.metadata().and_then(|meta| meta.modified()) {
77            Ok(modified) => modified,
78            Err(_) => continue,
79        };
80
81        // `duration_since` errors on files dated in the future (clock skew);
82        // treat those as recent and keep them.
83        if let Ok(age) = now.duration_since(modified) {
84            if age > max_age && std::fs::remove_file(&path).is_ok() {
85                removed += 1;
86            }
87        }
88    }
89
90    removed
91}