Skip to content

Commit

Permalink
refac: file keeper
Browse files Browse the repository at this point in the history
  • Loading branch information
Teajey committed Nov 29, 2023
1 parent 88de3c4 commit fcfbecc
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 156 deletions.
30 changes: 30 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ repository = "https://github.com/Teajey/custard"
[dependencies]
anyhow = "1.0.75"
axum = "0.6.20"
camino = "1.1.6"
chrono = { version = "0.4.31", features = ["serde"] }
notify = "5.2.0"
serde = { version = "1.0.188", features = ["serde_derive"] }
serde_json = "1.0.107"
serde_yaml = "0.9.25"
thiserror = "1.0.49"
tokio = { version = "1.32.0", features = ["full"] }

[dev-dependencies]
pretty_assertions = "1.4.0"
15 changes: 10 additions & 5 deletions src/frontmatter_file.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
pub mod map;
pub mod keeper;

use anyhow::Result;
use camino::{Utf8Path as Path, Utf8PathBuf};
use chrono::{DateTime, Utc};
use serde::Serialize;

use crate::utf8_filepath::UTF8FilePath;
pub use map::Map;
pub use keeper::Keeper;

#[derive(Debug, Clone, Serialize)]
pub struct FrontmatterFile {
Expand Down Expand Up @@ -69,6 +69,8 @@ pub enum ReadFromPathError {
Yaml(String, serde_yaml::Error),
#[error("Failed to load: {0}")]
Io(#[from] std::io::Error),
#[error("Tried to read from path with no file name: {0}")]
NoFileNamePath(Utf8PathBuf),
}

impl FrontmatterFile {
Expand All @@ -92,8 +94,11 @@ impl FrontmatterFile {
&self.modified
}

pub fn read_from_path(path: &UTF8FilePath) -> Result<Self, ReadFromPathError> {
let name = path.name().to_owned();
pub fn read_from_path(path: &Path) -> Result<Self, ReadFromPathError> {
let name = path
.file_name()
.ok_or_else(|| ReadFromPathError::NoFileNamePath(path.to_path_buf()))?
.to_owned();
let metadata = std::fs::metadata(path)?;
let modified = metadata.modified()?.into();
let created = metadata.created()?.into();
Expand Down
67 changes: 49 additions & 18 deletions src/frontmatter_file/map.rs → src/frontmatter_file/keeper.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,57 @@
use std::{
collections::HashMap,
collections::{hash_map::Values, HashMap},
sync::{Arc, Mutex},
};

use crate::{fs::path_has_extensions, utf8_filepath::UTF8FilePath};
use camino::{Utf8Path, Utf8PathBuf};

use crate::fs::{self, path_has_extensions};

use super::FrontmatterFile;

pub struct Map {
pub inner: HashMap<UTF8FilePath, FrontmatterFile>,
pub struct Keeper {
inner: HashMap<Utf8PathBuf, FrontmatterFile>,
}

#[derive(Debug, thiserror::Error)]
pub enum NewKeeperError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Failed to load frontmatter file: {0}")]
ReadFrontmatterFromPath(#[from] super::ReadFromPathError),
}

impl Keeper {
pub fn new(path: &Utf8Path) -> Result<Self, NewKeeperError> {
let markdown_fps = fs::filepaths_with_extensions(path, &["md"])?
.into_iter()
.map(|path| -> Result<_, super::ReadFromPathError> {
let md = FrontmatterFile::read_from_path(&path)?;

Ok((path, md))
})
.collect::<Result<HashMap<_, _>, _>>()?;
Ok(Keeper {
inner: markdown_fps,
})
}

pub fn files(&self) -> Values<'_, Utf8PathBuf, FrontmatterFile> {
self.inner.values()
}
}

#[derive(Clone)]
pub struct ArcMutex(pub Arc<Mutex<Map>>);
pub struct ArcMutex(pub Arc<Mutex<Keeper>>);

impl ArcMutex {
pub fn new(map: HashMap<UTF8FilePath, FrontmatterFile>) -> Self {
Self(Arc::new(Mutex::new(Map { inner: map })))
pub fn new(keeper: Keeper) -> Self {
Self(Arc::new(Mutex::new(keeper)))
}
}

impl Map {
fn process_rename_event(&mut self, path: &UTF8FilePath) {
impl Keeper {
fn process_rename_event(&mut self, path: &Utf8Path) {
let was_removed = self.inner.remove(path).is_some();
if !was_removed {
let file = match FrontmatterFile::read_from_path(path) {
Expand All @@ -31,11 +61,11 @@ impl Map {
return;
}
};
self.inner.insert(path.clone(), file);
self.inner.insert(path.to_owned(), file);
}
}

fn process_edit_event(&mut self, path: &UTF8FilePath) {
fn process_edit_event(&mut self, path: &Utf8Path) {
let Some(file) = self.inner.get_mut(path) else {
eprintln!("Couldn't find ({path:?}) in Edit event.");
return;
Expand All @@ -50,14 +80,14 @@ impl Map {
*file = new_file;
}

fn process_removal_event(&mut self, path: &UTF8FilePath) {
fn process_removal_event(&mut self, path: &Utf8Path) {
let was_removed = self.inner.remove(path).is_some();
if !was_removed {
eprintln!("Couldn't find ({path:?}) in Remove event..");
}
}

fn process_create_event(&mut self, path: &UTF8FilePath) {
fn process_create_event(&mut self, path: &Utf8Path) {
if self.inner.contains_key(path) {
eprintln!(
"A Create event occurred for a path ({path:?}) but it already exists in memory."
Expand All @@ -71,7 +101,7 @@ impl Map {
return;
}
};
self.inner.insert(path.clone(), new_file);
self.inner.insert(path.to_owned(), new_file);
}
}

Expand All @@ -84,16 +114,16 @@ impl notify::EventHandler for ArcMutex {
attrs: _,
}) => {
let path = paths.first().expect("event must have at least one path");
if !path_has_extensions(path, &["md"]) {
return;
}
let path = match UTF8FilePath::try_from(path.clone()) {
let path = match Utf8PathBuf::try_from(path.clone()) {
Ok(path) => path,
Err(err) => {
eprintln!("Event filepath ({path:?}) was not UTF-8: {err}\n\nNon-UTF-8 paths not supported.");
return;
}
};
if !path_has_extensions(&path, &["md"]) {
return;
}
let mut map = match self.0.as_ref().lock() {
Ok(map) => map,
Err(err) => {
Expand Down Expand Up @@ -125,3 +155,4 @@ impl notify::EventHandler for ArcMutex {
}
}
}

7 changes: 3 additions & 4 deletions src/fs.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
use std::path::{Path, PathBuf};
use camino::{Utf8Path as Path, Utf8PathBuf as PathBuf};

pub fn path_has_extensions(path: &Path, extensions: &[&str]) -> bool {
path.extension()
.and_then(std::ffi::OsStr::to_str)
.is_some_and(|ext| extensions.contains(&ext))
}

pub fn filepaths_with_extensions(
dir: &Path,
extensions: &[&str],
) -> Result<Vec<PathBuf>, std::io::Error> {
std::fs::read_dir(dir)?
dir.read_dir_utf8()?
.filter_map(|entry| {
entry
.map(|entry| {
let path = entry.path();
let path = entry.path().to_path_buf();
if !path.is_file() {
return None;
}
Expand Down
Loading

0 comments on commit fcfbecc

Please sign in to comment.