From 2e7b2dbc7e8ac5c78de76206f7f47e18ea2483ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Cs=C3=A1rdi?= Date: Fri, 18 Aug 2023 12:13:38 +0200 Subject: [PATCH] rig rstudio --config-path on Windows Towards fixing #134. --- Cargo.lock | 11 +++++ Cargo.toml | 1 + src/args.rs | 9 +++- src/common.rs | 9 ++++ src/windows.rs | 122 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 65fc2ae..b102f46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1249,6 +1249,7 @@ dependencies = [ "sudo", "tabular", "tokio", + "whoami", "winreg", ] @@ -1867,6 +1868,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "whoami" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 6548b1c..de17125 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ tokio = { version = "1", features = ["full"] } [target.'cfg(windows)'.dependencies] is_elevated = "0.1.2" remove_dir_all = "0.7.0" +whoami = "1.4.1" winreg = "0.10" [build-dependencies] diff --git a/src/args.rs b/src/args.rs index 1da341a..0ae968d 100644 --- a/src/args.rs +++ b/src/args.rs @@ -440,7 +440,14 @@ pub fn rig_app() -> Command { Arg::new("project-file") .help("RStudio project file (.Rproj) to open") .required(false), - ); + ) + .arg( + Arg::new("config-path") + .help("Do not start RStudio, only print the path of the RStudio config directory") + .long("config-path") + .required(false) + .num_args(0) + ); let cmd_library = Command::new("library") .about("Manage package libraries [alias: lib] (experimental)") diff --git a/src/common.rs b/src/common.rs index df876d2..d44b761 100644 --- a/src/common.rs +++ b/src/common.rs @@ -164,6 +164,15 @@ pub fn sc_rstudio(args: &ArgMatches) -> Result<(), Box> { let mut ver: Option<&String> = args.get_one("version"); let mut prj: Option<&String> = args.get_one("project-file"); + if args.get_flag("config-path") { + let cp = get_rstudio_config_path(); + match cp { + Ok(x) => println!("{}", x.display()), + Err(x) => bail!("Error: {}", x.to_string()) + }; + return Ok(()); + } + if let Some(ver2) = ver { if ver2.ends_with("renv.lock") { let lockfile = PathBuf::new().join(ver2); diff --git a/src/windows.rs b/src/windows.rs index f34fa9a..63be790 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -12,10 +12,12 @@ use std::process::Command; use std::{thread, time}; use clap::ArgMatches; +use directories::BaseDirs; use remove_dir_all::remove_dir_all; use semver; use simple_error::{bail, SimpleError}; use simplelog::*; +use whoami::{hostname, username}; use winreg::enums::*; use winreg::RegKey; @@ -867,6 +869,126 @@ fn get_latest_install_path() -> Result, Box> { Ok(None) } +// All this is from https://github.com/rstudio/rstudio/blob/44f09c50d469a14d5a9c3840c7a239f3bf21ace9/src/cpp/core/system/Xdg.cpp#L85 +// +// 1. RSTUDIO_CONFIG_HOME might point to the final path. +// 2. XDG_CONFIG_HOME might point to the user config path (append RStudio). +// 3. Otherwise query FOLDERID_RoamingAppData for the user config path. +// 4. Or fall back to `~/.config` if that fails (it really should not). +// 5. Set USER, HOME and HOSTNAME env vars for expansion. +// 6. Expand env vars, both $ENV and ${ENV} types. +// 7. Expand ~ to home dir +// +// 5-6 are only needed if the path has a '$' or '~' character. +// 7 is only needed if the path has a '~' character or if it is empty. + +pub fn get_rstudio_config_path() -> Result> { + let mut final_path = true; + let mut xdg: Option = None; + + // RSTUDIO_CONFIG_HOME may point to the final path + match std::env::var("RSTUDIO_CONFIG_HOME") { + Ok(x) => xdg = Some(std::path::PathBuf::from(x)), + Err(_) => { } + }; + + // XDG_CONFIG_HOME may point to the user config path + if xdg.is_none() { + final_path = false; + match std::env::var("XDG_CONFIG_HOME") { + Ok(x) => { + let mut xdg2 = std::path::PathBuf::new(); + xdg2.push(x); + xdg = Some(xdg2); + }, + Err(_) => { } + }; + } + + // Get user config path from system + if xdg.is_none() { + if let Some(bd) = BaseDirs::new() { + xdg = Some(std::path::PathBuf::from(bd.config_dir())); + } + } + + // Fall back to `~/.config` + if xdg.is_none() { + xdg = Some(std::path::PathBuf::from("~/.config")); + } + + // We have a path at this point + let xdg = xdg.unwrap(); + + // Set USER, HOME, HOSTNAME, for expansion, if needed + let mut path = match xdg.to_str() { + Some(x) => String::from(x), + None => bail!("RStudio config path cannot be represented as Unicode. :(") + }; + let has_dollar = path.contains("$"); + let empty = path.len() == 0; + let has_tilde = path.contains("~"); + + if has_dollar || empty || has_tilde { + match std::env::var("USER") { + Ok(_) => { }, + Err(_) => { + let username = username(); + std::env::set_var("USER", username); + } + }; + } + + if has_dollar { + match std::env::var("HOME") { + Ok(_) => { }, + Err(_) => { + let bd = BaseDirs::new(); + match bd { + Some(x) => { + std::env::set_var("HOME", x.home_dir().as_os_str()); + }, + None => { + warn!("Cannot determine HOME directory"); + } + }; + } + }; + } + + if has_dollar { + match std::env::var("HOSTNAME") { + Ok(_) => { }, + Err(_) => { + let hostname = hostname(); + std::env::set_var("HOSTNAME", hostname); + } + }; + } + + // Expand env vars + if has_dollar { + path = match shellexpand::env(&path) { + Ok(x) => x.to_string(), + Err(e) => { + bail!("RStudio config path contains unknown environment variable: {}", e.var_name); + } + }; + } + + // Expand empty path and ~ + if empty || has_tilde { + path = shellexpand::tilde(&path).to_string(); + } + + let mut xdg = std::path::PathBuf::from(path); + if !final_path { + xdg.push("RStudio"); + } + + Ok(xdg) +} + pub fn sc_rstudio_(version: Option<&str>, project: Option<&str>, arg: Option<&OsStr>) -> Result<(), Box> { // we only need to restore if 'ver' is given, there is a default and