prehnite_core/util/
file_dialog.rs

1#![doc = "Prehniteブックを開く"]
2use crate::db::open_book_or_alert;
3use crate::i18n::i18n;
4use crate::opt_unwrap_or_return;
5use crate::settings::registry::SettingRegistry;
6use crate::settings::value::SettingValueType;
7use crate::settings::GlobalSettingKey;
8use crate::util::alert::alert_i18n_spawn;
9use iced::window::raw_window_handle::HasWindowHandle;
10use iced::{window, Task};
11use native_dialog::{FileDialogBuilder, MessageLevel};
12use std::path::PathBuf;
13use tracing::{debug, error, trace};
14use tracing_unwrap::ResultExt;
15
16fn prehnite_file_dialog_builder(
17    title_i18n_id: &str,
18    owner: &Option<&dyn HasWindowHandle>,
19) -> FileDialogBuilder {
20    let v = FileDialogBuilder::default()
21        .set_title(i18n(title_i18n_id))
22        .add_filter("prehnite book", ["prehnite"]);
23    match owner {
24        None => v,
25        Some(w) => v.set_owner(w),
26    }
27}
28
29fn unwrap_dialog_result(value: native_dialog::Result<Option<PathBuf>>) -> Option<PathBuf> {
30    match value.ok_or_log() {
31        Some(v) => {
32            if v.is_none() {
33                trace!("File select canceled.");
34            }
35            v
36        }
37        None => None,
38    }
39}
40
41fn dialog_new_prehnite_book(window_id: window::Id) -> Task<Option<PathBuf>> {
42    window::run(window_id, |w| {
43        prehnite_file_dialog_builder("new-file", &Some(w)).save_single_file()
44    })
45    .then(|v| Task::future(async { unwrap_dialog_result(v.spawn().await) }))
46}
47
48fn dialog_open_prehnite_book(window_id: window::Id) -> Task<Option<PathBuf>> {
49    window::run(window_id, |w| {
50        prehnite_file_dialog_builder("open-file", &Some(w)).open_single_file()
51    })
52    .then(|v| Task::future(async { unwrap_dialog_result(v.spawn().await) }))
53}
54
55#[derive(Clone, Debug)]
56/// ファイルダイアログの種類
57pub enum FileOpe {
58    New,
59    Open,
60}
61
62#[derive(Clone, Debug, PartialEq, Eq)]
63/// ダイアログの結果
64pub enum OpenPrehniteBookStatus {
65    Success,
66    Failed,
67    NotSelected,
68}
69
70impl OpenPrehniteBookStatus {
71    /// ファイルパスの取得に成功したかどうか
72    pub fn is_success(&self) -> bool {
73        OpenPrehniteBookStatus::Success == *self
74    }
75}
76
77async fn prehnite_book_file_process(book_path: PathBuf, ope: FileOpe) -> OpenPrehniteBookStatus {
78    match ope {
79        FileOpe::New => match tokio::fs::remove_file(book_path.clone()).await {
80            Ok(_) => {}
81            Err(e) => {
82                if tokio::fs::try_exists(book_path.clone())
83                    .await
84                    .unwrap_or(true)
85                {
86                    error!("Failed to remove file ({book_path:#?}): {e}");
87                    alert_i18n_spawn(("error", "permission-denied"), MessageLevel::Error).await;
88                    return OpenPrehniteBookStatus::Failed;
89                }
90            }
91        },
92        FileOpe::Open => {
93            if !tokio::fs::try_exists(book_path.clone())
94                .await
95                .unwrap_or(false)
96            {
97                error!("File does not exist ({book_path:#?})");
98                alert_i18n_spawn(("error", "file-notfound"), MessageLevel::Error).await;
99                SettingRegistry::immediate_apply(
100                    GlobalSettingKey::LastOpened.into(),
101                    SettingValueType::String(None),
102                )
103                .await
104                .ok_or_log();
105                return OpenPrehniteBookStatus::Failed;
106            }
107        }
108    }
109    if open_book_or_alert(book_path).await {
110        OpenPrehniteBookStatus::Success
111    } else {
112        OpenPrehniteBookStatus::Failed
113    }
114}
115
116#[tracing::instrument]
117/// [`Task`]として非同期にファイルダイアログを開きPrehniteブックを開きます。
118pub fn select_and_open_prehnite_book_file(
119    window_id: window::Id,
120    ope: FileOpe,
121) -> Task<OpenPrehniteBookStatus> {
122    let file_dialog_result = match &ope {
123        FileOpe::New => dialog_new_prehnite_book(window_id),
124        FileOpe::Open => dialog_open_prehnite_book(window_id),
125    };
126    file_dialog_result.then(move |book_path| {
127        let book_path =
128            opt_unwrap_or_return!(book_path, Task::done(OpenPrehniteBookStatus::NotSelected));
129        debug!("Opening the book: {:#?}", book_path);
130        Task::future(prehnite_book_file_process(book_path, ope.clone()))
131    })
132}