Reorganize state module
This commit is contained in:
parent
8c5be0c371
commit
6f0ab738f0
@ -1,19 +1,27 @@
|
||||
mod edit;
|
||||
mod show;
|
||||
mod state;
|
||||
mod tui;
|
||||
mod watcher;
|
||||
|
||||
|
||||
use std::fs::create_dir_all;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser as ArgParser;
|
||||
use directories::ProjectDirs;
|
||||
use tracing::Level as TracingLevel;
|
||||
use tracing_appender::rolling;
|
||||
use tracing_subscriber::filter::Targets;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
|
||||
use crate::state::logs_dir;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref DIRS: ProjectDirs = {
|
||||
ProjectDirs::from("", "valeth", "wayfarer").unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, ArgParser)]
|
||||
@ -80,3 +88,17 @@ fn tracing_setup() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn logs_dir() -> Result<PathBuf> {
|
||||
let log_root_path = DIRS
|
||||
.state_dir()
|
||||
.unwrap_or_else(|| DIRS.cache_dir())
|
||||
.join("logs");
|
||||
|
||||
if !log_root_path.exists() {
|
||||
create_dir_all(&log_root_path)?;
|
||||
}
|
||||
|
||||
Ok(log_root_path)
|
||||
}
|
||||
|
@ -1,104 +0,0 @@
|
||||
use std::fs::create_dir_all;
|
||||
use std::path::PathBuf;
|
||||
#[cfg(feature = "tui")]
|
||||
use std::{
|
||||
fs::{self, read_to_string},
|
||||
io::Write,
|
||||
os::unix::prelude::OsStrExt,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use directories::ProjectDirs;
|
||||
#[cfg(feature = "tui")]
|
||||
use jrny_save::Savefile;
|
||||
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref DIRS: ProjectDirs = {
|
||||
ProjectDirs::from("", "valeth", "wayfarer").unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
pub fn logs_dir() -> Result<PathBuf> {
|
||||
let log_root_path = DIRS
|
||||
.state_dir()
|
||||
.unwrap_or_else(|| DIRS.cache_dir())
|
||||
.join("logs");
|
||||
|
||||
if !log_root_path.exists() {
|
||||
create_dir_all(&log_root_path)?;
|
||||
}
|
||||
|
||||
Ok(log_root_path)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "tui")]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PersistentState {
|
||||
pub savefile: Option<Savefile>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "tui")]
|
||||
impl PersistentState {
|
||||
pub fn load() -> Result<Self> {
|
||||
let data_dir = DIRS.data_local_dir();
|
||||
|
||||
if !data_dir.exists() {
|
||||
create_dir_all(&data_dir)?;
|
||||
}
|
||||
|
||||
let savefile = load_last_active_savefile()?;
|
||||
|
||||
Ok(Self { savefile })
|
||||
}
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
pub fn reload_active_savefile(&mut self) -> Result<()> {
|
||||
if let Some(cur_savefile) = &self.savefile {
|
||||
let new_savefile = Savefile::from_path(&cur_savefile.path)?;
|
||||
self.savefile = Some(new_savefile);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_active_savefile_path<P>(&mut self, path: P) -> Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let savefile = Savefile::from_path(&path)?;
|
||||
|
||||
let state_path = DIRS.data_local_dir().join("active_savefile");
|
||||
let mut state_file = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.create(true)
|
||||
.open(state_path)?;
|
||||
|
||||
let active_savefile = savefile.path.as_os_str().as_bytes();
|
||||
|
||||
state_file.write_all(active_savefile)?;
|
||||
self.savefile = Some(savefile);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "tui")]
|
||||
fn load_last_active_savefile() -> Result<Option<Savefile>> {
|
||||
let state_path = DIRS.data_local_dir().join("active_savefile");
|
||||
|
||||
if !state_path.exists() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let path = read_to_string(&state_path).unwrap();
|
||||
|
||||
let savefile = Savefile::from_path(path.trim_end())?;
|
||||
|
||||
Ok(Some(savefile))
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#![cfg(feature = "tui")]
|
||||
|
||||
mod events;
|
||||
mod state;
|
||||
mod view;
|
||||
|
||||
|
||||
@ -16,11 +17,8 @@ use crossterm::terminal::{
|
||||
};
|
||||
use ratatui::backend::CrosstermBackend;
|
||||
use tracing::{debug, error, info};
|
||||
use tui_input::Input;
|
||||
|
||||
use crate::state::PersistentState;
|
||||
#[cfg(feature = "watch")]
|
||||
use crate::watcher::FileWatcher;
|
||||
use self::state::{Mode, State};
|
||||
use crate::Args as AppArgs;
|
||||
|
||||
|
||||
@ -31,15 +29,6 @@ type Terminal = ratatui::Terminal<CrosstermBackend<Stdout>>;
|
||||
pub struct Args;
|
||||
|
||||
|
||||
pub struct State {
|
||||
persistent: PersistentState,
|
||||
mode: Mode,
|
||||
file_select: Input,
|
||||
#[cfg(feature = "watch")]
|
||||
file_watcher: Option<FileWatcher>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum Message {
|
||||
@ -57,30 +46,11 @@ pub enum Message {
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub enum Mode {
|
||||
#[default]
|
||||
Normal,
|
||||
|
||||
ShowError(String),
|
||||
|
||||
SelectFile,
|
||||
}
|
||||
|
||||
|
||||
pub(crate) fn execute(_app_args: &AppArgs, _args: &Args) -> Result<()> {
|
||||
let persistent = PersistentState::load()?;
|
||||
let state = State::load()?;
|
||||
|
||||
let mut terminal = setup()?;
|
||||
|
||||
let state = State {
|
||||
persistent,
|
||||
mode: Mode::default(),
|
||||
file_select: Input::default(),
|
||||
#[cfg(feature = "watch")]
|
||||
file_watcher: None,
|
||||
};
|
||||
|
||||
run(&mut terminal, state)?;
|
||||
|
||||
reset(terminal)?;
|
||||
@ -137,45 +107,39 @@ fn handle_message(
|
||||
|
||||
Message::LoadFile => {
|
||||
let file_path = state.file_select.value();
|
||||
|
||||
info!("Loading file {}", file_path);
|
||||
|
||||
state.persistent.set_active_savefile_path(file_path)?;
|
||||
state.set_selected_as_active_savefile()?;
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
if state.file_watcher.is_some() {
|
||||
state.file_watcher = None;
|
||||
if state.is_watching_file() {
|
||||
state.reset_file_watcher();
|
||||
}
|
||||
|
||||
msg_tx.send(Message::SetMode(Mode::Normal))?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
Message::ToggleFileWatch if state.persistent.savefile.is_some() => {
|
||||
let savefile = state.persistent.savefile.as_ref().unwrap();
|
||||
Message::ToggleFileWatch => {
|
||||
if let Some(savefile) = state.savefile() {
|
||||
if state.is_watching_file() {
|
||||
let evq_tx = msg_tx.clone();
|
||||
let callback = move || {
|
||||
evq_tx.send(Message::ReloadFile).unwrap();
|
||||
};
|
||||
|
||||
if state.file_watcher.is_none() {
|
||||
let evq_tx = msg_tx.clone();
|
||||
let callback = move || {
|
||||
evq_tx.send(Message::ReloadFile).unwrap();
|
||||
};
|
||||
|
||||
info!("Starting file watcher on {}", savefile.path.display());
|
||||
|
||||
let file_watcher = FileWatcher::new(&savefile.path, callback);
|
||||
state.file_watcher = Some(file_watcher);
|
||||
} else {
|
||||
info!("Stopped file watcher on {}", savefile.path.display());
|
||||
|
||||
state.file_watcher = None;
|
||||
info!("Starting file watcher on {}", savefile.path.display());
|
||||
state.enable_file_watcher(callback);
|
||||
} else {
|
||||
info!("Stopped file watcher on {}", savefile.path.display());
|
||||
state.reset_file_watcher();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
Message::ReloadFile if state.persistent.savefile.is_some() => {
|
||||
debug!("Reloading file");
|
||||
|
||||
state.persistent.reload_active_savefile()?;
|
||||
Message::ReloadFile => {
|
||||
state.reload_active_savefile()?;
|
||||
}
|
||||
|
||||
_ => (),
|
||||
|
122
crates/wayfarer/src/tui/state.rs
Normal file
122
crates/wayfarer/src/tui/state.rs
Normal file
@ -0,0 +1,122 @@
|
||||
use std::fs::{self, create_dir_all, read_to_string};
|
||||
use std::io::Write;
|
||||
use std::os::unix::prelude::OsStrExt;
|
||||
|
||||
use anyhow::Result;
|
||||
use jrny_save::Savefile;
|
||||
use tui_input::Input;
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
use crate::watcher::FileWatcher;
|
||||
use crate::DIRS;
|
||||
|
||||
|
||||
pub struct State {
|
||||
savefile: Option<Savefile>,
|
||||
pub mode: Mode,
|
||||
pub file_select: Input,
|
||||
#[cfg(feature = "watch")]
|
||||
file_watcher: Option<FileWatcher>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub enum Mode {
|
||||
#[default]
|
||||
Normal,
|
||||
|
||||
ShowError(String),
|
||||
|
||||
SelectFile,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn load() -> Result<Self> {
|
||||
let data_dir = DIRS.data_local_dir();
|
||||
|
||||
if !data_dir.exists() {
|
||||
create_dir_all(&data_dir)?;
|
||||
}
|
||||
|
||||
let savefile = load_last_active_savefile()?;
|
||||
|
||||
Ok(Self {
|
||||
savefile,
|
||||
mode: Mode::default(),
|
||||
file_select: Input::default(),
|
||||
#[cfg(feature = "watch")]
|
||||
file_watcher: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn savefile(&self) -> Option<&Savefile> {
|
||||
self.savefile.as_ref()
|
||||
}
|
||||
|
||||
pub fn set_selected_as_active_savefile(&mut self) -> Result<()> {
|
||||
let savefile = Savefile::from_path(&self.file_select.value())?;
|
||||
|
||||
let state_path = DIRS.data_local_dir().join("active_savefile");
|
||||
let mut state_file = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.create(true)
|
||||
.open(state_path)?;
|
||||
|
||||
let active_savefile = savefile.path.as_os_str().as_bytes();
|
||||
|
||||
state_file.write_all(active_savefile)?;
|
||||
self.savefile = Some(savefile);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
pub fn is_watching_file(&self) -> bool {
|
||||
self.file_watcher.is_some()
|
||||
}
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
pub fn enable_file_watcher<F>(&mut self, callback: F)
|
||||
where
|
||||
F: Fn() + Send + 'static,
|
||||
{
|
||||
if let Some(savefile) = &self.savefile {
|
||||
let file_watcher = FileWatcher::new(&savefile.path, callback);
|
||||
self.file_watcher = Some(file_watcher);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
pub fn reset_file_watcher(&mut self) {
|
||||
self.file_watcher = None;
|
||||
}
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
pub fn reload_active_savefile(&mut self) -> Result<()> {
|
||||
use tracing::debug;
|
||||
|
||||
if let Some(cur_savefile) = &self.savefile {
|
||||
debug!("Reloading file");
|
||||
let new_savefile = Savefile::from_path(&cur_savefile.path)?;
|
||||
self.savefile = Some(new_savefile);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn load_last_active_savefile() -> Result<Option<Savefile>> {
|
||||
let state_path = DIRS.data_local_dir().join("active_savefile");
|
||||
|
||||
if !state_path.exists() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let path = read_to_string(&state_path)?;
|
||||
|
||||
let savefile = Savefile::from_path(path.trim_end())?;
|
||||
|
||||
Ok(Some(savefile))
|
||||
}
|
@ -7,7 +7,7 @@ use crate::tui::State;
|
||||
|
||||
|
||||
pub fn render(state: &mut State, frame: &mut Frame, area: Rect) {
|
||||
match &state.persistent.savefile {
|
||||
match state.savefile() {
|
||||
Some(savefile) => render_info(savefile, frame, area),
|
||||
None => render_no_active_file(frame, area),
|
||||
}
|
||||
|
@ -18,20 +18,20 @@ pub fn render(state: &State, mut frame: &mut Frame, area: Rect) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
Mode::Normal if state.file_watcher.is_some() && state.persistent.savefile.is_some() => {
|
||||
let savefile = state.persistent.savefile.as_ref().unwrap();
|
||||
let text = format!("Watching file: {}", savefile.path.display());
|
||||
let status = Paragraph::new(text).block(status_block);
|
||||
frame.render_widget(status, area);
|
||||
Mode::Normal if state.is_watching_file() => {
|
||||
if let Some(savefile) = state.savefile() {
|
||||
let text = format!("Watching file: {}", savefile.path.display());
|
||||
let status = Paragraph::new(text).block(status_block);
|
||||
frame.render_widget(status, area);
|
||||
}
|
||||
}
|
||||
|
||||
Mode::Normal if state.persistent.savefile.is_none() => (),
|
||||
|
||||
Mode::Normal => {
|
||||
let savefile = state.persistent.savefile.as_ref().unwrap();
|
||||
let text = format!("Showing file: {}", savefile.path.display());
|
||||
let status = Paragraph::new(text).block(status_block);
|
||||
frame.render_widget(status, area);
|
||||
if let Some(savefile) = state.savefile() {
|
||||
let text = format!("Showing file: {}", savefile.path.display());
|
||||
let status = Paragraph::new(text).block(status_block);
|
||||
frame.render_widget(status, area);
|
||||
}
|
||||
}
|
||||
|
||||
Mode::SelectFile => render_file_select(&state, &mut frame, status_block, area),
|
||||
|
Loading…
Reference in New Issue
Block a user