Hey, some tests are passing. I can open a zip file and check it for validity, presence, file size, and kind.
This commit is contained in:
parent
841fa7e91e
commit
e85da1cf34
|
|
@ -9,3 +9,4 @@ target
|
||||||
# rustc will dump stack traces when hitting an internal compiler error to PWD
|
# rustc will dump stack traces when hitting an internal compiler error to PWD
|
||||||
rustc-ice-*.txt
|
rustc-ice-*.txt
|
||||||
|
|
||||||
|
todo.md
|
||||||
|
|
|
||||||
|
|
@ -806,6 +806,7 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
|
"rc-zip",
|
||||||
"rc-zip-tokio",
|
"rc-zip-tokio",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -13,6 +13,7 @@ futures-util = "0.3.32"
|
||||||
http = "1.4.0"
|
http = "1.4.0"
|
||||||
|
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
|
rc-zip = "5.4.1"
|
||||||
|
|
||||||
rc-zip-tokio = "4.3.1"
|
rc-zip-tokio = "4.3.1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
use rc_zip::parse::EntryKind;
|
||||||
|
use rc_zip_tokio::ReadZip;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub struct EntryMeta {
|
||||||
|
pub size: u64,
|
||||||
|
pub is_dir: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Entries {
|
||||||
|
pub entries: HashMap<String, EntryMeta>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entries {
|
||||||
|
pub async fn new(data: &[u8]) -> Result<Arc<Self>, rc_zip::Error> {
|
||||||
|
// The only thing zip cares about is can it be read as a zip file? Does it have a Cursor?
|
||||||
|
let records = data.read_zip().await?;
|
||||||
|
let mut entries = HashMap::<String, EntryMeta>::new();
|
||||||
|
for record in records.entries() {
|
||||||
|
let name = record.sanitized_name();
|
||||||
|
if name.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let kind = record.kind();
|
||||||
|
if kind == EntryKind::Symlink {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = name.unwrap();
|
||||||
|
entries.insert(
|
||||||
|
name.to_string(),
|
||||||
|
EntryMeta {
|
||||||
|
is_dir: kind == EntryKind::Directory,
|
||||||
|
size: record.uncompressed_size,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(Arc::new(Entries { entries }))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, path: &str) -> Option<&EntryMeta> {
|
||||||
|
path.strip_prefix('/')
|
||||||
|
.and_then(|stripped| self.entries.get(stripped))
|
||||||
|
.or_else(|| self.entries.get(path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
const ZIPPATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../demo/assets/demo.zip");
|
||||||
|
|
||||||
|
use super::Entries;
|
||||||
|
use crate::source::ZipSource;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn finds_zip_file_by_zip_path() {
|
||||||
|
let zipfile = ZipSource::File(ZIPPATH.into());
|
||||||
|
let archive = zipfile.load().await.unwrap();
|
||||||
|
let entries = Entries::new(&archive).await.unwrap();
|
||||||
|
let index = entries.get("index.html");
|
||||||
|
assert!(index.is_some());
|
||||||
|
let indexfile = index.unwrap();
|
||||||
|
assert!(indexfile.size == 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn finds_zip_file_with_rooted_path() {
|
||||||
|
let zipfile = ZipSource::File(ZIPPATH.into());
|
||||||
|
let archive = zipfile.load().await.unwrap();
|
||||||
|
let entries = Entries::new(&archive).await.unwrap();
|
||||||
|
assert!(entries.get("/index.html").is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn returns_none_on_bad_filename() {
|
||||||
|
let zipfile = ZipSource::File(ZIPPATH.into());
|
||||||
|
let archive = zipfile.load().await.unwrap();
|
||||||
|
let entries = Entries::new(&archive).await.unwrap();
|
||||||
|
assert!(entries.get("index.garbage").is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn returns_isdir_when_expected() {
|
||||||
|
let zipfile = ZipSource::File(ZIPPATH.into());
|
||||||
|
let archive = zipfile.load().await.unwrap();
|
||||||
|
let entries = Entries::new(&archive).await.unwrap();
|
||||||
|
assert!(entries.get("docs/").is_some());
|
||||||
|
assert!(entries.get("docs/").unwrap().is_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,2 @@
|
||||||
pub fn add(left: u64, right: u64) -> u64 {
|
pub mod index;
|
||||||
left + right
|
pub mod source;
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
let result = add(2, 2);
|
|
||||||
assert_eq!(result, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ZipSource {
|
||||||
|
Local(&'static [u8]),
|
||||||
|
File(PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZipSource {
|
||||||
|
pub async fn load(self) -> Result<Arc<[u8]>, std::io::Error> {
|
||||||
|
match self {
|
||||||
|
ZipSource::Local(bytes) => Ok(Arc::from(bytes)),
|
||||||
|
ZipSource::File(path) => {
|
||||||
|
let bytes = tokio::fs::read(&path).await?;
|
||||||
|
Ok(Arc::from(bytes.as_slice()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue