use serde::{Deserialize, Serialize};
use std::fmt;
use super::command_map::{get_mapping, is_native_command, is_target_command_for_os, CommandMapping};
use super::os::Os;
use super::path::{translate_path, is_windows_path, is_unix_path};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TranslationResult {
pub command: String,
pub original: String,
pub from_os: Os,
pub to_os: Os,
pub warnings: Vec<String>,
pub had_unmapped_flags: bool,
}
impl TranslationResult {
pub fn new(command: String, original: String, from_os: Os, to_os: Os) -> Self {
Self {
command,
original,
from_os,
to_os,
warnings: Vec::new(),
had_unmapped_flags: false,
}
}
}
impl fmt::Display for TranslationResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.command)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TranslationError {
CommandNotFound(String),
EmptyCommand,
InvalidOs(String),
SameOs,
}
impl fmt::Display for TranslationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TranslationError::CommandNotFound(cmd) => {
write!(f, "No translation found for command '{}'", cmd)
}
TranslationError::EmptyCommand => {
write!(f, "Empty command provided")
}
TranslationError::InvalidOs(os) => {
write!(f, "Invalid operating system: '{}'", os)
}
TranslationError::SameOs => {
write!(f, "Source and target OS are the same")
}
}
}
}
impl std::error::Error for TranslationError {}
fn parse_command(input: &str) -> (String, Vec<String>) {
let trimmed = input.trim();
let parts: Vec<&str> = trimmed.split_whitespace().collect();
if parts.is_empty() {
return (String::new(), Vec::new());
}
let command = parts[0].to_lowercase();
let args: Vec<String> = parts[1..].iter().map(|s| s.to_string()).collect();
(command, args)
}
const MAX_WINDOWS_FLAG_LEN: usize = 4;
fn is_path_argument(arg: &str, from_os: Os) -> bool {
if arg.starts_with('-') {
return false;
}
if from_os == Os::Windows && arg.starts_with('/') && arg.len() <= MAX_WINDOWS_FLAG_LEN {
return false;
}
if from_os == Os::Windows {
return is_windows_path(arg);
}
if from_os.is_unix_like() {
return is_unix_path(arg);
}
false
}
fn translate_path_args(
args: &[String],
from_os: Os,
to_os: Os,
result: &mut TranslationResult
) -> Vec<String> {
args.iter().map(|arg| {
if is_path_argument(arg, from_os) {
match translate_path(arg, from_os, to_os) {
Ok(path_result) => {
if !path_result.warnings.is_empty() {
result.warnings.extend(path_result.warnings);
}
path_result.path
}
Err(e) => {
result.warnings.push(format!("Path '{}' could not be translated: {}", arg, e));
arg.clone() }
}
} else {
arg.clone()
}
}).collect()
}
fn translate_flags(
args: &[String],
mapping: &CommandMapping,
result: &mut TranslationResult,
) -> Vec<String> {
let mut translated_args = Vec::new();
for arg in args {
let mut found = false;
for flag_mapping in &mapping.flag_mappings {
if arg == &flag_mapping.source || arg.to_lowercase() == flag_mapping.source.to_lowercase() {
if !flag_mapping.target.is_empty() {
for part in flag_mapping.target.split_whitespace() {
translated_args.push(part.to_string());
}
}
found = true;
break;
}
if arg.starts_with(&flag_mapping.source) {
let value = &arg[flag_mapping.source.len()..];
if !flag_mapping.target.is_empty() {
if value.is_empty() {
translated_args.push(flag_mapping.target.clone());
} else {
let value_clean = value.trim_start_matches(':').trim_start_matches('=');
translated_args.push(format!("{} {}", flag_mapping.target, value_clean));
}
}
found = true;
break;
}
}
if !found {
if mapping.preserve_unmapped_flags {
translated_args.push(arg.clone());
if arg.starts_with('-') || arg.starts_with('/') {
result.warnings.push(format!("Flag '{}' was not translated", arg));
result.had_unmapped_flags = true;
}
} else {
result.warnings.push(format!("Flag '{}' was dropped", arg));
result.had_unmapped_flags = true;
}
}
}
translated_args
}
pub fn translate_command(
input: &str,
from_os: Os,
to_os: Os,
) -> Result<TranslationResult, TranslationError> {
let trimmed = input.trim();
if trimmed.is_empty() {
return Err(TranslationError::EmptyCommand);
}
if from_os == to_os {
return Ok(TranslationResult::new(
trimmed.to_string(),
trimmed.to_string(),
from_os,
to_os,
));
}
let (command_name, args) = parse_command(trimmed);
if command_name.is_empty() {
return Err(TranslationError::EmptyCommand);
}
if is_native_command(&command_name, to_os) && !is_native_command(&command_name, from_os) {
let mut result = TranslationResult::new(
trimmed.to_string(),
trimmed.to_string(),
from_os,
to_os,
);
result.warnings.push(format!(
"Command '{}' is already in {} format, passed through unchanged",
command_name, to_os
));
return Ok(result);
}
if is_native_command(&command_name, to_os) && is_native_command(&command_name, from_os) {
if let Some(mapping) = get_mapping(&command_name, from_os, to_os) {
let mut result = TranslationResult::new(
String::new(),
trimmed.to_string(),
from_os,
to_os,
);
let translated_args = translate_flags(&args, mapping, &mut result);
let mut final_command = mapping.target_cmd.clone();
if !translated_args.is_empty() {
final_command.push(' ');
final_command.push_str(&translated_args.join(" "));
}
result.command = final_command;
return Ok(result);
} else {
return Ok(TranslationResult::new(
trimmed.to_string(),
trimmed.to_string(),
from_os,
to_os,
));
}
}
let mapping = match get_mapping(&command_name, from_os, to_os) {
Some(m) => m,
None => {
if from_os.is_unix_like() && to_os.is_unix_like() {
let mut result = TranslationResult::new(
trimmed.to_string(),
trimmed.to_string(),
from_os,
to_os,
);
result.warnings.push(format!(
"Command '{}' passed through (Unix-like OS compatibility assumed)",
command_name
));
return Ok(result);
}
if is_target_command_for_os(&command_name, to_os) {
let mut result = TranslationResult::new(
trimmed.to_string(),
trimmed.to_string(),
from_os,
to_os,
);
result.warnings.push(format!(
"Command '{}' appears to already be a {} command, passed through unchanged",
command_name, to_os
));
return Ok(result);
}
return Err(TranslationError::CommandNotFound(command_name));
}
};
let mut result = TranslationResult::new(
String::new(),
trimmed.to_string(),
from_os,
to_os,
);
let translated_args = translate_flags(&args, mapping, &mut result);
let mut final_command = mapping.target_cmd.clone();
if !translated_args.is_empty() {
final_command.push(' ');
final_command.push_str(&translated_args.join(" "));
}
result.command = final_command;
if let Some(notes) = &mapping.notes {
result.warnings.push(notes.clone());
}
Ok(result)
}
pub fn translate_command_str(
input: &str,
from_os: &str,
to_os: &str,
) -> Result<TranslationResult, TranslationError> {
let from = Os::parse(from_os)
.ok_or_else(|| TranslationError::InvalidOs(from_os.to_string()))?;
let to = Os::parse(to_os)
.ok_or_else(|| TranslationError::InvalidOs(to_os.to_string()))?;
translate_command(input, from, to)
}
pub fn translate_batch(
commands: &[&str],
from_os: Os,
to_os: Os,
) -> Vec<Result<TranslationResult, TranslationError>> {
commands
.iter()
.map(|cmd| translate_command(cmd, from_os, to_os))
.collect()
}
pub fn translate_full(
input: &str,
from_os: Os,
to_os: Os,
) -> Result<TranslationResult, TranslationError> {
let trimmed = input.trim();
if trimmed.is_empty() {
return Err(TranslationError::EmptyCommand);
}
if from_os == to_os {
return Ok(TranslationResult::new(
trimmed.to_string(),
trimmed.to_string(),
from_os,
to_os,
));
}
let (command_name, args) = parse_command(trimmed);
if command_name.is_empty() {
return Err(TranslationError::EmptyCommand);
}
let mut result = TranslationResult::new(
String::new(),
trimmed.to_string(),
from_os,
to_os,
);
let args_with_translated_paths = translate_path_args(&args, from_os, to_os, &mut result);
if is_native_command(&command_name, to_os) && !is_native_command(&command_name, from_os) {
let mut final_command = command_name.clone();
if !args_with_translated_paths.is_empty() {
final_command.push(' ');
final_command.push_str(&args_with_translated_paths.join(" "));
}
result.command = final_command;
result.warnings.push(format!(
"Command '{}' is already in {} format, only paths translated",
command_name, to_os
));
return Ok(result);
}
if is_native_command(&command_name, to_os) && is_native_command(&command_name, from_os) {
if let Some(mapping) = get_mapping(&command_name, from_os, to_os) {
let translated_args = translate_flags(&args_with_translated_paths, mapping, &mut result);
let mut final_command = mapping.target_cmd.clone();
if !translated_args.is_empty() {
final_command.push(' ');
final_command.push_str(&translated_args.join(" "));
}
result.command = final_command;
return Ok(result);
} else {
let mut final_command = command_name.clone();
if !args_with_translated_paths.is_empty() {
final_command.push(' ');
final_command.push_str(&args_with_translated_paths.join(" "));
}
result.command = final_command;
return Ok(result);
}
}
let mapping = match get_mapping(&command_name, from_os, to_os) {
Some(m) => m,
None => {
if from_os.is_unix_like() && to_os.is_unix_like() {
let mut final_command = command_name.clone();
if !args_with_translated_paths.is_empty() {
final_command.push(' ');
final_command.push_str(&args_with_translated_paths.join(" "));
}
result.command = final_command;
result.warnings.push(format!(
"Command '{}' passed through with path translation (Unix-like OS compatibility assumed)",
command_name
));
return Ok(result);
}
if is_target_command_for_os(&command_name, to_os) {
let mut final_command = command_name.clone();
if !args_with_translated_paths.is_empty() {
final_command.push(' ');
final_command.push_str(&args_with_translated_paths.join(" "));
}
result.command = final_command;
result.warnings.push(format!(
"Command '{}' appears to already be a {} command, paths translated",
command_name, to_os
));
return Ok(result);
}
return Err(TranslationError::CommandNotFound(command_name));
}
};
let translated_args = translate_flags(&args_with_translated_paths, mapping, &mut result);
let mut final_command = mapping.target_cmd.clone();
if !translated_args.is_empty() {
final_command.push(' ');
final_command.push_str(&translated_args.join(" "));
}
result.command = final_command;
if let Some(notes) = &mapping.notes {
result.warnings.push(notes.clone());
}
Ok(result)
}
const COMPOUND_OPERATORS: &[&str] = &["&&", "||", ";", "|"];
pub fn translate_compound_command(
input: &str,
from_os: Os,
to_os: Os,
) -> Result<TranslationResult, TranslationError> {
let trimmed = input.trim();
if trimmed.is_empty() {
return Err(TranslationError::EmptyCommand);
}
if from_os == to_os {
return Ok(TranslationResult::new(
trimmed.to_string(),
trimmed.to_string(),
from_os,
to_os,
));
}
let parts = split_compound_command(trimmed);
if parts.len() == 1 {
return translate_command(trimmed, from_os, to_os);
}
let mut result = TranslationResult::new(
String::new(),
trimmed.to_string(),
from_os,
to_os,
);
let mut translated_parts = Vec::new();
for part in &parts {
let trimmed_part = part.trim();
if COMPOUND_OPERATORS.contains(&trimmed_part) {
translated_parts.push(trimmed_part.to_string());
} else if !trimmed_part.is_empty() {
match translate_command(trimmed_part, from_os, to_os) {
Ok(cmd_result) => {
translated_parts.push(cmd_result.command);
result.warnings.extend(cmd_result.warnings);
result.had_unmapped_flags |= cmd_result.had_unmapped_flags;
}
Err(TranslationError::CommandNotFound(_)) => {
translated_parts.push(trimmed_part.to_string());
result.warnings.push(format!("Command '{}' was not translated", trimmed_part.split_whitespace().next().unwrap_or(trimmed_part)));
}
Err(e) => return Err(e),
}
}
}
result.command = translated_parts.join(" ");
Ok(result)
}
fn split_compound_command(input: &str) -> Vec<String> {
let mut parts = Vec::new();
let mut current = String::new();
let chars: Vec<char> = input.chars().collect();
let mut i = 0;
while i < chars.len() {
if i + 1 < chars.len() {
let two_char = format!("{}{}", chars[i], chars[i + 1]);
if two_char == "&&" || two_char == "||" {
if !current.is_empty() {
parts.push(current);
current = String::new();
}
parts.push(two_char);
i += 2;
continue;
}
}
if chars[i] == '|' || chars[i] == ';' {
if !current.is_empty() {
parts.push(current);
current = String::new();
}
parts.push(chars[i].to_string());
i += 1;
continue;
}
current.push(chars[i]);
i += 1;
}
if !current.is_empty() {
parts.push(current);
}
parts
}
pub fn translate_script_extension(filename: &str, from_os: Os, to_os: Os) -> String {
if from_os == to_os {
return filename.to_string();
}
let filename = filename.trim();
if from_os == Os::Windows && to_os.is_unix_like() {
let filename_lower = filename.to_lowercase();
if let Some(base) = filename_lower.strip_suffix(".bat") {
return format!("{}.sh", &filename[..base.len()]);
}
if let Some(base) = filename_lower.strip_suffix(".cmd") {
return format!("{}.sh", &filename[..base.len()]);
}
if let Some(base) = filename_lower.strip_suffix(".ps1") {
return format!("{}.sh", &filename[..base.len()]);
}
if let Some(base) = filename_lower.strip_suffix(".exe") {
return filename[..base.len()].to_string();
}
}
if from_os.is_unix_like() && to_os == Os::Windows {
if let Some(base) = filename.strip_suffix(".sh") {
return format!("{}.bat", base);
}
if let Some(file_name) = std::path::Path::new(filename).file_name() {
let name = file_name.to_string_lossy();
if !name.contains('.') {
return format!("{}.exe", filename);
}
}
}
filename.to_string()
}
pub fn translate_shebang(line: &str, from_os: Os, to_os: Os) -> String {
if from_os == to_os {
return line.to_string();
}
let line = line.trim();
if from_os.is_unix_like() && to_os == Os::Windows && line.starts_with("#!") {
return "@echo off".to_string();
}
if from_os == Os::Windows && to_os.is_unix_like() && line.to_lowercase().starts_with("@echo off") {
return "#!/bin/bash".to_string();
}
line.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_command() {
let (cmd, args) = parse_command("ls -la /home");
assert_eq!(cmd, "ls");
assert_eq!(args, vec!["-la", "/home"]);
}
#[test]
fn test_parse_command_no_args() {
let (cmd, args) = parse_command("ls");
assert_eq!(cmd, "ls");
assert!(args.is_empty());
}
#[test]
fn test_parse_command_empty() {
let (cmd, args) = parse_command("");
assert!(cmd.is_empty());
assert!(args.is_empty());
}
#[test]
fn test_translate_dir_to_ls() {
let result = translate_command("dir", Os::Windows, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.command, "ls");
}
#[test]
fn test_translate_dir_with_flags() {
let result = translate_command("dir /w", Os::Windows, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.command.contains("ls"));
assert!(result.command.contains("-C"));
}
#[test]
fn test_translate_ls_to_dir() {
let result = translate_command("ls", Os::Linux, Os::Windows);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.command, "dir");
}
#[test]
fn test_translate_ls_with_flags() {
let result = translate_command("ls -la", Os::Linux, Os::Windows);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.command.contains("dir"));
}
#[test]
fn test_translate_copy_to_cp() {
let result = translate_command("copy /y", Os::Windows, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.command.contains("cp"));
assert!(result.command.contains("-f"));
}
#[test]
fn test_translate_cls_to_clear() {
let result = translate_command("cls", Os::Windows, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.command, "clear");
}
#[test]
fn test_translate_clear_to_cls() {
let result = translate_command("clear", Os::Linux, Os::Windows);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.command, "cls");
}
#[test]
fn test_translate_grep_to_findstr() {
let result = translate_command("grep -i pattern", Os::Linux, Os::Windows);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.command.contains("findstr"));
assert!(result.command.contains("/i"));
}
#[test]
fn test_translate_findstr_to_grep() {
let result = translate_command("findstr /i pattern", Os::Windows, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.command.contains("grep"));
assert!(result.command.contains("-i"));
}
#[test]
fn test_translate_same_os() {
let result = translate_command("ls -la", Os::Linux, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.command, "ls -la");
}
#[test]
fn test_translate_empty_command() {
let result = translate_command("", Os::Windows, Os::Linux);
assert!(result.is_err());
match result {
Err(TranslationError::EmptyCommand) => {}
_ => panic!("Expected EmptyCommand error"),
}
}
#[test]
fn test_translate_command_not_found() {
let result = translate_command("nonexistent", Os::Windows, Os::Linux);
assert!(result.is_err());
match result {
Err(TranslationError::CommandNotFound(_)) => {}
_ => panic!("Expected CommandNotFound error"),
}
}
#[test]
fn test_translate_command_str() {
let result = translate_command_str("dir", "windows", "linux");
assert!(result.is_ok());
assert_eq!(result.unwrap().command, "ls");
}
#[test]
fn test_translate_command_str_invalid_os() {
let result = translate_command_str("dir", "invalid", "linux");
assert!(result.is_err());
match result {
Err(TranslationError::InvalidOs(_)) => {}
_ => panic!("Expected InvalidOs error"),
}
}
#[test]
fn test_translate_batch() {
let commands = vec!["dir", "cls", "copy"];
let results = translate_batch(&commands, Os::Windows, Os::Linux);
assert_eq!(results.len(), 3);
assert!(results.iter().all(|r| r.is_ok()));
}
#[test]
fn test_unix_to_unix_passthrough() {
let result = translate_command("some_unix_cmd", Os::Linux, Os::MacOS);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.command, "some_unix_cmd");
}
#[test]
fn test_translate_tasklist_to_ps() {
let result = translate_command("tasklist", Os::Windows, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.command.contains("ps"));
}
#[test]
fn test_translate_ps_to_tasklist() {
let result = translate_command("ps", Os::Linux, Os::Windows);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.command.contains("tasklist"));
}
#[test]
fn test_translate_ping_flags() {
let result = translate_command("ping -n 5 localhost", Os::Windows, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.command.contains("ping"));
assert!(result.command.contains("-c"));
}
#[test]
fn test_compound_command_and() {
let result = translate_compound_command("dir && cls", Os::Windows, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.command.contains("ls"));
assert!(result.command.contains("&&"));
assert!(result.command.contains("clear"));
}
#[test]
fn test_compound_command_or() {
let result = translate_compound_command("dir || cls", Os::Windows, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.command.contains("ls"));
assert!(result.command.contains("||"));
assert!(result.command.contains("clear"));
}
#[test]
fn test_compound_command_pipe() {
let result = translate_compound_command("dir | findstr test", Os::Windows, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.command.contains("ls"));
assert!(result.command.contains("|"));
assert!(result.command.contains("grep"));
}
#[test]
fn test_compound_command_semicolon() {
let result = translate_compound_command("ls; clear", Os::Linux, Os::Windows);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.command.contains("dir"));
assert!(result.command.contains(";"));
assert!(result.command.contains("cls"));
}
#[test]
fn test_compound_command_single() {
let result = translate_compound_command("dir", Os::Windows, Os::Linux);
assert!(result.is_ok());
assert_eq!(result.unwrap().command, "ls");
}
#[test]
fn test_split_compound_command() {
let parts = split_compound_command("dir && cls || type");
assert_eq!(parts.len(), 5);
assert_eq!(parts[0].trim(), "dir");
assert_eq!(parts[1], "&&");
assert_eq!(parts[2].trim(), "cls");
assert_eq!(parts[3], "||");
assert_eq!(parts[4].trim(), "type");
}
#[test]
fn test_native_command_passthrough() {
let result = translate_command("dir", Os::Linux, Os::Windows);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.command, "dir");
assert!(result.warnings.iter().any(|w| w.contains("already")));
}
#[test]
fn test_native_command_passthrough_with_flags() {
let result = translate_command("dir /w", Os::Linux, Os::Windows);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.command, "dir /w");
}
#[test]
fn test_native_unix_command_passthrough_to_linux() {
let result = translate_command("ls", Os::Windows, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.command, "ls");
assert!(result.warnings.iter().any(|w| w.contains("already")));
}
#[test]
fn test_native_unix_command_passthrough_with_flags() {
let result = translate_command("ls -la", Os::Windows, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.command, "ls -la");
}
#[test]
fn test_common_command_with_different_flags() {
let result = translate_command("ping -n 5 localhost", Os::Windows, Os::Linux);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.command.contains("ping"));
assert!(result.command.contains("-c")); }
#[test]
fn test_translate_script_extension_bat_to_sh() {
let result = translate_script_extension("script.bat", Os::Windows, Os::Linux);
assert_eq!(result, "script.sh");
}
#[test]
fn test_translate_script_extension_cmd_to_sh() {
let result = translate_script_extension("build.cmd", Os::Windows, Os::Linux);
assert_eq!(result, "build.sh");
}
#[test]
fn test_translate_script_extension_ps1_to_sh() {
let result = translate_script_extension("deploy.ps1", Os::Windows, Os::Linux);
assert_eq!(result, "deploy.sh");
}
#[test]
fn test_translate_script_extension_sh_to_bat() {
let result = translate_script_extension("script.sh", Os::Linux, Os::Windows);
assert_eq!(result, "script.bat");
}
#[test]
fn test_translate_script_extension_exe_removal() {
let result = translate_script_extension("program.exe", Os::Windows, Os::Linux);
assert_eq!(result, "program");
}
#[test]
fn test_translate_script_extension_add_exe() {
let result = translate_script_extension("program", Os::Linux, Os::Windows);
assert_eq!(result, "program.exe");
}
#[test]
fn test_translate_script_extension_same_os() {
let result = translate_script_extension("script.bat", Os::Windows, Os::Windows);
assert_eq!(result, "script.bat");
}
#[test]
fn test_translate_shebang_unix_to_windows() {
let result = translate_shebang("#!/bin/bash", Os::Linux, Os::Windows);
assert_eq!(result, "@echo off");
}
#[test]
fn test_translate_shebang_windows_to_unix() {
let result = translate_shebang("@echo off", Os::Windows, Os::Linux);
assert_eq!(result, "#!/bin/bash");
}
#[test]
fn test_translate_shebang_same_os() {
let result = translate_shebang("#!/bin/bash", Os::Linux, Os::Linux);
assert_eq!(result, "#!/bin/bash");
}
#[test]
fn test_translate_full_windows_to_linux_with_path() {
let result = translate_full("copy C:\\Users\\file.txt D:\\backup\\", Os::Windows, Os::Linux);
assert!(result.is_ok());
let r = result.unwrap();
assert!(r.command.contains("cp"));
assert!(r.command.contains("/mnt/c/"));
assert!(r.command.contains("/mnt/d/"));
}
#[test]
fn test_translate_full_linux_to_windows_with_path() {
let result = translate_full("cp /mnt/c/Users/file.txt /tmp/backup", Os::Linux, Os::Windows);
assert!(result.is_ok());
let r = result.unwrap();
assert!(r.command.contains("copy"));
assert!(r.command.contains("C:"));
}
#[test]
fn test_translate_full_dir_with_path() {
let result = translate_full("dir C:\\Windows", Os::Windows, Os::Linux);
assert!(result.is_ok());
let r = result.unwrap();
assert!(r.command.contains("ls"));
assert!(r.command.contains("/mnt/c/"));
}
#[test]
fn test_translate_full_ls_with_path() {
let result = translate_full("ls /home/user/documents", Os::Linux, Os::Windows);
assert!(result.is_ok());
let r = result.unwrap();
assert!(r.command.contains("dir"));
assert!(r.command.contains("Users"));
}
#[test]
fn test_translate_full_preserves_flags_and_paths() {
let result = translate_full("copy /y C:\\src\\file.txt D:\\dest\\", Os::Windows, Os::Linux);
assert!(result.is_ok());
let r = result.unwrap();
assert!(r.command.contains("cp"));
assert!(r.command.contains("-f")); assert!(r.command.contains("/mnt/c/"));
assert!(r.command.contains("/mnt/d/"));
}
#[test]
fn test_is_path_argument() {
assert!(is_path_argument("C:\\Users", Os::Windows));
assert!(is_path_argument("D:\\Documents\\file.txt", Os::Windows));
assert!(is_path_argument("/home/user", Os::Linux));
assert!(is_path_argument("~/Documents", Os::Linux));
assert!(is_path_argument("./local/file", Os::Linux));
assert!(!is_path_argument("-la", Os::Linux));
assert!(!is_path_argument("/w", Os::Windows));
assert!(!is_path_argument("--help", Os::Linux));
}
}