Reorganize state module

This commit is contained in:
Patrick Auernig 2023-08-14 18:32:19 +02:00
parent 8c5be0c371
commit 6f0ab738f0
6 changed files with 179 additions and 175 deletions

View File

@ -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)
}

View File

@ -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))
}

View File

@ -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()?;
}
_ => (),

View 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))
}

View File

@ -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),
}

View File

@ -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),