A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

119 lines
3.3 KiB

7 years ago
6 years ago
7 years ago
7 years ago
7 years ago
5 years ago
7 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
7 years ago
7 years ago
6 years ago
5 years ago
5 years ago
5 years ago
6 years ago
7 years ago
5 years ago
5 years ago
7 years ago
7 years ago
5 years ago
5 years ago
7 years ago
  1. use crate::command_runner::CommandRunner;
  2. use crate::symbols::Symbol;
  3. use async_trait::async_trait;
  4. use regex::Regex;
  5. use std::cmp::max;
  6. use std::error::Error;
  7. use std::fs::File as FsFile;
  8. use std::io;
  9. use std::io::{BufRead, BufReader};
  10. use std::path::Path;
  11. use std::path::PathBuf;
  12. #[derive(Debug)]
  13. pub struct Translation<'a, C, D, R> {
  14. path: D,
  15. version: &'a str,
  16. locale: C,
  17. command_runner: &'a R,
  18. }
  19. impl<'a, D, C: AsRef<str>, R: CommandRunner> Translation<'a, C, D, R> {
  20. pub fn new(path: D, version: &'a str, locale: C, command_runner: &'a R) -> Self {
  21. Self {
  22. path,
  23. version,
  24. locale,
  25. command_runner,
  26. }
  27. }
  28. }
  29. impl<C: AsRef<str>, D: AsRef<Path>, R: CommandRunner> Translation<'_, C, D, R> {
  30. fn get_pairs(&self) -> Vec<(String, PathBuf)> {
  31. let version_x = self
  32. .version
  33. .trim_end_matches(|c: char| c.is_digit(10))
  34. .to_owned()
  35. + "x";
  36. let locale = self.locale.as_ref();
  37. let path_locale = if locale == "de_DE" {
  38. "de".to_owned()
  39. } else {
  40. locale.to_lowercase().replace('_', "-")
  41. };
  42. let mut res = vec![];
  43. for &(in_slug, out_slug) in &[
  44. ("", ""),
  45. ("cc/", "continents-cities-"),
  46. ("admin/", "admin-"),
  47. ("admin/network/", "admin-network-"),
  48. ] {
  49. for format in &["po", "mo"] {
  50. res.push((
  51. format!("https://translate.wordpress.org/projects/wp/{}/{}{}/default/export-translations?format={}", version_x, in_slug, path_locale, format),
  52. [self.path.as_ref(), format!("{}{}.{}", out_slug, self.locale.as_ref(), format).as_ref()].iter().collect()
  53. ))
  54. }
  55. }
  56. res
  57. }
  58. }
  59. #[async_trait(?Send)]
  60. impl<C: AsRef<str>, D: AsRef<Path>, R: CommandRunner> Symbol for Translation<'_, C, D, R> {
  61. async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  62. let mut newest = String::new();
  63. let match_date = Regex::new("(?m)^\"PO-Revision-Date: (.+)\\+0000\\\\n\"$").unwrap();
  64. for (_, target) in self.get_pairs() {
  65. match FsFile::open(target.clone()) {
  66. Err(e) => {
  67. // Check if file exists
  68. return if e.kind() == io::ErrorKind::NotFound {
  69. Ok(false)
  70. } else {
  71. Err(Box::new(e))
  72. };
  73. }
  74. Ok(file) => {
  75. if target.ends_with(".po") {
  76. let reader = BufReader::new(file);
  77. for content in reader.lines() {
  78. if let Some(match_result) = match_date.captures(&content?) {
  79. newest = max(newest, match_result[1].to_string());
  80. break;
  81. }
  82. }
  83. }
  84. }
  85. }
  86. }
  87. let upstream = self
  88. .command_runner
  89. .get_output(
  90. "curl",
  91. args![format!(
  92. "https://api.wordpress.org/core/version-check/1.7/?version={}&locale={}",
  93. self.version,
  94. self.locale.as_ref()
  95. )],
  96. )
  97. .await?;
  98. Ok(String::from_utf8(upstream)?.contains(&format!(
  99. r###"language":"{}","version":"{}","updated":"{}"###,
  100. self.locale.as_ref(),
  101. self.version,
  102. newest
  103. )))
  104. }
  105. async fn execute(&self) -> Result<(), Box<dyn Error>> {
  106. for (source, target) in self.get_pairs() {
  107. self
  108. .command_runner
  109. .run_successfully("curl", args!["--compressed", "-o", target, source,])
  110. .await?;
  111. }
  112. Ok(())
  113. }
  114. }