refactor(host/W6.1): extract secret/config-dir helpers into the pf-paths leaf crate

Second de-coupling for the host crate carve (plan §W6.1 leaf). config_dir /
create_private_dir / write_secret_file (+ the Windows DACL helpers) were pub(crate) in the
gamestream junk drawer, yet consumed by vdisplay, stats, gpu, library, mgmt_token,
native_pairing and the Windows service — many of which become pf-media / pf-vdisplay, for
which crate::gamestream would be an illegal upward edge. New leaf crate pf-paths (pure std
+ tracing) owns them; ~40 call sites across 14 files repoint to pf_paths::. gamestream
keeps only its own concerns.

Verified: Linux (home-worker-5) clippy -p pf-paths -p punktfunk-host --all-targets
-D warnings + tests (347 pass, incl. secrets_are_written_owner_only); Windows
(192.168.1.158) clippy --features nvenc,amf-qsv --all-targets green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-17 01:07:26 +02:00
parent 2e3208f75e
commit c42ce88921
19 changed files with 248 additions and 218 deletions
@@ -76,7 +76,7 @@ impl DisplayIdentityMap {
/// re-derives ids, costing a client one scaling re-set the first time). Migrates the legacy
/// Windows `pf-vdisplay-identity.json` if the new file is absent.
pub(crate) fn load() -> Self {
let dir = crate::gamestream::config_dir();
let dir = pf_paths::config_dir();
let path = dir.join(FILE);
let bytes = std::fs::read(&path)
.or_else(|_| std::fs::read(dir.join(LEGACY_FILE)))
@@ -240,7 +240,7 @@ impl ScaleMap {
/// Load the persisted map (empty on first run / unreadable file — a client just re-sets its
/// scaling once). Drops non-finite / out-of-range entries from a hand-edited file.
fn load() -> Self {
let path = crate::gamestream::config_dir().join(SCALE_FILE);
let path = pf_paths::config_dir().join(SCALE_FILE);
let mut map: std::collections::BTreeMap<String, f64> = std::fs::read(&path)
.ok()
.and_then(|b| serde_json::from_slice(&b).ok())
+6 -6
View File
@@ -481,10 +481,10 @@ impl DisplayPolicyStore {
pub fn set(&self, policy: DisplayPolicy) -> Result<()> {
let policy = policy.sanitized();
if let Some(dir) = self.path.parent() {
crate::gamestream::create_private_dir(dir)?;
pf_paths::create_private_dir(dir)?;
}
let tmp = self.path.with_extension("json.tmp");
crate::gamestream::write_secret_file(&tmp, &serde_json::to_vec_pretty(&policy)?)?;
pf_paths::write_secret_file(&tmp, &serde_json::to_vec_pretty(&policy)?)?;
std::fs::rename(&tmp, &self.path)?;
*self.cur.lock().unwrap() = Some(policy);
Ok(())
@@ -497,7 +497,7 @@ impl DisplayPolicyStore {
pub fn prefs() -> &'static DisplayPolicyStore {
static STORE: OnceLock<DisplayPolicyStore> = OnceLock::new();
STORE.get_or_init(|| {
DisplayPolicyStore::load_from(crate::gamestream::config_dir().join("display-settings.json"))
DisplayPolicyStore::load_from(pf_paths::config_dir().join("display-settings.json"))
})
}
@@ -539,7 +539,7 @@ pub struct CustomPresetInput {
}
fn custom_presets_path() -> PathBuf {
crate::gamestream::config_dir().join("display-presets.json")
pf_paths::config_dir().join("display-presets.json")
}
/// Clamp a saved preset's fields to their valid ranges — the same bounds [`DisplayPolicy::sanitized`]
@@ -566,10 +566,10 @@ pub fn load_custom_presets() -> Vec<CustomPreset> {
fn save_custom_presets(presets: &[CustomPreset]) -> Result<()> {
let path = custom_presets_path();
if let Some(dir) = path.parent() {
crate::gamestream::create_private_dir(dir)?;
pf_paths::create_private_dir(dir)?;
}
let tmp = path.with_extension("json.tmp");
crate::gamestream::write_secret_file(&tmp, &serde_json::to_vec_pretty(presets)?)?;
pf_paths::write_secret_file(&tmp, &serde_json::to_vec_pretty(presets)?)?;
std::fs::rename(&tmp, &path)?;
Ok(())
}