cargo-gra 0.6.2

Cargo subcommand for gtk-rust-app.
Documentation
// SPDX-License-Identifier: GPL-3.0-or-later

use std::{
    fs::File,
    io::{BufRead, Write},
    path::Path,
};

use crate::ProjectDescriptor;

pub fn build_gettext(project_dir: &Path, project_descriptor: &ProjectDescriptor) {
    let gra_gen_abs_dir = project_dir.join("target/gra-gen");
    let gra_gen_rel_dir = std::path::Path::new("target/gra-gen");
    let id = &project_descriptor.app.id;
    let domain = &project_descriptor.package.name;
    let target_pot_dir = &gra_gen_abs_dir.join("po");
    std::fs::create_dir_all(target_pot_dir).expect("Could not create target/po.");

    let potfiles = target_pot_dir.join("POTFILES");
    let mut potfiles_file = File::create(&potfiles).expect("Could not create target/po/POTFILES");

    let mut pofiles: Vec<String> = match std::process::Command::new("grep")
        .current_dir(project_dir)
        .args(["-lr", "-E", "gettext|translatable", "src/"])
        .output()
    {
        Ok(output) => String::from_utf8(output.stdout)
            .unwrap()
            .lines()
            .map(String::from)
            .collect(),
        Err(e) => {
            eprintln!("[gra] {:?}", e);
            return;
        }
    };

    pofiles.push(
        gra_gen_rel_dir
            .join(format!("data/{}.appdata.xml", id))
            .to_str()
            .unwrap()
            .to_string(),
    );

    if let Err(e) = potfiles_file.write_all(pofiles.join("\n").as_bytes()) {
        eprintln!("[gra] {:?}", e);
    }

    if let Err(e) = std::fs::create_dir_all(target_pot_dir) {
        eprintln!("[gra] {:?}", e);
        return;
    }

    println!("[gra] Update po files");
    let lines = read_lines(project_dir.join("po/LINGUAS"));
    if lines.is_err() {
        eprintln!("[gra] Can not read po/LINGUAS file: {}", lines.unwrap_err());
        return;
    }
    for line in lines.unwrap().map_while(Result::ok) {
        if !line.starts_with('#') {
            let locale = &line;
            let po_file = project_dir.join(format!("po/{}.po", locale)); // format!("po/{}.po", locale);
            let pot_file = target_pot_dir.join(format!("{}.pot", locale));
            let target_mo_dir = gra_gen_abs_dir
                .join("locale")
                .join(locale)
                .join("LC_MESSAGES");
            let target_mo = gra_gen_abs_dir
                .join("locale")
                .join(locale)
                .join("LC_MESSAGES")
                .join(format!("{}.mo", domain));

            if let Err(e) = std::fs::create_dir_all(target_mo_dir) {
                eprintln!("[gra] {:?}", e);
            }

            println!("[gra] Generate/Update translations {:?}", &po_file);

            println!(
                "[gra] xgettext -f {:?} -o {:?} in {:?}",
                &potfiles, &pot_file, &project_dir
            );
            match std::process::Command::new("xgettext")
                .current_dir(project_dir)
                .arg("-f")
                .arg(&potfiles)
                .arg("-o")
                .arg(&pot_file)
                .output()
            {
                Err(e) => {
                    println!("[gra] {:?}", e);
                }
                Ok(output) => {
                    let o = String::from_utf8(output.stdout).unwrap();
                    if !o.trim().is_empty() {
                        println!("[gra] {}", o);
                    }
                    if !output.stderr.is_empty() {
                        for line in String::from_utf8(output.stderr)
                            .unwrap_or_else(|_| "".into())
                            .lines()
                        {
                            if line.is_empty() || line.contains("'rs'") || line.contains("warning")
                            {
                                continue;
                            }
                            eprintln!("[gra] {}", line);
                        }
                    }
                }
            }
            println!("[gra] msgmerge {:?} -U {:?}", &po_file, &pot_file);
            match std::process::Command::new("msgmerge")
                .current_dir(project_dir)
                .arg(&po_file)
                .arg(&pot_file)
                .arg("-U")
                .output()
            {
                Err(e) => {
                    eprintln!("[gra] {:?}", e);
                }
                Ok(output) => {
                    let o = String::from_utf8(output.stdout).unwrap();
                    if !o.trim().is_empty() {
                        println!("[gra] {}", o);
                    }
                    if !output.stderr.is_empty() {
                        let s = String::from_utf8(output.stderr).unwrap_or_else(|_| "".into());
                        if !s.contains("...") {
                            eprintln!("[gra] {}", s)
                        }
                    }
                }
            }
            println!(
                "[gra] msgfmt -o {:?} {:?} in {:?}",
                &target_mo, &pot_file, &project_dir
            );
            match std::process::Command::new("msgfmt")
                .current_dir(project_dir)
                .arg("-o")
                .arg(&target_mo)
                .arg(&po_file)
                .output()
            {
                Err(e) => {
                    eprintln!("[gra] {:?}", e);
                }
                Ok(output) => {
                    let o = String::from_utf8(output.stdout).unwrap();
                    if !o.trim().is_empty() {
                        println!("[gra] {}", o);
                    }
                    if !output.stderr.is_empty() {
                        let s = String::from_utf8(output.stderr).unwrap_or_else(|_| "".into());
                        eprintln!("[gra] {}", s)
                    }
                }
            }
        }
    }
}

fn read_lines<P>(filename: P) -> std::io::Result<std::io::Lines<std::io::BufReader<std::fs::File>>>
where
    P: AsRef<std::path::Path>,
{
    let file = std::fs::File::open(filename)?;
    Ok(std::io::BufReader::new(file).lines())
}