mod filters;
use filters::{is_valid_extension, mime_types_to_extensions};
use crate::api::{PickOptions, SaveOptions};
use crate::error::{AccessError, PickerError};
use crate::token::{FileAccessToken, PermissionStatus, ReadSeek, TokenInner, WriteSeek};
pub(crate) async fn pick_open_single(
options: PickOptions,
) -> Result<Option<FileAccessToken>, PickerError> {
let mut dialog = rfd::AsyncFileDialog::new();
if !options.mime_types.is_empty() {
let extensions = mime_types_to_extensions(&options.mime_types);
let ext_refs: Vec<&str> = extensions
.iter()
.map(String::as_str)
.filter(|e| is_valid_extension(e))
.collect();
if !ext_refs.is_empty() {
let label = options.filter_label.as_deref().unwrap_or("Files");
dialog = dialog.add_filter(label, &ext_refs);
}
}
match dialog.pick_file().await {
None => Ok(None),
Some(h) => {
let path = h.path().to_path_buf();
let display_name = file_name_from_path(&path);
Ok(Some(FileAccessToken {
inner: TokenInner::Desktop { path, display_name },
}))
}
}
}
pub(crate) async fn pick_open_multi(
options: PickOptions,
) -> Result<Vec<FileAccessToken>, PickerError> {
let mut dialog = rfd::AsyncFileDialog::new();
if !options.mime_types.is_empty() {
let extensions = mime_types_to_extensions(&options.mime_types);
let ext_refs: Vec<&str> = extensions
.iter()
.map(String::as_str)
.filter(|e| is_valid_extension(e))
.collect();
if !ext_refs.is_empty() {
let label = options.filter_label.as_deref().unwrap_or("Files");
dialog = dialog.add_filter(label, &ext_refs);
}
}
match dialog.pick_files().await {
None => Ok(vec![]),
Some(list) => {
let tokens = list
.into_iter()
.map(|h| {
let path = h.path().to_path_buf();
let display_name = file_name_from_path(&path);
FileAccessToken {
inner: TokenInner::Desktop { path, display_name },
}
})
.collect();
Ok(tokens)
}
}
}
pub(crate) async fn pick_save(
options: SaveOptions,
) -> Result<Option<FileAccessToken>, PickerError> {
let mut dialog = rfd::AsyncFileDialog::new();
if let Some(ref name) = options.suggested_name {
dialog = dialog.set_file_name(name);
}
if let Some(ref mime) = options.mime_type {
let extensions = mime_types_to_extensions(std::slice::from_ref(mime));
let ext_refs: Vec<&str> = extensions
.iter()
.map(String::as_str)
.filter(|e| is_valid_extension(e))
.collect();
if !ext_refs.is_empty() {
dialog = dialog.add_filter("File", &ext_refs);
}
}
match dialog.save_file().await {
None => Ok(None),
Some(h) => {
let path = h.path().to_path_buf();
let display_name = file_name_from_path(&path);
Ok(Some(FileAccessToken {
inner: TokenInner::Desktop { path, display_name },
}))
}
}
}
pub(crate) fn open_read(inner: &TokenInner) -> Result<Box<dyn ReadSeek>, AccessError> {
match inner {
TokenInner::Desktop { path, .. } => {
let file = std::fs::File::open(path)?;
Ok(Box::new(file))
}
_ => Err(AccessError::Platform {
message: "non-desktop token on desktop platform".into(),
}),
}
}
pub(crate) fn open_write(inner: &TokenInner) -> Result<Box<dyn WriteSeek>, AccessError> {
match inner {
TokenInner::Desktop { path, .. } => {
let file = std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(false)
.open(path)?;
Ok(Box::new(file))
}
_ => Err(AccessError::Platform {
message: "non-desktop token on desktop platform".into(),
}),
}
}
pub(crate) fn check_permission(inner: &TokenInner) -> PermissionStatus {
match inner {
TokenInner::Desktop { path, .. } => {
if path.exists() {
PermissionStatus::Valid
} else {
PermissionStatus::Revoked
}
}
_ => PermissionStatus::Unknown,
}
}
fn file_name_from_path(path: &std::path::Path) -> String {
path.file_name()
.and_then(|n| n.to_str())
.unwrap_or("unnamed")
.to_owned()
}