add fallback mime guess in case file(1) is not available

This commit is contained in:
Nguyen Duc Toan 2023-09-13 22:04:16 +07:00
parent e6fccf9d17
commit e00d943a63
4 changed files with 66 additions and 3 deletions

26
Cargo.lock generated
View file

@ -379,6 +379,7 @@ dependencies = [
"futures",
"indexmap 2.0.0",
"libc",
"mime_guess",
"natord",
"notify",
"parking_lot",
@ -1051,6 +1052,22 @@ dependencies = [
"autocfg",
]
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@ -2004,6 +2021,15 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "unicase"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.13"

View file

@ -29,6 +29,7 @@ tracing = "^0"
trash = "^3"
unicode-width = "^0"
yazi-prebuild = "^0"
mime_guess = "2.0.4"
[target.'cfg(target_os = "windows")'.dependencies]
clipboard-win = "^4"

View file

@ -3,7 +3,7 @@ use std::collections::BTreeMap;
use anyhow::{bail, Result};
use futures::TryFutureExt;
use shared::{MimeKind, Url};
use tokio::process::Command;
use tokio::{fs, io::AsyncReadExt, process::Command};
use tracing::error;
async fn _file(files: &[&Url]) -> Result<BTreeMap<Url, String>> {
@ -36,5 +36,41 @@ async fn _file(files: &[&Url]) -> Result<BTreeMap<Url, String>> {
}
pub async fn file(files: &[impl AsRef<Url>]) -> Result<BTreeMap<Url, String>> {
_file(&files.iter().map(AsRef::as_ref).collect::<Vec<_>>()).await
let _files = &files.iter().map(AsRef::as_ref).collect::<Vec<_>>();
_file(_files).or_else(move |_| fallback(_files)).await
}
async fn fallback(files: &[&Url]) -> Result<BTreeMap<Url, String>> {
let mut mimes = BTreeMap::new();
for &file in files {
if let Ok(mime) = guess_mime(file).await {
mimes.insert(file.clone(), mime);
}
}
if mimes.is_empty() {
error!("failed to get mime types: {:?}", files);
bail!("failed to get mime types");
}
Ok(mimes)
}
async fn guess_mime(file: impl AsRef<Url>) -> Result<String> {
// First, try to guess mime type from file extenstion.
// If fail, either return "text/plain" or "application/octet-stream" by
// trying to convert 1kb of data to utf8 string
let file = file.as_ref();
match mime_guess::from_path(file).first() {
Some(mime) => Ok(mime.to_string()),
None => {
let mut buf = [0; 1024];
let num_bytes = fs::File::open(file).await?.read(&mut buf).await?;
if String::from_utf8(buf[..num_bytes].to_vec()).is_ok() {
Ok(String::from("text/plain"))
} else {
Ok(String::from("application/octet-stream"))
}
}
}
}

View file

@ -18,7 +18,7 @@ pub enum MimeKind {
impl MimeKind {
pub fn new(s: &str) -> Self {
if s.starts_with("text/") || s.ends_with("/xml") || s.ends_with("/javascript") {
if s.starts_with("text/") || s.ends_with("/xml") || s.ends_with("/javascript") || s.ends_with("/x-sh") {
Self::Text
} else if s.starts_with("image/") {
Self::Image