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.

145 lines
4.1 KiB

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