cmds.rs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. use crate::{
  2. core::{ClashInfo, ProfileItem, Profiles, VergeConfig},
  3. states::{ClashState, ProfilesState, VergeState},
  4. utils::{dirs::app_home_dir, fetch::fetch_profile, sysopt::SysProxyConfig},
  5. };
  6. use serde_yaml::Mapping;
  7. use std::process::Command;
  8. use tauri::State;
  9. /// get all profiles from `profiles.yaml`
  10. /// do not acquire the lock of ProfileLock
  11. #[tauri::command]
  12. pub fn get_profiles(profiles_state: State<'_, ProfilesState>) -> Result<Profiles, String> {
  13. match profiles_state.0.lock() {
  14. Ok(profiles) => Ok(profiles.clone()),
  15. Err(_) => Err("failed to get profiles lock".into()),
  16. }
  17. }
  18. /// synchronize data irregularly
  19. #[tauri::command]
  20. pub fn sync_profiles(profiles_state: State<'_, ProfilesState>) -> Result<(), String> {
  21. match profiles_state.0.lock() {
  22. Ok(mut profiles) => profiles.sync_file(),
  23. Err(_) => Err("failed to get profiles lock".into()),
  24. }
  25. }
  26. /// import the profile from url
  27. /// and save to `profiles.yaml`
  28. #[tauri::command]
  29. pub async fn import_profile(
  30. url: String,
  31. with_proxy: bool,
  32. profiles_state: State<'_, ProfilesState>,
  33. ) -> Result<(), String> {
  34. match fetch_profile(&url, with_proxy).await {
  35. Some(result) => {
  36. let mut profiles = profiles_state.0.lock().unwrap();
  37. profiles.import_from_url(url, result)
  38. }
  39. None => Err(format!("failed to fetch profile from `{}`", url)),
  40. }
  41. }
  42. /// Update the profile
  43. #[tauri::command]
  44. pub async fn update_profile(
  45. index: usize,
  46. with_proxy: bool,
  47. clash_state: State<'_, ClashState>,
  48. profiles_state: State<'_, ProfilesState>,
  49. ) -> Result<(), String> {
  50. // maybe we can get the url from the web app directly
  51. let url = match profiles_state.0.lock() {
  52. Ok(mut profile) => {
  53. let items = profile.items.take().unwrap_or(vec![]);
  54. if index >= items.len() {
  55. return Err("the index out of bound".into());
  56. }
  57. let url = match &items[index].url {
  58. Some(u) => u.clone(),
  59. None => return Err("failed to update profile for `invalid url`".into()),
  60. };
  61. profile.items = Some(items);
  62. url
  63. }
  64. Err(_) => return Err("failed to get profiles lock".into()),
  65. };
  66. match fetch_profile(&url, with_proxy).await {
  67. Some(result) => match profiles_state.0.lock() {
  68. Ok(mut profiles) => {
  69. profiles.update_item(index, result)?;
  70. // reactivate the profile
  71. let current = profiles.current.clone().unwrap_or(0);
  72. if current == index {
  73. let clash = clash_state.0.lock().unwrap();
  74. profiles.activate(&clash)
  75. } else {
  76. Ok(())
  77. }
  78. }
  79. Err(_) => Err("failed to get profiles lock".into()),
  80. },
  81. None => Err(format!("failed to fetch profile from `{}`", url)),
  82. }
  83. }
  84. /// change the current profile
  85. #[tauri::command]
  86. pub fn select_profile(
  87. index: usize,
  88. clash_state: State<'_, ClashState>,
  89. profiles_state: State<'_, ProfilesState>,
  90. ) -> Result<(), String> {
  91. let mut profiles = profiles_state.0.lock().unwrap();
  92. match profiles.put_current(index) {
  93. Ok(()) => {
  94. let clash = clash_state.0.lock().unwrap();
  95. profiles.activate(&clash)
  96. }
  97. Err(err) => Err(err),
  98. }
  99. }
  100. /// delete profile item
  101. #[tauri::command]
  102. pub fn delete_profile(
  103. index: usize,
  104. clash_state: State<'_, ClashState>,
  105. profiles_state: State<'_, ProfilesState>,
  106. ) -> Result<(), String> {
  107. let mut profiles = profiles_state.0.lock().unwrap();
  108. match profiles.delete_item(index) {
  109. Ok(change) => match change {
  110. true => {
  111. let clash = clash_state.0.lock().unwrap();
  112. profiles.activate(&clash)
  113. }
  114. false => Ok(()),
  115. },
  116. Err(err) => Err(err),
  117. }
  118. }
  119. /// patch the profile config
  120. #[tauri::command]
  121. pub fn patch_profile(
  122. index: usize,
  123. profile: ProfileItem,
  124. profiles_state: State<'_, ProfilesState>,
  125. ) -> Result<(), String> {
  126. match profiles_state.0.lock() {
  127. Ok(mut profiles) => profiles.patch_item(index, profile),
  128. Err(_) => Err("can not get profiles lock".into()),
  129. }
  130. }
  131. /// run vscode command to edit the profile
  132. #[tauri::command]
  133. pub fn view_profile(index: usize, profiles_state: State<'_, ProfilesState>) -> Result<(), String> {
  134. let mut profiles = profiles_state.0.lock().unwrap();
  135. let items = profiles.items.take().unwrap_or(vec![]);
  136. if index >= items.len() {
  137. profiles.items = Some(items);
  138. return Err("the index out of bound".into());
  139. }
  140. let file = items[index].file.clone().unwrap_or("".into());
  141. profiles.items = Some(items);
  142. let path = app_home_dir().join("profiles").join(file);
  143. if !path.exists() {
  144. return Err("failed to open the file".into());
  145. }
  146. match which::which("code") {
  147. Ok(code) => match Command::new(code).arg(path).status() {
  148. Ok(_) => Ok(()),
  149. Err(_) => Err("failed to open file by VScode".into()),
  150. },
  151. Err(_) => Err("please install VScode for edit".into()),
  152. }
  153. }
  154. /// restart the sidecar
  155. #[tauri::command]
  156. pub fn restart_sidecar(
  157. clash_state: State<'_, ClashState>,
  158. profiles_state: State<'_, ProfilesState>,
  159. ) -> Result<(), String> {
  160. let mut clash = clash_state.0.lock().unwrap();
  161. let mut profiles = profiles_state.0.lock().unwrap();
  162. match clash.restart_sidecar(&mut profiles) {
  163. Ok(_) => Ok(()),
  164. Err(err) => {
  165. log::error!("{}", err);
  166. Err(err)
  167. }
  168. }
  169. }
  170. /// get the clash core info from the state
  171. /// the caller can also get the infomation by clash's api
  172. #[tauri::command]
  173. pub fn get_clash_info(clash_state: State<'_, ClashState>) -> Result<ClashInfo, String> {
  174. match clash_state.0.lock() {
  175. Ok(clash) => Ok(clash.info.clone()),
  176. Err(_) => Err("failed to get clash lock".into()),
  177. }
  178. }
  179. /// update the clash core config
  180. /// after putting the change to the clash core
  181. /// then we should save the latest config
  182. #[tauri::command]
  183. pub fn patch_clash_config(
  184. payload: Mapping,
  185. clash_state: State<'_, ClashState>,
  186. verge_state: State<'_, VergeState>,
  187. profiles_state: State<'_, ProfilesState>,
  188. ) -> Result<(), String> {
  189. let mut clash = clash_state.0.lock().unwrap();
  190. let mut verge = verge_state.0.lock().unwrap();
  191. let mut profiles = profiles_state.0.lock().unwrap();
  192. clash.patch_config(payload, &mut verge, &mut profiles)
  193. }
  194. /// get the system proxy
  195. #[tauri::command]
  196. pub fn get_sys_proxy() -> Result<SysProxyConfig, String> {
  197. match SysProxyConfig::get_sys() {
  198. Ok(value) => Ok(value),
  199. Err(err) => Err(err.to_string()),
  200. }
  201. }
  202. /// get the current proxy config
  203. /// which may not the same as system proxy
  204. #[tauri::command]
  205. pub fn get_cur_proxy(verge_state: State<'_, VergeState>) -> Result<Option<SysProxyConfig>, String> {
  206. match verge_state.0.lock() {
  207. Ok(verge) => Ok(verge.cur_sysproxy.clone()),
  208. Err(_) => Err("failed to get verge lock".into()),
  209. }
  210. }
  211. /// get the verge config
  212. #[tauri::command]
  213. pub fn get_verge_config(verge_state: State<'_, VergeState>) -> Result<VergeConfig, String> {
  214. match verge_state.0.lock() {
  215. Ok(arc) => Ok(arc.config.clone()),
  216. Err(_) => Err("failed to get verge lock".into()),
  217. }
  218. }
  219. /// patch the verge config
  220. /// this command only save the config and not responsible for other things
  221. #[tauri::command]
  222. pub async fn patch_verge_config(
  223. payload: VergeConfig,
  224. verge_state: State<'_, VergeState>,
  225. ) -> Result<(), String> {
  226. let mut verge = verge_state.0.lock().unwrap();
  227. verge.patch_config(payload)
  228. }