Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/cli/src/download_cache.rs
3309 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
use std::{
7
fs::create_dir_all,
8
path::{Path, PathBuf},
9
};
10
11
use futures::Future;
12
use tokio::fs::remove_dir_all;
13
14
use crate::{
15
state::PersistedState,
16
util::errors::{wrap, AnyError, WrappedError},
17
};
18
19
const KEEP_LRU: usize = 5;
20
const STAGING_SUFFIX: &str = ".staging";
21
const RENAME_ATTEMPTS: u32 = 20;
22
const RENAME_DELAY: std::time::Duration = std::time::Duration::from_millis(200);
23
const PERSISTED_STATE_FILE_NAME: &str = "lru.json";
24
25
#[derive(Clone)]
26
pub struct DownloadCache {
27
path: PathBuf,
28
state: PersistedState<Vec<String>>,
29
}
30
31
impl DownloadCache {
32
pub fn new(path: PathBuf) -> DownloadCache {
33
DownloadCache {
34
state: PersistedState::new(path.join(PERSISTED_STATE_FILE_NAME)),
35
path,
36
}
37
}
38
39
/// Gets the value stored on the state
40
pub fn get(&self) -> Vec<String> {
41
self.state.load()
42
}
43
44
/// Gets the download cache path. Names of cache entries can be formed by
45
/// joining them to the path.
46
pub fn path(&self) -> &Path {
47
&self.path
48
}
49
50
/// Gets whether a cache exists with the name already. Marks it as recently
51
/// used if it does exist.
52
pub fn exists(&self, name: &str) -> Option<PathBuf> {
53
let p = self.path.join(name);
54
if !p.exists() {
55
return None;
56
}
57
58
let _ = self.touch(name.to_string());
59
Some(p)
60
}
61
62
/// Removes the item from the cache, if it exists
63
pub fn delete(&self, name: &str) -> Result<(), WrappedError> {
64
let f = self.path.join(name);
65
if f.exists() {
66
std::fs::remove_dir_all(f).map_err(|e| wrap(e, "error removing cached folder"))?;
67
}
68
69
self.state.update(|l| {
70
l.retain(|n| n != name);
71
})
72
}
73
74
/// Calls the function to create the cached folder if it doesn't exist,
75
/// returning the path where the folder is. Note that the path passed to
76
/// the `do_create` method is a staging path and will not be the same as the
77
/// final returned path.
78
pub async fn create<F, T>(
79
&self,
80
name: impl AsRef<str>,
81
do_create: F,
82
) -> Result<PathBuf, AnyError>
83
where
84
F: FnOnce(PathBuf) -> T,
85
T: Future<Output = Result<(), AnyError>> + Send,
86
{
87
let name = name.as_ref();
88
let target_dir = self.path.join(name);
89
if target_dir.exists() {
90
return Ok(target_dir);
91
}
92
93
let temp_dir = self.path.join(format!("{name}{STAGING_SUFFIX}"));
94
let _ = remove_dir_all(&temp_dir).await; // cleanup any existing
95
96
create_dir_all(&temp_dir).map_err(|e| wrap(e, "error creating server directory"))?;
97
do_create(temp_dir.clone()).await?;
98
99
let _ = self.touch(name.to_string());
100
// retry the rename, it seems on WoA sometimes it takes a second for the
101
// directory to be 'unlocked' after doing file/process operations in it.
102
for attempt_no in 0..=RENAME_ATTEMPTS {
103
match std::fs::rename(&temp_dir, &target_dir) {
104
Ok(_) => {
105
break;
106
}
107
Err(e) if attempt_no == RENAME_ATTEMPTS => {
108
return Err(wrap(e, "error renaming downloaded server").into())
109
}
110
Err(_) => {
111
tokio::time::sleep(RENAME_DELAY).await;
112
}
113
}
114
}
115
116
Ok(target_dir)
117
}
118
119
fn touch(&self, name: String) -> Result<(), AnyError> {
120
self.state.update(|l| {
121
if let Some(index) = l.iter().position(|s| s == &name) {
122
l.remove(index);
123
}
124
l.insert(0, name);
125
126
if l.len() <= KEEP_LRU {
127
return;
128
}
129
130
if let Some(f) = l.last() {
131
let f = self.path.join(f);
132
if !f.exists() || std::fs::remove_dir_all(f).is_ok() {
133
l.pop();
134
}
135
}
136
})?;
137
138
Ok(())
139
}
140
}
141
142