safir_core/
utils.rs

1use anyhow::Result;
2use std::io::Write;
3use std::path::{Path, PathBuf};
4
5use crate::config::SafirConfig;
6
7use colored::*;
8use sysinfo::{Pid, System, SystemExt};
9use tokio::fs;
10
11pub async fn init() -> Result<SafirConfig> {
12    let store_dir = create_safir_directory().await?;
13    let cfg = load_safir_config(&store_dir).await?;
14    Ok(cfg)
15}
16
17pub fn check_rubin_installed() -> bool {
18    if which::which("rubin").is_ok() {
19        return true;
20    }
21
22    false
23}
24
25pub fn check_process_running(pid: u32) -> bool {
26    let mut system = System::new_all();
27    system.refresh_all();
28    if system.process(Pid::from(pid as usize)).is_some() {
29        return true;
30    }
31
32    false
33}
34
35pub fn is_safir_running(pid: Option<u32>) -> bool {
36    match pid {
37        Some(pid) => check_process_running(pid),
38        None => false,
39    }
40}
41
42pub async fn path_exists(path: impl AsRef<Path>) -> bool {
43    path.as_ref().exists()
44}
45
46pub async fn create_safir_directory() -> Result<PathBuf> {
47    let home_dir = dirs::home_dir().unwrap();
48    let store_path = home_dir.join(".safirstore");
49    fs::create_dir_all(&store_path).await?;
50
51    Ok(store_path)
52}
53
54#[cfg(target_family = "unix")]
55pub async fn kill_process(pid: u32) -> Result<()> {
56    if let Ok(process) = psutil::process::Process::new(pid) {
57        if let Err(err) = process.kill() {
58            eprintln!("failed to kill process: {}", err);
59        }
60    } else {
61        eprintln!("failed to get process information");
62    }
63
64    Ok(())
65}
66
67#[cfg(target_os = "windows")]
68pub async fn kill_process(pid: u32) {
69    println!("*** Windows -- This is experimental and may not work as intended! ***");
70    let output = Command::new("taskkill")
71        .arg("/F")
72        .arg("/PID")
73        .arg(pid.to_string())
74        .output()
75        .expect("failed to call taskkill");
76
77    if !output.status.success() {
78        eprintln!("failed to terminate process");
79    }
80}
81
82/// Formats and prints the message to stdout
83pub fn print_output(msg: &str) {
84    println!("{}\n", msg);
85}
86
87/// Prints the Safirstore header
88pub fn print_header() {
89    println!("{}", "--=Safirstore=--\n".bold());
90}
91
92pub fn print_headless(prefix: &str, key: &str, value: &str) {
93    if value == "" {
94        return;
95    }
96
97    let has_whitespace = value.contains(char::is_whitespace);
98
99    let output = if has_whitespace {
100        format!("{}=\"{}\"", key, value)
101    } else {
102        format!("{}={}", key, value)
103    };
104
105    if !prefix.is_empty() {
106        println!("{} {}", prefix, output);
107    } else {
108        println!("{}", output);
109    }
110}
111
112/// Confirmation dialog for important calls
113pub fn confirm_entry(msg: &str) -> bool {
114    let mut answer = String::new();
115    print!("{} (y/n) ", msg);
116    std::io::stdout().flush().expect("failed to flush buffer");
117
118    let _ = std::io::stdin()
119        .read_line(&mut answer)
120        .expect("unable to get input from user");
121
122    let answer = answer.trim().to_lowercase();
123    if answer == "y" || answer == "yes" {
124        return true;
125    }
126
127    false
128}
129
130pub async fn load_safir_config(store_dir: impl AsRef<Path>) -> Result<SafirConfig> {
131    let cfg_path = &store_dir.as_ref().join("safir.cfg");
132    let mut cfg = if path_exists(&cfg_path).await {
133        SafirConfig::load(&cfg_path).await?
134    } else {
135        SafirConfig::new()
136    };
137
138    cfg.root_path = store_dir.as_ref().to_owned();
139    cfg.config_path = cfg_path.to_owned();
140
141    // Used in cases where the process has ended ungracefully and the config hasnt been updated
142    if let Some(pid) = cfg.memcache_pid {
143        if !check_process_running(pid) {
144            cfg = SafirConfig::new();
145            cfg.write().await?;
146        }
147    }
148
149    Ok(cfg)
150}