use std::path::{Path, PathBuf};
use anyhow::{Context as _, Result};
use gpui_kit::{App, PathPromptOptions};
pub fn prompt_for_open(cx: &App) -> impl Future<Output = Result<Option<Vec<PathBuf>>>> + use<> {
let paths = cx.prompt_for_paths(PathPromptOptions {
files: true,
directories: false,
multiple: true,
prompt: Some("Open".into()),
});
async move {
match paths.await {
Ok(paths) => paths.context("the file picker could not be opened"),
Err(_) => Ok(None),
}
}
}
pub fn prompt_for_import(cx: &App) -> impl Future<Output = Result<Option<PathBuf>>> + use<> {
let paths = cx.prompt_for_paths(PathPromptOptions {
files: true,
directories: false,
multiple: false,
prompt: Some("Import".into()),
});
async move {
match paths.await {
Ok(paths) => {
let paths = paths.context("the file picker could not be opened")?;
Ok(paths.and_then(|paths| paths.into_iter().next()))
}
Err(_) => Ok(None),
}
}
}
pub fn prompt_for_save(
path: Option<&Path>,
title: &str,
extension: &str,
cx: &App,
) -> impl Future<Output = Result<Option<PathBuf>>> + use<> {
let directory = path
.and_then(Path::parent)
.map(Path::to_path_buf)
.unwrap_or_else(default_directory);
let name = path
.and_then(Path::file_name)
.map(|name| name.to_string_lossy().into_owned())
.unwrap_or_else(|| suggested_name(title, extension));
let extension = extension.to_string();
let chosen = cx.prompt_for_new_path(&directory, Some(&name));
async move {
match chosen.await {
Ok(chosen) => Ok(chosen
.context("the file picker could not be opened")?
.map(|path| with_extension(path, &extension))),
Err(_) => Ok(None),
}
}
}
pub async fn read(path: PathBuf) -> Result<String> {
std::fs::read_to_string(&path).with_context(|| format!("could not open {}", path.display()))
}
pub async fn write(path: PathBuf, contents: String) -> Result<()> {
std::fs::write(&path, contents).with_context(|| format!("could not save {}", path.display()))
}
pub fn label(path: &Path) -> String {
path.file_name()
.map(|name| name.to_string_lossy().into_owned())
.unwrap_or_else(|| path.display().to_string())
}
fn default_directory() -> PathBuf {
dirs::document_dir()
.or_else(dirs::home_dir)
.unwrap_or_else(|| PathBuf::from("."))
}
fn suggested_name(title: &str, extension: &str) -> String {
let stem: String = title
.chars()
.map(|character| {
if std::path::is_separator(character) {
'-'
} else {
character
}
})
.collect();
let stem = match stem.trim() {
"" => "query",
stem => stem,
};
format!("{stem}.{extension}")
}
fn with_extension(path: PathBuf, extension: &str) -> PathBuf {
match path.extension() {
Some(_) => path,
None => path.with_extension(extension),
}
}