any_version_manager/
lib.rs1use serde::{Deserialize, Serialize};
2use smol_str::SmolStr;
3use std::future::Future;
4use std::pin::Pin;
5use std::task::{Context, Poll};
6use std::{path::PathBuf, sync::atomic::AtomicBool};
7
8pub mod io;
9pub mod platform;
10pub mod tool;
11
12#[derive(Debug, Deserialize)]
13pub struct UrlMirrorEntry {
14 from: String,
15 to: String,
16}
17#[derive(Debug, Default, Deserialize)]
18pub struct UrlMirror {
19 mirror: Vec<UrlMirrorEntry>,
20}
21
22#[derive(Debug, Default, Deserialize)]
23pub struct Config {
24 #[serde(flatten)]
25 pub mirror: Option<UrlMirror>,
26 pub data_path: Option<PathBuf>,
27 pub rustup: Option<RustupConfig>,
28}
29
30#[derive(Debug, Default, Deserialize)]
31pub struct RustupConfig {
32 pub path: Option<PathBuf>,
33}
34
35pub async fn spawn_blocking<T: Send + 'static>(
36 f: impl FnOnce() -> anyhow::Result<T> + Send + 'static,
37) -> anyhow::Result<T> {
38 match tokio::task::spawn_blocking(f).await {
39 Ok(r) => r,
40 Err(_) => Err(anyhow::anyhow!("Failed to join spawned IO task")),
41 }
42}
43
44pub struct HttpClient {
45 mirror: UrlMirror,
46 client_inner: reqwest::Client,
47}
48
49impl HttpClient {
50 pub fn new(mirror: UrlMirror) -> HttpClient {
51 HttpClient {
52 mirror,
53 client_inner: reqwest::Client::new(),
54 }
55 }
56
57 pub fn get(&self, url: &str) -> reqwest::RequestBuilder {
58 for entry in &self.mirror.mirror {
59 if let Some(rest) = url.strip_prefix(&entry.from) {
60 let mut result = String::new();
61 result.push_str(entry.to.as_str());
62 result.push_str(rest);
63 log::debug!("Applied mirror {} => {}", url, result);
64 return self.client_inner.get(result);
65 }
66 }
67
68 self.client_inner.get(url)
69 }
70}
71
72pub enum Status {
73 InProgress {
74 name: SmolStr,
75 progress_ratio: Option<(u64, u64)>,
76 },
77 Stopped,
78}
79
80#[derive(Clone, Default, Deserialize, Serialize)]
81pub struct FileHash {
82 #[serde(skip_serializing_if = "Option::is_none")]
83 sha1: Option<SmolStr>,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 sha256: Option<SmolStr>,
86}
87
88static CANCELLED: AtomicBool = AtomicBool::new(false);
89
90pub fn set_cancelled() {
91 CANCELLED.store(true, std::sync::atomic::Ordering::Relaxed);
92}
93
94pub fn is_cancelled() -> bool {
95 CANCELLED.load(std::sync::atomic::Ordering::Relaxed)
96}
97
98pub struct CancellableFuture<Fut> {
99 inner: Fut,
100}
101
102impl<Fut> CancellableFuture<Fut> {
103 pub fn new(inner: Fut) -> Self {
104 CancellableFuture { inner }
105 }
106}
107
108impl<Fut> Future for CancellableFuture<Fut>
109where
110 Fut: Future,
111{
112 type Output = Option<Fut::Output>;
113
114 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
115 if is_cancelled() {
116 Poll::Ready(None)
117 } else {
118 let inner = unsafe { self.map_unchecked_mut(|s| &mut s.inner) };
120 match inner.poll(cx) {
121 Poll::Ready(output) => Poll::Ready(Some(output)),
122 Poll::Pending => Poll::Pending,
123 }
124 }
125 }
126}