Take XDG resolution from the shared crate too
Sixth and last of the modules that were the link rather than this product. main.rs loses config_home, data_home and xdg_dir, and its test module with them -- it held one test, which moved to the crate that now holds the code. The helpers gained a `product` parameter, matching certs::ensure and netif::wg_address, which is what keeps two products' state apart while resolving it identically. Verified by running it: with only XDG_CONFIG_HOME and XDG_DATA_HOME set and no --config or --data-dir, the server puts its certificates in $XDG_CONFIG_HOME/ai-app/certs and its sessions in $XDG_DATA_HOME/ai-app/sessions, and still prints an aiapp:// enrollment URI. 35 tests here and 19 in the crate, clippy silent, rustfmt clean.
This commit is contained in:
1 parent
aa05ff9336
commit
2c925a679f
2 files changed
+8
-58
No files matched your search
+7
-57
@@ -33,40 +33,13 @@ use tokio::signal::unix::{SignalKind, signal};
|
|||||||
|
|
||||||
use wg_app_link::enroll;
|
use wg_app_link::enroll;
|
||||||
use wg_app_link::netif::{self, WG_INTERFACE};
|
use wg_app_link::netif::{self, WG_INTERFACE};
|
||||||
|
use wg_app_link::xdg::{config_home, data_home};
|
||||||
|
|
||||||
use config::TokenEntry;
|
use config::TokenEntry;
|
||||||
use session::SessionManager;
|
use session::SessionManager;
|
||||||
|
|
||||||
const DEFAULT_PORT: u16 = 8443;
|
const DEFAULT_PORT: u16 = 8443;
|
||||||
|
|
||||||
/// `$XDG_CONFIG_HOME/ai-app`, or `~/.config/ai-app`. Holds `config.ron`
|
|
||||||
/// and `certs/`.
|
|
||||||
fn config_home() -> PathBuf {
|
|
||||||
xdg_dir(std::env::var_os("XDG_CONFIG_HOME"), ".config")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `$XDG_DATA_HOME/ai-app`, or `~/.local/share/ai-app`. Holds the session
|
|
||||||
/// directories: transcripts, attachments, produced images.
|
|
||||||
fn data_home() -> PathBuf {
|
|
||||||
xdg_dir(std::env::var_os("XDG_DATA_HOME"), ".local/share")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This app's directory under `base` -- the XDG variable's value, if it
|
|
||||||
/// was set to an absolute path as the spec requires -- or under
|
|
||||||
/// `~/<fallback>` otherwise. Takes the value rather than reading the
|
|
||||||
/// environment itself so the rule is testable without mutating a
|
|
||||||
/// process-wide variable other threads may be reading.
|
|
||||||
fn xdg_dir(base: Option<std::ffi::OsString>, fallback: &str) -> PathBuf {
|
|
||||||
base.map(PathBuf::from)
|
|
||||||
.filter(|path| path.is_absolute())
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
std::env::home_dir()
|
|
||||||
.unwrap_or_else(|| PathBuf::from("."))
|
|
||||||
.join(fallback)
|
|
||||||
})
|
|
||||||
.join("ai-app")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serves AI coding sessions (Claude Code, llama.cpp) to the phone app.
|
/// Serves AI coding sessions (Claude Code, llama.cpp) to the phone app.
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Args {
|
struct Args {
|
||||||
@@ -121,16 +94,16 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
let config_path = args
|
let config_path = args
|
||||||
.config
|
.config
|
||||||
.unwrap_or_else(|| config_home().join("config.ron"));
|
.unwrap_or_else(|| config_home("ai-app").join("config.ron"));
|
||||||
let data_dir = args
|
let data_dir = args
|
||||||
.data_dir
|
.data_dir
|
||||||
.unwrap_or_else(|| data_home().join("sessions"));
|
.unwrap_or_else(|| data_home("ai-app").join("sessions"));
|
||||||
// Beside the session data rather than under it: models outlive every
|
// Beside the session data rather than under it: models outlive every
|
||||||
// session and are shared by all of them, so deleting a session must
|
// session and are shared by all of them, so deleting a session must
|
||||||
// never take a multi-gigabyte download with it.
|
// never take a multi-gigabyte download with it.
|
||||||
let models_dir = args
|
let models_dir = args
|
||||||
.models_dir
|
.models_dir
|
||||||
.unwrap_or_else(|| data_home().join("models"));
|
.unwrap_or_else(|| data_home("ai-app").join("models"));
|
||||||
let models = Arc::new(models::ModelStore::new(models_dir.clone()));
|
let models = Arc::new(models::ModelStore::new(models_dir.clone()));
|
||||||
let manager = Arc::new(
|
let manager = Arc::new(
|
||||||
SessionManager::new(config_path.clone(), data_dir, models_dir.clone())
|
SessionManager::new(config_path.clone(), data_dir, models_dir.clone())
|
||||||
@@ -163,7 +136,9 @@ async fn main() -> Result<()> {
|
|||||||
// also what the phone app embeds at build time, so they need to be
|
// also what the phone app embeds at build time, so they need to be
|
||||||
// obtainable on a machine whose tunnel isn't up yet. The leaf is
|
// obtainable on a machine whose tunnel isn't up yet. The leaf is
|
||||||
// reissued on every start, so once wg0 exists the next start covers it.
|
// reissued on every start, so once wg0 exists the next start covers it.
|
||||||
let certs_dir = args.certs.unwrap_or_else(|| config_home().join("certs"));
|
let certs_dir = args
|
||||||
|
.certs
|
||||||
|
.unwrap_or_else(|| config_home("ai-app").join("certs"));
|
||||||
let certificates = wg_app_link::certs::ensure("ai-app", &certs_dir, &netif::local_addresses())
|
let certificates = wg_app_link::certs::ensure("ai-app", &certs_dir, &netif::local_addresses())
|
||||||
.with_context(|| format!("failed to prepare certificates in {}", certs_dir.display()))?;
|
.with_context(|| format!("failed to prepare certificates in {}", certs_dir.display()))?;
|
||||||
if certificates.ca_is_new {
|
if certificates.ca_is_new {
|
||||||
@@ -247,28 +222,3 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn xdg_dirs_respect_the_environment_and_are_namespaced() {
|
|
||||||
let home_fallback = xdg_dir(None, ".config");
|
|
||||||
assert!(home_fallback.ends_with("ai-app"));
|
|
||||||
assert!(home_fallback.parent().expect("parent").ends_with(".config"));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
xdg_dir(Some("/somewhere".into()), ".config"),
|
|
||||||
PathBuf::from("/somewhere/ai-app"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Relative values are ignored per the spec, rather than resolving
|
|
||||||
// against whatever the working directory happens to be -- so a
|
|
||||||
// relative setting lands on the same path as no setting at all.
|
|
||||||
assert_eq!(
|
|
||||||
xdg_dir(Some("relative/path".into()), ".config"),
|
|
||||||
home_fallback
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-1
Submodule wg-app-link updated: 841a3a8372...73a32cb897.
Reference in new issue
Block a user