summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJiayi Zhao <jeff.no.zhao@gmail.com>2020-03-20 13:08:26 -0400
committerJiayi Zhao <jeff.no.zhao@gmail.com>2020-03-20 14:00:05 -0400
commit8fc59f0a0c3729d201f844738db5275035f59f2d (patch)
tree6deeba8db7275387f44b5bdb03c1f801efe437ab /src
parent84e912b7779faaaeb09d1085e09630a548c7fb54 (diff)
add initial support for detecting mimetypes
Diffstat (limited to 'src')
-rw-r--r--src/commands/open_file.rs96
-rw-r--r--src/config/mimetype.rs4
-rw-r--r--src/fs/metadata.rs33
-rw-r--r--src/ui/widgets/tui_footer.rs10
4 files changed, 112 insertions, 31 deletions
diff --git a/src/commands/open_file.rs b/src/commands/open_file.rs
index 5d1e7de..66c514c 100644
--- a/src/commands/open_file.rs
+++ b/src/commands/open_file.rs
@@ -4,6 +4,7 @@ use crate::commands::{ChangeDirectory, JoshutoCommand, JoshutoRunnable};
use crate::config::mimetype::JoshutoMimetypeEntry;
use crate::context::JoshutoContext;
use crate::error::{JoshutoError, JoshutoErrorKind, JoshutoResult};
+use crate::fs::{JoshutoDirEntry, JoshutoMetadata};
use crate::ui::widgets::{TuiMenu, TuiTextField};
use crate::ui::TuiBackend;
use crate::util::load_child::LoadChild;
@@ -21,45 +22,70 @@ impl OpenFile {
"open_file"
}
- pub fn get_options<'a>(path: &Path) -> Vec<&'a JoshutoMimetypeEntry> {
+ pub fn get_options<'a>(entry: &JoshutoDirEntry) -> Vec<&'a JoshutoMimetypeEntry> {
let mut mimetype_options: Vec<&JoshutoMimetypeEntry> = Vec::new();
/* extensions have priority */
- if let Some(file_ext) = path.extension() {
+ if let Some(file_ext) = entry.file_path().extension() {
if let Some(file_ext) = file_ext.to_str() {
let ext_entries = MIMETYPE_T.get_entries_for_ext(file_ext);
mimetype_options.extend(ext_entries);
}
}
+ #[cfg(feature = "file_mimetype")]
+ {
+ if let Some(mimetype) = entry.metadata.mimetype.as_ref() {
+ let mime_entries = MIMETYPE_T.get_entries_for_mimetype(mimetype.as_str());
+ mimetype_options.extend(mime_entries);
+ }
+ }
mimetype_options
}
fn open(context: &mut JoshutoContext, backend: &mut TuiBackend) -> std::io::Result<()> {
let mut dirpath = None;
- let mut filepaths = None;
-
- if let Some(curr_list) = context.tabs[context.curr_tab_index].curr_list_ref() {
- if let Some(index) = curr_list.index {
- let child_path = curr_list.contents[index].file_path();
- if child_path.is_dir() {
- dirpath = Some(child_path.clone());
- } else {
- filepaths = Some(curr_list.get_selected_paths());
- }
+ let mut selected_entries = None;
+
+ {
+ let curr_tab = context.curr_tab_ref();
+ match curr_tab.curr_list_ref() {
+ None => return Ok(()),
+ Some(curr_list) => match curr_list.get_curr_ref() {
+ Some(entry) if entry.file_path().is_dir() => {
+ let path = entry.file_path().clone();
+ dirpath = Some(path);
+ }
+ Some(entry) => {
+ let vec: Vec<&JoshutoDirEntry> = curr_list.selected_entries().collect();
+ if vec.is_empty() {
+ selected_entries = Some(vec![entry]);
+ } else {
+ selected_entries = Some(vec);
+ }
+ }
+ None => return Ok(()),
+ },
}
}
+
if let Some(path) = dirpath {
ChangeDirectory::cd(path.as_path(), context)?;
LoadChild::load_child(context)?;
- } else if let Some(paths) = filepaths {
- let options = Self::get_options(paths[0]);
+ } else if let Some(entries) = selected_entries {
+ let options = Self::get_options(entries[0]);
+ let entry_paths: Vec<&Path> = entries.iter().map(|e| e.file_path().as_path()).collect();
if !options.is_empty() {
- backend.terminal_drop();
- let res = options[0].execute_with(&paths);
- backend.terminal_restore()?;
+ let res = if options[0].get_fork() {
+ backend.terminal_drop();
+ let res = options[0].execute_with(entry_paths.as_slice());
+ backend.terminal_restore()?;
+ res
+ } else {
+ options[0].execute_with(entry_paths.as_slice())
+ };
return res;
} else {
- OpenFileWith::open_with(context, backend, &paths)?;
+ OpenFileWith::open_with(context, backend, &entries)?;
}
}
Ok(())
@@ -95,11 +121,11 @@ impl OpenFileWith {
pub fn open_with(
context: &JoshutoContext,
backend: &mut TuiBackend,
- paths: &[&PathBuf],
+ entries: &[&JoshutoDirEntry],
) -> std::io::Result<()> {
const PROMPT: &str = "open_with ";
- let mimetype_options: Vec<&JoshutoMimetypeEntry> = OpenFile::get_options(&paths[0]);
+ let mimetype_options: Vec<&JoshutoMimetypeEntry> = OpenFile::get_options(&entries[0]);
let user_input: Option<String> = {
let menu_options: Vec<String> = mimetype_options
@@ -116,6 +142,7 @@ impl OpenFileWith {
.menu(&mut menu_widget);
textfield.get_input(backend, &context)
};
+ let entry_paths: Vec<&Path> = entries.iter().map(|e| e.file_path().as_path()).collect();
match user_input.as_ref() {
Some(user_input) if user_input.starts_with(PROMPT) => {
@@ -129,10 +156,10 @@ impl OpenFileWith {
Ok(n) => {
let mimetype_entry = &mimetype_options[n];
if mimetype_entry.get_fork() {
- mimetype_entry.execute_with(paths)
+ mimetype_entry.execute_with(entry_paths.as_slice())
} else {
backend.terminal_drop();
- let res = mimetype_entry.execute_with(paths);
+ let res = mimetype_entry.execute_with(entry_paths.as_slice());
backend.terminal_restore()?;
res
}
@@ -144,7 +171,7 @@ impl OpenFileWith {
backend.terminal_drop();
let res = JoshutoMimetypeEntry::new(String::from(cmd))
.args(args_iter)
- .execute_with(paths);
+ .execute_with(entry_paths.as_slice());
backend.terminal_restore()?;
res
}
@@ -168,18 +195,31 @@ impl std::fmt::Display for OpenFileWith {
impl JoshutoRunnable for OpenFileWith {
fn execute(&self, context: &mut JoshutoContext, backend: &mut TuiBackend) -> JoshutoResult<()> {
- let paths = match &context.tabs[context.curr_tab_index].curr_list_ref() {
- Some(curr_list) => curr_list.get_selected_paths(),
- None => vec![],
+ let selected_entries = {
+ let curr_tab = context.curr_tab_ref();
+ match curr_tab.curr_list_ref() {
+ None => vec![],
+ Some(curr_list) => match curr_list.get_curr_ref() {
+ Some(entry) => {
+ let vec: Vec<&JoshutoDirEntry> = curr_list.selected_entries().collect();
+ if vec.is_empty() {
+ vec![entry]
+ } else {
+ vec
+ }
+ }
+ None => vec![],
+ },
+ }
};
- if paths.is_empty() {
+ if selected_entries.is_empty() {
return Err(JoshutoError::new(
JoshutoErrorKind::IONotFound,
String::from("No files selected"),
));
}
- Self::open_with(context, backend, &paths)?;
+ Self::open_with(context, backend, &selected_entries)?;
Ok(())
}
}
diff --git a/src/config/mimetype.rs b/src/config/mimetype.rs
index f8c4f69..e6103dd 100644
--- a/src/config/mimetype.rs
+++ b/src/config/mimetype.rs
@@ -2,7 +2,7 @@ use serde_derive::Deserialize;
use std::collections::HashMap;
use std::fmt;
use std::io::Read;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use std::process;
use super::{parse_config_file, ConfigStructure};
@@ -82,7 +82,7 @@ impl JoshutoMimetypeEntry {
self._confirm_exit
}
- pub fn execute_with(&self, paths: &[&PathBuf]) -> std::io::Result<()> {
+ pub fn execute_with(&self, paths: &[&Path]) -> std::io::Result<()> {
let program = String::from(self.get_command());
let mut command = process::Command::new(program);
diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs
index e1e55e8..9f3ddd9 100644
--- a/src/fs/metadata.rs
+++ b/src/fs/metadata.rs
@@ -1,4 +1,4 @@
-use std::{fs, path, time};
+use std::{fs, path, process, time};
#[derive(Clone, Debug)]
pub struct JoshutoMetadata {
@@ -6,6 +6,7 @@ pub struct JoshutoMetadata {
pub modified: time::SystemTime,
pub permissions: fs::Permissions,
pub file_type: fs::FileType,
+ pub mimetype: Option<String>,
#[cfg(unix)]
pub uid: u32,
#[cfg(unix)]
@@ -25,6 +26,14 @@ impl JoshutoMetadata {
let modified = metadata.modified()?;
let permissions = metadata.permissions();
let file_type = metadata.file_type();
+ let mut mimetype = None;
+
+ if file_type.is_file() {
+ #[cfg(feature = "file_mimetype")]
+ {
+ mimetype = file_mimetype(path)
+ }
+ }
#[cfg(unix)]
let uid = metadata.uid();
@@ -38,6 +47,7 @@ impl JoshutoMetadata {
modified,
permissions,
file_type,
+ mimetype,
#[cfg(unix)]
uid,
#[cfg(unix)]
@@ -47,3 +57,24 @@ impl JoshutoMetadata {
})
}
}
+
+fn file_mimetype(path: &path::Path) -> Option<String> {
+ let output = process::Command::new("file")
+ .args(&["-Lb", "--mime-type"])
+ .arg(path)
+ .output();
+
+ match output {
+ Ok(s) => {
+ if s.status.success() {
+ match String::from_utf8(s.stdout) {
+ Ok(s) => Some(s),
+ Err(_) => None,
+ }
+ } else {
+ None
+ }
+ }
+ Err(_) => None,
+ }
+}
diff --git a/src/ui/widgets/tui_footer.rs b/src/ui/widgets/tui_footer.rs
index 3e044f4..072b143 100644
--- a/src/ui/widgets/tui_footer.rs
+++ b/src/ui/widgets/tui_footer.rs
@@ -33,12 +33,22 @@ impl<'a> Widget for TuiFooter<'a> {
let size = self.entry.metadata.len;
let size = format::file_size_to_string(size as f64);
+ #[cfg(unix)]
+ let mimetype = match self.entry.metadata.mimetype.as_ref() {
+ Some(s) => s,
+ None => "",
+ };
+
let mut text = vec![
Text::styled(mode, mode_style),
Text::raw(" "),
Text::raw(mtime),
Text::raw(" "),
Text::raw(size),
+ #[cfg(unix)]
+ Text::raw(" "),
+ #[cfg(unix)]
+ Text::raw(mimetype),
];
if self.entry.metadata.file_type.is_symlink() {