From 136684ed742def18600773a6015c9d61d03374d6 Mon Sep 17 00:00:00 2001 From: Jarrod Doyle Date: Mon, 5 Feb 2024 16:33:09 +0000 Subject: [PATCH] Add basic project state and book listing --- Cargo.lock | 1 + Cargo.toml | 1 + src/application.rs | 31 ++++++++++++++++++++++++------- src/file.rs | 9 +++++++++ src/main.rs | 1 + src/project.rs | 36 ++++++++++++++++++++++++++++++++++++ 6 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 src/project.rs diff --git a/Cargo.lock b/Cargo.lock index a1be6ac..6031049 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2368,6 +2368,7 @@ dependencies = [ "iced", "rfd", "tokio", + "walkdir", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0db9448..b4bcaec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" iced = { git = "https://github.com/iced-rs/iced.git", version = "0.12.0", features = ["tokio"] } rfd = "0.13.0" tokio = { version = "1.35.1", features = ["fs"] } +walkdir = "2.4.0" diff --git a/src/application.rs b/src/application.rs index d701a86..4e51c7f 100644 --- a/src/application.rs +++ b/src/application.rs @@ -2,12 +2,13 @@ use std::{path::PathBuf, sync::Arc}; use iced::{ executor, - widget::{button, column, row, text, text_editor}, - Application, Command, Element, Theme, + widget::{button, column, row, scrollable, text, text_editor, vertical_rule, Column}, + Application, Command, Element, Length, Theme, }; use crate::{ - file::{default_file, load_file, pick_file}, + file::{default_file, load_file, load_folder}, + project::ProjectState, Error, }; @@ -16,9 +17,11 @@ pub enum Message { Open, Edit(text_editor::Action), FileOpened(Result<(PathBuf, Arc), Error>), + FolderSelected(Result), } pub struct BookManagerApp { + project: Option, book_path: Option, book_content: text_editor::Content, io_error: Option, @@ -33,6 +36,7 @@ impl Application for BookManagerApp { fn new(_flags: Self::Flags) -> (Self, Command) { ( Self { + project: None, book_path: None, book_content: text_editor::Content::new(), io_error: None, @@ -55,8 +59,12 @@ impl Application for BookManagerApp { } Err(error) => self.io_error = Some(error), }, + Message::FolderSelected(result) => match result { + Ok(path) => self.project = Some(ProjectState::new(path)), + Err(error) => self.io_error = Some(error), + }, Message::Open => { - return Command::perform(pick_file(), Message::FileOpened); + return Command::perform(load_folder(), Message::FolderSelected); } } @@ -68,10 +76,19 @@ impl Application for BookManagerApp { let controls = row![button("Open").on_press(Message::Open)]; let active_editor = text_editor(&self.book_content).on_action(Message::Edit); - column![hello_world, controls, active_editor] + let mut books: Vec> = vec![]; + if let Some(project) = &self.project { + for book in project.books.iter() { + books.push(text(book.file_name.clone()).into()); + } + } + + let left_panel = scrollable(Column::with_children(books)).width(Length::Fixed(256.0)); + let right_panel = column![hello_world, controls, active_editor] .spacing(10) - .padding(10) - .into() + .padding(10); + + row![left_panel, vertical_rule(2), right_panel].into() } fn theme(&self) -> Theme { diff --git a/src/file.rs b/src/file.rs index 01796b4..7954a72 100644 --- a/src/file.rs +++ b/src/file.rs @@ -23,3 +23,12 @@ pub async fn pick_file() -> Result<(PathBuf, Arc), Error> { pub fn default_file() -> PathBuf { PathBuf::from(format!("{}/src/main.rs", env!("CARGO_MANIFEST_DIR"))) } + +pub async fn load_folder() -> Result { + let file_handle = rfd::AsyncFileDialog::new() + .set_title("Choose project folder...") + .pick_folder() + .await + .ok_or(Error::DialogClosed)?; + Ok(file_handle.path().to_owned()) +} diff --git a/src/main.rs b/src/main.rs index effbee3..e642268 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod application; mod file; +mod project; use std::io; diff --git a/src/project.rs b/src/project.rs new file mode 100644 index 0000000..79befec --- /dev/null +++ b/src/project.rs @@ -0,0 +1,36 @@ +use std::{ffi::OsStr, path::PathBuf}; +use walkdir::WalkDir; + +#[derive(Debug, Clone)] +pub struct Book { + pub path: PathBuf, + pub file_name: String, +} + +#[derive(Debug, Clone)] +pub struct ProjectState { + pub path: PathBuf, + pub books: Vec, +} + +impl ProjectState { + pub fn new(path: PathBuf) -> Self { + let mut books = vec![]; + let book_walker = WalkDir::new(path.join("books")).into_iter(); + for entry in book_walker.flatten() { + if entry.metadata().is_ok_and(|md| md.is_dir()) { + continue; + } + + let extension = entry.path().extension().and_then(OsStr::to_str); + if extension.is_some_and(|ext| ext.to_lowercase() == "str") { + books.push(Book { + path: entry.path().to_owned(), + file_name: entry.file_name().to_str().unwrap().to_owned(), + }); + } + } + + Self { path, books } + } +}