use crate::{pos, utils_mod::OptionLogNone, ResultLogError, GREEN, RED, RESET, YELLOW};
pub fn new_pwa_wasm(remote_repo: Option<String>, web_server_user_at_domain: Option<String>) -> anyhow::Result<()> {
fn encode_to_favicon_ico(img: &image::DynamicImage, rust_project_name: &str) -> anyhow::Result<()> {
fn favicon_add_entry(img: &image::DynamicImage, size: u32, icon_dir: &mut ico::IconDir) -> anyhow::Result<()> {
let img_rgba_vec = img
.resize(size, size, image::imageops::FilterType::Lanczos3)
.into_rgba8()
.into_raw();
let icon_image = ico::IconImage::from_rgba_data(size, size, img_rgba_vec);
icon_dir.add_entry(ico::IconDirEntry::encode(&icon_image)?);
Ok(())
}
let mut icon_dir = ico::IconDir::new(ico::ResourceType::Icon);
favicon_add_entry(img, 16, &mut icon_dir).log(pos!())?;
favicon_add_entry(img, 32, &mut icon_dir).log(pos!())?;
favicon_add_entry(img, 48, &mut icon_dir).log(pos!())?;
let file_name = format!("{rust_project_name}/web_server_folder/{rust_project_name}/favicon.ico");
let buffer = std::fs::File::create(file_name).log(pos!())?;
icon_dir.write(buffer).log(pos!())?;
Ok(())
}
fn decode_png(vec: Vec<u8>) -> anyhow::Result<image::DynamicImage> {
let img = image::ImageReader::new(std::io::Cursor::new(vec));
let img = img.with_guessed_format().log(pos!())?;
Ok(img.decode()?)
}
fn resize_image(img: &image::DynamicImage, img_size: u32, file_name: &str, rust_project_name: &str) -> anyhow::Result<()> {
fn encode_to_png(new_img: image::DynamicImage) -> anyhow::Result<Vec<u8>> {
let vec_u8: Vec<u8> = Vec::new();
let mut cursor_1 = std::io::Cursor::new(vec_u8);
new_img.write_to(&mut cursor_1, image::ImageFormat::Png).log(pos!())?;
Ok(cursor_1.get_ref().to_owned())
}
let new_img = img.resize(img_size, img_size, image::imageops::FilterType::Lanczos3);
let vec_u8 = encode_to_png(new_img).log(pos!())?;
let file_name = format!("{rust_project_name}/web_server_folder/{rust_project_name}/icons/{file_name}");
std::fs::write(file_name, vec_u8).log(pos!())?;
Ok(())
}
let Some(remote_repo) = remote_repo else {
println!("{RED}Error: Argument remote_repo is missing: `cargo auto new_pwa_wasm remote_repo web_server_user_at_domain`{RESET}");
println!("{RED}Example: `cargo auto new_pwa_wasm codeberg.org/bestia-dev-work-in-progress/hello_world luciano@bestia.dev`{RESET}");
return Ok(());
};
let Some(web_server_user_at_domain) = web_server_user_at_domain else {
println!("{RED}Error: Argument web_server_user_at_domain is missing: `cargo auto new_pwa_wasm remote_repo web_server_user_at_domain`{RESET}");
println!("{RED}Example: `cargo auto new_pwa_wasm codeberg.org/bestia-dev-work-in-progress/hello_world luciano@bestia.dev`{RESET}");
return Ok(());
};
if !std::path::Path::new("icon512x512.png").exists() {
println!(
r#"
{RED}The mandatory file `icon512x512.png` is not found in this folder.{RESET}
{YELLOW}If you don't have your icon, you can download and use this default:{RESET}
{GREEN}curl -L https://codeberg.org/automation-tasks-rs/cargo_auto_template_new_pwa_wasm/raw/main/icon512x512.png --output icon512x512.png{RESET}
"#
);
return Ok(());
}
let mut splitted = remote_repo.split("/");
let repo_domain = splitted.next().log_msg(pos!(), "repo_domain missing")?;
let owner_or_organization = splitted.next().log_msg(pos!(), "owner_or_organization missing")?;
let rust_project_name = splitted.next().log_msg(pos!(), "rust_project_name missing")?;
let mut splitted = web_server_user_at_domain.split("@");
let web_server_username = splitted.next().log_msg(pos!(), "server_username missing")?;
let web_server_domain = splitted.next().log_msg(pos!(), "web_server_domain missing")?;
copy_to_files(
repo_domain,
owner_or_organization,
rust_project_name,
web_server_username,
web_server_domain,
)
.log(pos!())?;
let img = std::fs::read("icon512x512.png").log(pos!())?;
let img = decode_png(img).log(pos!())?;
resize_image(&img, 32, "icon-032.png", rust_project_name).log(pos!())?;
resize_image(&img, 72, "icon-072.png", rust_project_name).log(pos!())?;
resize_image(&img, 96, "icon-096.png", rust_project_name).log(pos!())?;
resize_image(&img, 120, "icon-120.png", rust_project_name).log(pos!())?;
resize_image(&img, 128, "icon-128.png", rust_project_name).log(pos!())?;
resize_image(&img, 144, "icon-144.png", rust_project_name).log(pos!())?;
resize_image(&img, 152, "icon-152.png", rust_project_name).log(pos!())?;
resize_image(&img, 167, "icon-167.png", rust_project_name).log(pos!())?;
resize_image(&img, 180, "icon-180.png", rust_project_name).log(pos!())?;
resize_image(&img, 192, "icon-192.png", rust_project_name).log(pos!())?;
resize_image(&img, 196, "icon-196.png", rust_project_name).log(pos!())?;
resize_image(&img, 512, "icon-512.png", rust_project_name).log(pos!())?;
resize_image(&img, 192, "icon-maskable.png", rust_project_name).log(pos!())?;
encode_to_favicon_ico(&img, rust_project_name).log(pos!())?;
println!(
r#"
{YELLOW}You can open this new Rust project in VSCode:{RESET}
{GREEN}code {package_name}{RESET}
{YELLOW}Then build it with:{RESET}
{GREEN}cargo auto build{RESET}
{YELLOW}Then follow the detailed instructions.{RESET}
"#,
package_name = rust_project_name
);
Ok(())
}
fn copy_to_files(
repo_domain: &str,
owner_or_organization: &str,
rust_project_name: &str,
web_server_username: &str,
web_server_domain: &str,
) -> anyhow::Result<()> {
let folder_path = std::path::Path::new(rust_project_name);
if folder_path.exists() {
anyhow::bail!("{RED}Error: Folder {rust_project_name} already exists! {RESET}");
}
std::fs::create_dir_all(folder_path).log(pos!())?;
println!(" {YELLOW}Downloading template.tar.gz...{RESET}");
let file_to_download = "template.tar.gz";
let path = "./template.tar.gz";
let url = "https://codeberg.org/automation-tasks-rs/cargo_auto_template_new_pwa_wasm/releases/latest";
let custom_policy = reqwest::redirect::Policy::custom(|attempt| attempt.stop());
let reqwest_client = reqwest::blocking::ClientBuilder::new()
.redirect(custom_policy)
.build()
.log(pos!())?;
let http_response = reqwest_client.get(url).send().log(pos!())?;
let location = http_response
.headers()
.get(reqwest::header::LOCATION)
.log_msg(pos!(), "header Location None")?;
let location = location.to_str().log(pos!())?;
let location = location.replace("/tag/", "/download/");
let url = format!("https://codeberg.org{location}/{file_to_download}");
let reqwest_client = reqwest::blocking::Client::new();
let http_response = reqwest_client.get(url).send();
if let Ok(body) = http_response {
let body = body.bytes().log(pos!())?;
std::fs::write(path, &body)
.or_else(|err| anyhow::bail!("Download failed for {file_to_download} {err}"))
.log(pos!())?;
} else {
anyhow::bail!("Error while retrieving data: {:#?}", http_response.err());
}
let tar_gz = std::fs::File::open(path).log(pos!())?;
let tar = flate2::read::GzDecoder::new(tar_gz);
let mut archive = tar::Archive::new(tar);
archive.unpack(folder_path).log(pos!())?;
std::fs::remove_file(path).log(pos!())?;
for entry in walkdir::WalkDir::new(folder_path).into_iter().filter_map(Result::ok) {
if entry.file_type().is_file() {
if entry.file_name().to_string_lossy().ends_with(".ico")
|| entry.file_name().to_string_lossy().ends_with(".png")
|| entry.file_name().to_string_lossy().ends_with(".woff2")
{
} else {
println!("replace: {}", entry.path().to_string_lossy());
let mut content = std::fs::read_to_string(entry.path()).log(pos!())?;
content = content.replace(
"codeberg.org/automation-tasks-rs/cargo_auto_template_new_pwa_wasm",
&format!("{repo_domain}/{owner_or_organization}/{rust_project_name}"),
);
content = content.replace(
"automation-tasks-rs/cargo_auto_template_new_pwa_wasm",
&format!("{owner_or_organization}/{rust_project_name}"),
);
content = content.replace("cargo_auto_template_new_pwa_wasm", rust_project_name);
content = content.replace("CARGO_AUTO_TEMPLATE_NEW_PWA_WASM", rust_project_name.to_uppercase().as_str());
let content = content.replace("web_server_domain", web_server_domain);
let content = content.replace("web_server_username", web_server_username);
std::fs::write(entry.path(), content).log(pos!())?;
}
}
}
let mut traverse_reverse: Vec<walkdir::DirEntry> = walkdir::WalkDir::new(folder_path).into_iter().filter_map(Result::ok).collect();
traverse_reverse.reverse();
for entry in traverse_reverse.iter() {
if entry.file_name().to_string_lossy().contains("cargo_auto_template_new_pwa_wasm") {
println!("rename: {}", entry.path().to_string_lossy());
std::fs::rename(
entry.path(),
entry
.path()
.to_string_lossy()
.replace("cargo_auto_template_new_pwa_wasm", rust_project_name),
)
.log(pos!())?;
}
}
Ok(())
}