use std::fs;
use std::path::PathBuf;
use anyhow::{Context, Result};
use crate::{
browsers::WebBrowser,
cli::{CreateArgs, EditArgs},
icons,
os::OperatingSystem,
utils::{generate_codename, normalize_url},
};
mod desktop_file;
use desktop_file::DesktopFile;
pub fn get_local_path() -> Result<PathBuf> {
dirs::data_local_dir().ok_or_else(|| anyhow::anyhow!("No data local directory found"))
}
pub fn create_shortcut(
_os: &OperatingSystem,
browser: &WebBrowser,
args: CreateArgs,
) -> Result<String> {
let codename = generate_codename(&args.name)?;
let profiles_root = browser.get_profiles_root()?;
let profile_dir = profiles_root.join(&codename);
let apps_dir = get_local_path()?.join("applications");
fs::create_dir_all(&apps_dir)?;
let desktop_file_path = apps_dir.join(format!("WebApp-{}.desktop", codename));
let url = normalize_url(&args.url)?;
let resolved_icon = match &args.icon {
Some(icon) => icons::resolve_icon(&codename, icon).context("Failed to resolve icon")?,
None => icons::fetch_favicon(&url, &codename).context("Failed to fetch favicon")?,
};
browser
.create_profile(&profile_dir, args.no_ublock)
.context("Failed to create browser profile")?;
let exec = browser.format_exec_command(
&profile_dir,
&url,
&resolved_icon,
args.private_window,
args.custom_params.as_deref(),
);
DesktopFile::create(
&desktop_file_path,
&args.name,
&url,
&resolved_icon,
&args.category,
&codename,
&exec,
args.private_window,
)
.context("Failed to create desktop file")?;
println!("✓ Created webapp: {} (codename: {})", args.name, codename);
println!(" Desktop file: {:?}", desktop_file_path);
println!(" Profile: {:?}", profile_dir);
Ok(codename)
}
pub fn list_webapps(_os: &OperatingSystem) -> Result<()> {
let apps_dir = get_local_path()?.join("applications");
let entries = fs::read_dir(&apps_dir).context("Failed to read applications directory")?;
println!("Webapps:");
let mut count = 0;
for entry in entries {
let entry = entry?;
let path = entry.path();
if let Some(name) = path.file_name() {
let name = name.to_string_lossy();
if name.starts_with("WebApp-") && name.ends_with(".desktop") {
count += 1;
let codename = name
.strip_prefix("WebApp-")
.unwrap()
.strip_suffix(".desktop")
.unwrap();
if let Ok(content) = fs::read_to_string(&path) {
let app_name = content
.lines()
.find(|l| l.starts_with("Name="))
.and_then(|l| l.strip_prefix("Name="))
.unwrap_or("Unknown");
let app_url = content
.lines()
.find(|l| l.starts_with("X-WebApp-URL="))
.and_then(|l| l.strip_prefix("X-WebApp-URL="))
.unwrap_or("Unknown");
println!(" - {} ({})", app_name, codename);
println!(" Codename: {}", codename);
println!(" URL: {}", app_url);
println!(" File: {:?}", path);
println!();
} else {
println!(" - {}", name);
}
}
}
}
if count == 0 {
println!(" No webapps found.");
} else {
println!("Total: {} webapp(s)", count);
}
Ok(())
}
pub fn run_webapp(_os: &OperatingSystem, codename: &str) -> Result<()> {
use std::process::Command;
let apps_dir = get_local_path()?.join("applications");
let desktop_file = apps_dir.join(format!("WebApp-{}.desktop", codename));
if !desktop_file.exists() {
anyhow::bail!("Webapp not found: {}", codename);
}
let exec_cmd = DesktopFile::get_exec_command(&desktop_file)?;
Command::new("sh")
.arg("-c")
.arg(&exec_cmd)
.spawn()
.context("Failed to launch webapp")?;
println!("✓ Launched webapp: {}", codename);
Ok(())
}
pub fn edit_webapp(
_os: &OperatingSystem,
_browser: &WebBrowser,
codename: &str,
args: EditArgs,
) -> Result<()> {
let apps_dir = get_local_path()?.join("applications");
let desktop_file = apps_dir.join(format!("WebApp-{}.desktop", codename));
if !desktop_file.exists() {
anyhow::bail!("Webapp not found: {}", codename);
}
let edit_args = if let Some(icon) = &args.icon {
let resolved_icon =
icons::resolve_icon(codename, icon).context("Failed to resolve icon")?;
EditArgs {
icon: Some(resolved_icon),
private_window: args.private_window,
name: args.name.clone(),
url: args.url.clone(),
}
} else {
args
};
DesktopFile::edit(&desktop_file, edit_args).context("Failed to edit desktop file")?;
println!("✓ Edited webapp: {}", codename);
Ok(())
}
pub fn update_webapps(browser: &WebBrowser) -> Result<()> {
let profiles_root = browser.get_profiles_root()?;
let entries = fs::read_dir(&profiles_root).context("Failed to read profiles directory")?;
let mut count = 0;
for entry in entries {
let entry = entry?;
let profile_dir = entry.path();
if profile_dir.is_dir() {
browser
.update_profile(&profile_dir)
.context("Failed to update profile")?;
count += 1;
println!(
"✓ Updated: {}",
profile_dir.file_name().unwrap().to_string_lossy()
);
}
}
if count == 0 {
println!("No webapp profiles found.");
} else {
println!("✓ Updated {} profile(s)", count);
}
Ok(())
}
pub fn delete_webapp(_os: &OperatingSystem, browser: &WebBrowser, codename: &str) -> Result<()> {
let profiles_root = browser.get_profiles_root()?;
let apps_dir = get_local_path()?.join("applications");
let profile_dir = profiles_root.join(codename);
let desktop_file = apps_dir.join(format!("WebApp-{}.desktop", codename));
let mut deleted = false;
if profile_dir.exists() {
browser.delete_profile(&profile_dir)?;
println!("✓ Deleted profile: {:?}", profile_dir);
deleted = true;
}
if desktop_file.exists() {
DesktopFile::delete(&desktop_file)?;
println!("✓ Deleted desktop file: {:?}", desktop_file);
deleted = true;
}
icons::delete_icon(codename)?;
if !deleted {
anyhow::bail!("Webapp not found: {}", codename);
}
println!("✓ Deleted webapp: {}", codename);
Ok(())
}