use std::path::PathBuf;
#[derive(Debug, Clone, Default)]
pub struct SfxConfig {
pub title: Option<String>,
pub extract_path: Option<PathBuf>,
pub run_program: Option<String>,
pub run_parameters: Option<String>,
pub progress: bool,
pub begin_prompt: Option<String>,
pub icon: Option<Vec<u8>>,
pub delete_after_run: bool,
pub install_path: Option<String>,
pub error_title: Option<String>,
}
impl SfxConfig {
pub fn new() -> Self {
Self::default()
}
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
pub fn extract_path(mut self, path: impl Into<PathBuf>) -> Self {
self.extract_path = Some(path.into());
self
}
pub fn run_program(mut self, program: impl Into<String>) -> Self {
self.run_program = Some(program.into());
self
}
pub fn run_parameters(mut self, params: impl Into<String>) -> Self {
self.run_parameters = Some(params.into());
self
}
pub fn progress(mut self, show: bool) -> Self {
self.progress = show;
self
}
pub fn begin_prompt(mut self, message: impl Into<String>) -> Self {
self.begin_prompt = Some(message.into());
self
}
pub fn icon(mut self, ico_data: Vec<u8>) -> Self {
self.icon = Some(ico_data);
self
}
pub fn has_settings(&self) -> bool {
self.title.is_some()
|| self.extract_path.is_some()
|| self.run_program.is_some()
|| self.run_parameters.is_some()
|| self.progress
|| self.begin_prompt.is_some()
|| self.delete_after_run
|| self.install_path.is_some()
}
pub fn encode(&self) -> Vec<u8> {
if !self.has_settings() {
return Vec::new();
}
let mut config = String::new();
config.push_str(";!@Install@!UTF-8!\n");
if let Some(ref title) = self.title {
config.push_str(&format!("Title=\"{}\"\n", escape_value(title)));
}
if let Some(ref begin_prompt) = self.begin_prompt {
config.push_str(&format!("BeginPrompt=\"{}\"\n", escape_value(begin_prompt)));
}
if self.progress {
config.push_str("Progress=\"yes\"\n");
}
if let Some(ref run_program) = self.run_program {
config.push_str(&format!("RunProgram=\"{}\"\n", escape_value(run_program)));
}
if let Some(ref install_path) = self.install_path {
config.push_str(&format!("InstallPath=\"{}\"\n", escape_value(install_path)));
}
if let Some(ref extract_path) = self.extract_path {
config.push_str(&format!(
"Directory=\"{}\"\n",
escape_value(&extract_path.display().to_string())
));
}
if self.delete_after_run {
config.push_str("Delete=\"$OUTDIR\"\n");
}
if let Some(ref error_title) = self.error_title {
config.push_str(&format!("ErrorTitle=\"{}\"\n", escape_value(error_title)));
}
config.push_str(";!@InstallEnd@!\n");
config.into_bytes()
}
}
fn escape_value(s: &str) -> String {
s.replace('\\', "\\\\").replace('"', "\\\"")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_config() {
let config = SfxConfig::new();
assert!(!config.has_settings());
assert!(config.encode().is_empty());
}
#[test]
fn test_config_with_title() {
let config = SfxConfig::new().title("My Installer");
assert!(config.has_settings());
let encoded = String::from_utf8(config.encode()).unwrap();
assert!(encoded.contains(";!@Install@!UTF-8!"));
assert!(encoded.contains("Title=\"My Installer\""));
assert!(encoded.contains(";!@InstallEnd@!"));
}
#[test]
fn test_full_config() {
let config = SfxConfig::new()
.title("Test App")
.run_program("setup.exe")
.run_parameters("/silent")
.progress(true)
.begin_prompt("Install Test App?");
let encoded = String::from_utf8(config.encode()).unwrap();
assert!(encoded.contains("Title=\"Test App\""));
assert!(encoded.contains("RunProgram=\"setup.exe\""));
assert!(encoded.contains("Progress=\"yes\""));
assert!(encoded.contains("BeginPrompt=\"Install Test App?\""));
}
#[test]
fn test_escape_special_chars() {
let config = SfxConfig::new().title("Test \"App\" with\\backslash");
let encoded = String::from_utf8(config.encode()).unwrap();
assert!(encoded.contains("Title=\"Test \\\"App\\\" with\\\\backslash\""));
}
#[test]
fn test_builder_pattern() {
let config = SfxConfig::new()
.title("My App")
.extract_path("/tmp/myapp")
.run_program("./install.sh")
.progress(true);
assert_eq!(config.title, Some("My App".to_string()));
assert_eq!(config.extract_path, Some(PathBuf::from("/tmp/myapp")));
assert_eq!(config.run_program, Some("./install.sh".to_string()));
assert!(config.progress);
}
}