help.rs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. use anyhow::{anyhow, bail, Context, Result};
  2. use nanoid::nanoid;
  3. use serde::{de::DeserializeOwned, Serialize};
  4. use serde_yaml::{Mapping, Value};
  5. use std::{fs, path::PathBuf, str::FromStr};
  6. use tauri::{
  7. api::shell::{open, Program},
  8. Manager,
  9. };
  10. /// read data from yaml as struct T
  11. pub fn read_yaml<T: DeserializeOwned>(path: &PathBuf) -> Result<T> {
  12. if !path.exists() {
  13. bail!("file not found \"{}\"", path.display());
  14. }
  15. let yaml_str = fs::read_to_string(path)
  16. .with_context(|| format!("failed to read the file \"{}\"", path.display()))?;
  17. serde_yaml::from_str::<T>(&yaml_str).with_context(|| {
  18. format!(
  19. "failed to read the file with yaml format \"{}\"",
  20. path.display()
  21. )
  22. })
  23. }
  24. /// read mapping from yaml fix #165
  25. pub fn read_merge_mapping(path: &PathBuf) -> Result<Mapping> {
  26. let mut val: Value = read_yaml(path)?;
  27. val.apply_merge()
  28. .with_context(|| format!("failed to apply merge \"{}\"", path.display()))?;
  29. Ok(val
  30. .as_mapping()
  31. .ok_or(anyhow!(
  32. "failed to transform to yaml mapping \"{}\"",
  33. path.display()
  34. ))?
  35. .to_owned())
  36. }
  37. /// save the data to the file
  38. /// can set `prefix` string to add some comments
  39. pub fn save_yaml<T: Serialize>(path: &PathBuf, data: &T, prefix: Option<&str>) -> Result<()> {
  40. let data_str = serde_yaml::to_string(data)?;
  41. let yaml_str = match prefix {
  42. Some(prefix) => format!("{prefix}\n\n{data_str}"),
  43. None => data_str,
  44. };
  45. let path_str = path.as_os_str().to_string_lossy().to_string();
  46. fs::write(path, yaml_str.as_bytes())
  47. .with_context(|| format!("failed to save file \"{path_str}\""))
  48. }
  49. const ALPHABET: [char; 62] = [
  50. '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
  51. 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B',
  52. 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
  53. 'V', 'W', 'X', 'Y', 'Z',
  54. ];
  55. /// generate the uid
  56. pub fn get_uid(prefix: &str) -> String {
  57. let id = nanoid!(11, &ALPHABET);
  58. format!("{prefix}{id}")
  59. }
  60. /// parse the string
  61. /// xxx=123123; => 123123
  62. pub fn parse_str<T: FromStr>(target: &str, key: &str) -> Option<T> {
  63. target.split(';').map(str::trim).find_map(|s| {
  64. let mut parts = s.splitn(2, '=');
  65. match (parts.next(), parts.next()) {
  66. (Some(k), Some(v)) if k == key => v.parse::<T>().ok(),
  67. _ => None,
  68. }
  69. })
  70. }
  71. /// get the last part of the url, if not found, return empty string
  72. pub fn get_last_part_and_decode(url: &str) -> Option<String> {
  73. let path = url.split('?').next().unwrap_or(""); // Splits URL and takes the path part
  74. let segments: Vec<&str> = path.split('/').collect();
  75. let last_segment = segments.last()?;
  76. Some(
  77. percent_encoding::percent_decode_str(last_segment)
  78. .decode_utf8_lossy()
  79. .to_string(),
  80. )
  81. }
  82. /// open file
  83. /// use vscode by default
  84. pub fn open_file(app: tauri::AppHandle, path: PathBuf) -> Result<()> {
  85. #[cfg(target_os = "macos")]
  86. let code = "Visual Studio Code";
  87. #[cfg(not(target_os = "macos"))]
  88. let code = "code";
  89. let _ = match Program::from_str(code) {
  90. Ok(code) => open(&app.shell_scope(), path.to_string_lossy(), Some(code)),
  91. Err(err) => {
  92. log::error!(target: "app", "Can't find VScode `{err}`");
  93. // default open
  94. open(&app.shell_scope(), path.to_string_lossy(), None)
  95. }
  96. };
  97. Ok(())
  98. }
  99. #[macro_export]
  100. macro_rules! error {
  101. ($result: expr) => {
  102. log::error!(target: "app", "{}", $result);
  103. };
  104. }
  105. #[macro_export]
  106. macro_rules! log_err {
  107. ($result: expr) => {
  108. if let Err(err) = $result {
  109. log::error!(target: "app", "{err}");
  110. }
  111. };
  112. ($result: expr, $err_str: expr) => {
  113. if let Err(_) = $result {
  114. log::error!(target: "app", "{}", $err_str);
  115. }
  116. };
  117. }
  118. #[macro_export]
  119. macro_rules! trace_err {
  120. ($result: expr, $err_str: expr) => {
  121. if let Err(err) = $result {
  122. log::trace!(target: "app", "{}, err {}", $err_str, err);
  123. }
  124. }
  125. }
  126. /// wrap the anyhow error
  127. /// transform the error to String
  128. #[macro_export]
  129. macro_rules! wrap_err {
  130. ($stat: expr) => {
  131. match $stat {
  132. Ok(a) => Ok(a),
  133. Err(err) => {
  134. log::error!(target: "app", "{}", err.to_string());
  135. Err(format!("{}", err.to_string()))
  136. }
  137. }
  138. };
  139. }
  140. /// return the string literal error
  141. #[macro_export]
  142. macro_rules! ret_err {
  143. ($str: expr) => {
  144. return Err($str.into())
  145. };
  146. }
  147. #[test]
  148. fn test_parse_value() {
  149. let test_1 = "upload=111; download=2222; total=3333; expire=444";
  150. let test_2 = "attachment; filename=Clash.yaml";
  151. assert_eq!(parse_str::<usize>(test_1, "upload").unwrap(), 111);
  152. assert_eq!(parse_str::<usize>(test_1, "download").unwrap(), 2222);
  153. assert_eq!(parse_str::<usize>(test_1, "total").unwrap(), 3333);
  154. assert_eq!(parse_str::<usize>(test_1, "expire").unwrap(), 444);
  155. assert_eq!(
  156. parse_str::<String>(test_2, "filename").unwrap(),
  157. format!("Clash.yaml")
  158. );
  159. assert_eq!(parse_str::<usize>(test_1, "aaa"), None);
  160. assert_eq!(parse_str::<usize>(test_1, "upload1"), None);
  161. assert_eq!(parse_str::<usize>(test_1, "expire1"), None);
  162. assert_eq!(parse_str::<usize>(test_2, "attachment"), None);
  163. }