Published source
Download source ZIP
CH-J Tools
Browse directories and files for a specific application release.
CH-J / Server Manager
1704 files
0.1.2-beta
Added: · Built:
1
//! Helper settings in `<data>/standby-helper.json`, written by the application (Settings → Standby Helper).2
//! The file holds only switches; no secrets. A missing or broken file means "everything off".3
use serde_json::{json, Value};4
use std::path::{Path, PathBuf};6
pub const FILE: &str = "standby-helper.json";7
const APP_DIRECTORY_NAME: &str = "CH-J Tools";8
/// Folder of the application before its rename; the application moves it to `APP_DIRECTORY_NAME` at its first start.9
const LEGACY_APP_DIRECTORY_NAME: &str = "CH-J Server Manager";11
/// Same resolution as the application's `AppPaths::resolve`, so both always use the same data folder: the new folder,12
/// or the old one while the application has not moved it yet (the helper never moves it itself).13
pub fn data_dir() -> PathBuf {14
for var in ["CHJ_USER_DATA_DIR", "CHJ_BUILD_SMOKE_USER_DATA_DIR"] {15
if let Some(path) = std::env::var_os(var).map(PathBuf::from).filter(|p| p.is_absolute()) {16
return path;17
}18
}19
choose(&config_dir())20
}22
fn choose(base: &Path) -> PathBuf {23
let target = base.join(APP_DIRECTORY_NAME);24
let legacy = base.join(LEGACY_APP_DIRECTORY_NAME);25
let usable = |path: &Path| std::fs::read_dir(path).map(|mut entries| entries.next().is_some()).unwrap_or(false);26
if !usable(&target) && legacy.is_dir() {27
legacy28
} else {29
target30
}31
}33
pub fn home() -> PathBuf {34
std::env::var_os(if cfg!(windows) { "USERPROFILE" } else { "HOME" }).map(PathBuf::from).unwrap_or_default()35
}37
fn config_dir() -> PathBuf {38
if cfg!(target_os = "macos") {39
home().join("Library").join("Application Support")40
} else if cfg!(windows) {41
std::env::var_os("APPDATA").map(PathBuf::from).unwrap_or_else(|| home().join("AppData").join("Roaming"))42
} else {43
std::env::var_os("XDG_CONFIG_HOME").map(PathBuf::from).filter(|p| p.is_absolute()).unwrap_or_else(|| home().join(".config"))44
}45
}47
#[derive(Clone, Debug, PartialEq, Eq)]48
pub struct Settings {49
/// Master switch: off removes every system entry and stops the background process.50
pub enabled: bool,51
/// Start the background process at login.52
pub start_at_login: bool,53
/// Context-menu entries of the system file manager.54
pub menu_verify: bool,55
pub menu_sign: bool,56
}58
impl Default for Settings {59
fn default() -> Self {60
Self { enabled: false, start_at_login: true, menu_verify: true, menu_sign: true }61
}62
}64
impl Settings {65
pub fn from_json(value: &Value) -> Self {66
let flag = |key: &str, default: bool| value.get(key).and_then(Value::as_bool).unwrap_or(default);67
let d = Self::default();68
Self {69
enabled: flag("enabled", d.enabled),70
start_at_login: flag("startAtLogin", d.start_at_login),71
menu_verify: value.get("menu").and_then(|m| m.get("verify")).and_then(Value::as_bool).unwrap_or(d.menu_verify),72
menu_sign: value.get("menu").and_then(|m| m.get("sign")).and_then(Value::as_bool).unwrap_or(d.menu_sign),73
}74
}75
pub fn to_json(&self) -> Value {76
json!({"enabled": self.enabled, "startAtLogin": self.start_at_login, "menu": {"verify": self.menu_verify, "sign": self.menu_sign}})77
}78
pub fn wants_login(&self) -> bool {79
self.enabled && self.start_at_login80
}81
pub fn wants_verify(&self) -> bool {82
self.enabled && self.menu_verify83
}84
pub fn wants_sign(&self) -> bool {85
self.enabled && self.menu_sign86
}87
}89
pub fn load(data: &Path) -> Settings {90
std::fs::read(data.join(FILE)).ok().and_then(|b| serde_json::from_slice::<Value>(&b).ok()).map(|v| Settings::from_json(&v)).unwrap_or_default()91
}93
/// UI language of the application (`config/config.json` → `ui.language`), reduced to the helper's CS/DE/EN.94
pub fn language(data: &Path) -> &'static str {95
let code = std::fs::read(data.join("config").join("config.json"))96
.ok()97
.and_then(|b| serde_json::from_slice::<Value>(&b).ok())98
.and_then(|v| v["ui"]["language"].as_str().map(str::to_string))99
.unwrap_or_default();100
match code.get(..2) {101
Some("cs") | Some("sk") => "cs",102
Some("de") => "de",103
_ => "en",104
}105
}107
/// Everything the integration needs: where the user's folders are and what the menu entries say.108
pub struct Context {109
pub home: PathBuf,110
pub helper: PathBuf,111
/// Icon of the Windows menu entries.112
#[cfg_attr(not(windows), allow(dead_code))]113
pub app: PathBuf,114
pub language: &'static str,115
}117
impl Context {118
pub fn new(data: &Path) -> Self {119
Self { home: home(), helper: crate::helper_binary(), app: crate::app_binary(), language: language(data) }120
}121
pub fn label(&self, action: &str) -> &'static str {122
match (self.language, action) {123
("cs", "verify") => "CH-J – Ověřit podpis",124
("cs", _) => "CH-J – Podepsat (OpenPGP)",125
("de", "verify") => "CH-J – Signatur prüfen",126
("de", _) => "CH-J – Signieren (OpenPGP)",127
(_, "verify") => "CH-J – Verify signature",128
_ => "CH-J – Sign (OpenPGP)",129
}130
}131
}133
#[cfg(test)]134
mod tests {135
#[test]136
fn data_folder_follows_the_rename() {137
let base = tempfile::tempdir().unwrap();138
assert_eq!(super::choose(base.path()), base.path().join("CH-J Tools"));139
std::fs::create_dir_all(base.path().join("CH-J Server Manager").join("vault")).unwrap();140
assert_eq!(super::choose(base.path()), base.path().join("CH-J Server Manager"));141
std::fs::create_dir_all(base.path().join("CH-J Tools")).unwrap();142
assert_eq!(super::choose(base.path()), base.path().join("CH-J Server Manager"), "an empty new folder is not the data yet");143
std::fs::write(base.path().join("CH-J Tools").join("config.json"), b"{}").unwrap();144
assert_eq!(super::choose(base.path()), base.path().join("CH-J Tools"));145
}147
use super::*;149
#[test]150
fn missing_or_broken_settings_mean_off() {151
let dir = std::env::temp_dir().join(format!("chj-standby-settings-{}", std::process::id()));152
std::fs::create_dir_all(&dir).unwrap();153
assert_eq!(load(&dir), Settings::default());154
assert!(!load(&dir).wants_verify());155
std::fs::write(dir.join(FILE), "{broken").unwrap();156
assert_eq!(load(&dir), Settings::default());157
std::fs::write(dir.join(FILE), r#"{"enabled":true,"startAtLogin":false,"menu":{"sign":false}}"#).unwrap();158
let s = load(&dir);159
assert!(s.wants_verify() && !s.wants_sign() && !s.wants_login());160
assert_eq!(Settings::from_json(&s.to_json()), s);161
std::fs::remove_dir_all(&dir).unwrap();162
}163
}SHA-256
1cf707e94731cedb585ee1fe73f3f5c579a7020da7cf16a751b84f95dc743edcCH-J Proprietary Software License 1.14 · Source is provided under the CH-J Proprietary Software License 1.14. Its availability does not change the license terms or grant additional rights.
Archive SHA-256
ac8fe790f4ba60fc098f8c985fc2656cfcc6cdc21fb34b30e308a2b54e62258f