Fix unzipping clangd and codelldb on Windows (#31080)

Closes https://github.com/zed-industries/zed/pull/30454

Release Notes:

- N/A
This commit is contained in:
Kirill Bulatov 2025-05-21 21:17:14 +03:00 committed by GitHub
parent c8f56e38b1
commit 6e5996a815
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 93 additions and 70 deletions

View file

@ -18,6 +18,7 @@ test-support = ["tempfile", "git2", "rand", "util_macros"]
[dependencies]
anyhow.workspace = true
async-fs.workspace = true
async_zip.workspace = true
collections.workspace = true
dirs.workspace = true
futures-lite.workspace = true
@ -36,6 +37,7 @@ take-until.workspace = true
tempfile = { workspace = true, optional = true }
unicase.workspace = true
util_macros = { workspace = true, optional = true }
walkdir.workspace = true
workspace-hack.workspace = true
[target.'cfg(unix)'.dependencies]

118
crates/util/src/archive.rs Normal file
View file

@ -0,0 +1,118 @@
use std::path::Path;
use anyhow::Result;
use async_zip::base::read::stream::ZipFileReader;
use futures::{AsyncRead, io::BufReader};
pub async fn extract_zip<R: AsyncRead + Unpin>(destination: &Path, reader: R) -> Result<()> {
let mut reader = ZipFileReader::new(BufReader::new(reader));
let destination = &destination
.canonicalize()
.unwrap_or_else(|_| destination.to_path_buf());
while let Some(mut item) = reader.next_with_entry().await? {
let entry_reader = item.reader_mut();
let entry = entry_reader.entry();
let path = destination.join(entry.filename().as_str().unwrap());
if entry.dir().unwrap() {
std::fs::create_dir_all(&path)?;
} else {
let parent_dir = path.parent().expect("failed to get parent directory");
std::fs::create_dir_all(parent_dir)?;
let mut file = smol::fs::File::create(&path).await?;
futures::io::copy(entry_reader, &mut file).await?;
}
reader = item.skip().await?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use async_zip::ZipEntryBuilder;
use async_zip::base::write::ZipFileWriter;
use futures::AsyncWriteExt;
use smol::io::Cursor;
use tempfile::TempDir;
use super::*;
async fn compress_zip(src_dir: &Path, dst: &Path) -> Result<()> {
let mut out = smol::fs::File::create(dst).await?;
let mut writer = ZipFileWriter::new(&mut out);
for entry in walkdir::WalkDir::new(src_dir) {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
continue;
}
let relative_path = path.strip_prefix(src_dir)?;
let data = smol::fs::read(&path).await?;
let filename = relative_path.display().to_string();
let builder = ZipEntryBuilder::new(filename.into(), async_zip::Compression::Deflate);
writer.write_entry_whole(builder, &data).await?;
}
writer.close().await?;
out.flush().await?;
Ok(())
}
#[track_caller]
fn assert_file_content(path: &Path, content: &str) {
assert!(path.exists(), "file not found: {:?}", path);
let actual = std::fs::read_to_string(path).unwrap();
assert_eq!(actual, content);
}
#[track_caller]
fn make_test_data() -> TempDir {
let dir = tempfile::tempdir().unwrap();
let dst = dir.path();
std::fs::write(dst.join("test"), "Hello world.").unwrap();
std::fs::create_dir_all(dst.join("foo/bar")).unwrap();
std::fs::write(dst.join("foo/bar.txt"), "Foo bar.").unwrap();
std::fs::write(dst.join("foo/dar.md"), "Bar dar.").unwrap();
std::fs::write(dst.join("foo/bar/dar你好.txt"), "你好世界").unwrap();
dir
}
async fn read_archive(path: &PathBuf) -> impl AsyncRead + Unpin {
let data = smol::fs::read(&path).await.unwrap();
Cursor::new(data)
}
#[test]
fn test_extract_zip() {
let test_dir = make_test_data();
let zip_file = test_dir.path().join("test.zip");
smol::block_on(async {
compress_zip(test_dir.path(), &zip_file).await.unwrap();
let reader = read_archive(&zip_file).await;
let dir = tempfile::tempdir().unwrap();
let dst = dir.path();
extract_zip(dst, reader).await.unwrap();
assert_file_content(&dst.join("test"), "Hello world.");
assert_file_content(&dst.join("foo/bar.txt"), "Foo bar.");
assert_file_content(&dst.join("foo/dar.md"), "Bar dar.");
assert_file_content(&dst.join("foo/bar/dar你好.txt"), "你好世界");
});
}
}

View file

@ -1,4 +1,5 @@
pub mod arc_cow;
pub mod archive;
pub mod command;
pub mod fs;
pub mod markdown;