Skip to content

Commit

Permalink
Merge pull request #22 from mrtryhard/v0.2.0
Browse files Browse the repository at this point in the history
V0.2.0
  • Loading branch information
mrtryhard authored Feb 2, 2024
2 parents 89975ff + 35e363b commit 28b0ac1
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 93 deletions.
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Change Log
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## Unreleased

## Added

## Changed

## Fixed

## [0.2.0] - 2024-01-01

Completion of the [second milestone](https://github.com/mrtryhard/qt-ts-tools/milestone/2). This introduces the `extract` and lighter binary.

### Added

- Extraction mechanism to extract only relevant translation types. [#16](https://github.com/mrtryhard/qt-ts-tools/issues/16)

### Changed

- Reduced release binary size [#19](https://github.com/mrtryhard/qt-ts-tools/issues/19)
- Updated Serde and Clap dependencies [#20](https://github.com/mrtryhard/qt-ts-tools/issues/20)

### Fixed

## [0.1.0] - 2024-01-30

Introduction of `qt-ts-tools`. This completes the first [milestone](https://github.com/mrtryhard/qt-ts-tools/milestone/1?closed=1).

### Added

- Sort mechanism to sort translation files by location and contexts. [#3](https://github.com/mrtryhard/qt-ts-tools/issues/3)
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ repository = "https://github.com/mrtryhard/qt-ts-tools"
keywords = ["qt", "translation"]
homepage = "https://github.com/mrtryhard/qt-ts-tools"
license = "MIT OR Apache-2.0"
version = "0.1.0"
version = "0.2.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = { version = "4.4.16", features = ["derive"] }
clap = { version = "4.4.18", features = ["derive"] }
quick-xml = { version = "0.31.0", features = ["serialize"] }
serde = { version = "1.0.195", features = ["derive"] }
serde = { version = "1.0.196", features = ["derive"] }

[profile.release]
strip = true
lto = true
102 changes: 102 additions & 0 deletions src/extract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use crate::ts;
use crate::ts::{TSNode, TranslationType};
use clap::Args;

#[derive(Args)]
pub struct ExtractArgs {
pub input_path: String,
#[arg(short('t'), long, value_enum, num_args = 1..)]
pub translation_type: Vec<TranslationTypeArg>,
#[arg(short, long)]
pub output_path: Option<String>,
}

#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
pub enum TranslationTypeArg {
Obsolete,
Unfinished,
Vanished,
}

/// Filters the translation file to keep only the messages containing unfinished translations.
pub fn extract_main(args: &ExtractArgs) -> Result<(), String> {
match quick_xml::Reader::from_file(&args.input_path) {
Ok(file) => {
let nodes: Result<TSNode, _> = quick_xml::de::from_reader(file.into_inner());
match nodes {
Ok(mut ts_node) => {
let wanted_types = args
.translation_type
.iter()
.map(to_translation_type)
.collect();
retain_ts_node(&mut ts_node, &wanted_types);
ts::write_to_output(&args.output_path, &ts_node)
}
Err(e) => Err(format!(
"Could not parse input file \"{}\". Error: {e:?}.",
args.input_path
)),
}
}
Err(e) => Err(format!(
"Could not open or parse input file \"{}\". Error: {e:?}",
args.input_path
)),
}
}

fn to_translation_type(value: &TranslationTypeArg) -> TranslationType {
match value {
TranslationTypeArg::Obsolete => TranslationType::Obsolete,
TranslationTypeArg::Unfinished => TranslationType::Unfinished,
TranslationTypeArg::Vanished => TranslationType::Vanished,
}
}

/// Keep only the desired translation type from the node (if it matches one in `wanted_types`).
fn retain_ts_node(ts_node: &mut TSNode, wanted_types: &Vec<TranslationType>) {
ts_node.contexts.retain_mut(|context| {
context.messages.retain(|message| {
message.translation.as_ref().is_some_and(|translation| {
translation
.translation_type
.as_ref()
.is_some_and(|translation_type| wanted_types.contains(&translation_type))
})
});

context.messages.len() > 0
});
}

#[cfg(test)]
mod extract_test {
use super::*;
use quick_xml;

#[test]
fn test_extract_ts_node() {
let reader_nosort = quick_xml::Reader::from_file("./test_data/example_key_de.xml")
.expect("Couldn't open example_unfinished test file");
let mut data: TSNode =
quick_xml::de::from_reader(reader_nosort.into_inner()).expect("Parsable");

let types = vec![TranslationType::Obsolete];
retain_ts_node(&mut data, &types);

assert_eq!(data.contexts[0].messages.len(), 3);
assert_eq!(
data.contexts[0].messages[0].source.as_ref().unwrap(),
"Shift+K"
);
assert_eq!(
data.contexts[0].messages[1].source.as_ref().unwrap(),
"Ctrl+K"
);
assert_eq!(
data.contexts[0].messages[2].source.as_ref().unwrap(),
"Alt+K"
);
}
}
18 changes: 11 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
mod extract;
mod sort;
mod ts_definition;
mod ts;

use crate::extract::{extract_main, ExtractArgs};
use crate::sort::{sort_main, SortArgs};
use clap::{Parser, Subcommand};

Expand All @@ -14,16 +16,18 @@ struct Cli {
#[derive(Subcommand)]
enum Commands {
Sort(SortArgs),
Extract(ExtractArgs),
}

fn main() {
let cli = Cli::parse();

let result = match &cli.command {
fn get_cli_result(cli: Cli) -> Result<(), String> {
match &cli.command {
Commands::Sort(args) => sort_main(&args),
};
Commands::Extract(args) => extract_main(&args),
}
}

if let Err(e) = result {
fn main() {
if let Err(e) = get_cli_result(Cli::parse()) {
println!("{e}");
std::process::exit(1);
}
Expand Down
74 changes: 4 additions & 70 deletions src/sort.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::ts_definition::*;
use crate::ts;
use crate::ts::TSNode;
use clap::Args;
use serde::Serialize;
use std::io::{BufWriter, Write};

#[derive(Args)]
pub struct SortArgs {
Expand All @@ -23,7 +22,7 @@ pub fn sort_main(args: &SortArgs) -> Result<(), String> {
match nodes {
Ok(mut ts_node) => {
sort_ts_node(&mut ts_node);
write_ts_to_output(&args, &ts_node)
ts::write_to_output(&args.output_path, &ts_node)
}
Err(e) => Err(format!(
"Could not parse input file \"{}\". Error: {e:?}.",
Expand Down Expand Up @@ -54,50 +53,13 @@ fn sort_ts_node(ts_node: &mut TSNode) {
});
}

/// Writes the output TS file to the specified output (file or stdout).
/// This writer will auto indent/pretty print. It will always expand empty nodes, e.g.
/// `<name></name>` instead of `<name/>`.
fn write_ts_to_output(args: &SortArgs, node: &TSNode) -> Result<(), String> {
let mut inner_writer: BufWriter<Box<dyn Write>> = match &args.output_path {
None => BufWriter::new(Box::new(std::io::stdout().lock())),
Some(output_path) => match std::fs::File::options()
.create(true)
.write(true)
.open(output_path)
{
Ok(file) => BufWriter::new(Box::new(file)),
Err(e) => {
return Err(format!(
"Error occured while opening output file \"{output_path}\": {e:?}"
))
}
},
};

let mut output_buffer =
String::from("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n");
let mut ser = quick_xml::se::Serializer::new(&mut output_buffer);
ser.indent(' ', 2).expand_empty_elements(true);

match node.serialize(ser) {
Ok(_) => {
let res = inner_writer.write_all(output_buffer.as_bytes());
match res {
Ok(_) => Ok(()),
Err(e) => Err(format!("Problem occured while serializing output: {e:?}")),
}
}
Err(e) => Err(format!("Problem occured while serializing output: {e:?}")),
}
}

#[cfg(test)]
mod sort_test {
use super::*;
use quick_xml;

#[test]
fn sort_ts_node_ts() {
fn test_sort_ts_node() {
let reader_nosort = quick_xml::Reader::from_file("./test_data/example_unfinished.xml")
.expect("Couldn't open example_unfinished test file");
let mut data_nosort: TSNode =
Expand Down Expand Up @@ -149,31 +111,3 @@ mod sort_test {
assert_eq!(messages[1].locations[3].line, Some(11));
}
}

#[cfg(test)]
mod write_file_test {
use super::*;
use quick_xml;

#[test]
fn write_ts_file_test() {
const OUTPUT_TEST_FILE: &str = "./test_data/test_result_write_to_ts.xml";

let reader = quick_xml::Reader::from_file("./test_data/example1.xml")
.expect("Couldn't open example1 test file");

let data: TSNode = quick_xml::de::from_reader(reader.into_inner()).expect("Parsable");
let args = SortArgs {
input_path: "whatever".to_owned(),
output_path: Some(OUTPUT_TEST_FILE.to_owned()),
};
write_ts_to_output(&args, &data).expect("Output");

let f =
quick_xml::Reader::from_file(OUTPUT_TEST_FILE).expect("Couldn't open output test file");

let output_data: TSNode = quick_xml::de::from_reader(f.into_inner()).expect("Parsable");
std::fs::remove_file(OUTPUT_TEST_FILE).expect("Test should clean test file.");
assert_eq!(data, output_data);
}
}
Loading

0 comments on commit 28b0ac1

Please sign in to comment.