use std::path::Path;
use std::process::Command;
pub fn open<P: AsRef<Path>>(path: P) -> std::io::Result<()> {
let path = path.as_ref();
#[cfg(target_os = "windows")]
{
Command::new("cmd")
.args(["/c", "start", ""])
.arg(path)
.spawn()?;
}
#[cfg(target_os = "macos")]
{
Command::new("open").arg(path).spawn()?;
}
#[cfg(target_os = "linux")]
{
Command::new("xdg-open").arg(path).spawn()?;
}
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
{
return Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"Cannot open file: Unsupported operating system",
));
}
Ok(())
}
pub fn edit_file<P: AsRef<Path>>(path: P) -> std::io::Result<()> {
let path = path.as_ref();
if let Ok(editor) = std::env::var("EDITOR") {
let mut parts = editor.split_whitespace();
if let Some(cmd) = parts.next() {
let args: Vec<&str> = parts.collect();
let status = Command::new(cmd).args(&args).arg(path).status()?;
if status.success() {
return Ok(());
} else {
return Err(std::io::Error::other(format!(
"Editor '{}' exited with non-zero status",
editor
)));
}
}
}
#[cfg(target_os = "windows")]
{
let status = Command::new("notepad").arg(path).status()?;
if status.success() {
return Ok(());
}
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
{
eprint!("===============================");
for editor in ["nano", "vim", "vi"] {
if let Ok(status) = Command::new(editor).arg(path).status() {
eprintln!("{editor} result: {status:?}");
if status.success() {
return Ok(());
}
}
}
}
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
{
return Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"Cannot edit file: Unsupported operating system",
));
}
open(path)
}
#[cfg(test)]
mod test {
use super::*;
use std::fs::File;
use std::io::Write;
#[test]
fn t_open_command_exists() {
#[cfg(target_os = "windows")]
{
let output = Command::new("where").arg("cmd").output();
assert!(output.is_ok() && output.unwrap().status.success());
}
#[cfg(target_os = "macos")]
{
let output = Command::new("which").arg("open").output();
assert!(output.is_ok() && output.unwrap().status.success());
}
#[cfg(target_os = "linux")]
{
let output = Command::new("which").arg("xdg-open").output();
assert!(output.is_ok() && output.unwrap().status.success());
}
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
{
panic!("Unsupported operating system - no open command available");
}
}
#[test]
fn t_edit_command_exists() {
#[cfg(target_os = "windows")]
{
let output = Command::new("where").arg("notepad").output();
assert!(output.is_ok() && output.unwrap().status.success());
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
{
let editors = ["nano", "vim", "vi"];
let found = editors.iter().any(|editor| {
Command::new("which")
.arg(editor)
.output()
.map(|output| output.status.success())
.unwrap_or(false)
});
assert!(found, "No common text editor found (nano, vim, vi)");
}
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
{
panic!("Unsupported operating system - no edit command available");
}
}
#[test]
fn t_open_file() {
use std::env;
let temp_dir = env::temp_dir();
let file_path = temp_dir.join("ftag_test_file.txt");
{
let mut file = File::create(&file_path).unwrap();
writeln!(file, "Test file for ftag open functionality").unwrap();
}
let result = open(&file_path);
let _ = std::fs::remove_file(&file_path);
assert!(result.is_ok());
}
}