Handle newly added files

This commit is contained in:
Adrian Wozniak 2020-04-23 22:39:55 +02:00
parent 45d356aa92
commit 80441feb78

View File

@ -1,7 +1,7 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fs::*; use std::fs::*;
use std::path::Path; use std::path::Path;
use std::sync::mpsc::channel; use std::sync::mpsc::{channel, Sender};
use std::sync::{Arc, RwLock, RwLockWriteGuard}; use std::sync::{Arc, RwLock, RwLockWriteGuard};
use std::time::Duration; use std::time::Duration;
use std::time::SystemTime; use std::time::SystemTime;
@ -18,7 +18,7 @@ enum Partial {
File(Css), File(Css),
} }
#[derive(Debug)] #[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
enum FileState { enum FileState {
Clean, Clean,
Dirty, Dirty,
@ -42,10 +42,36 @@ impl CssFile {
state: FileState::Clean, state: FileState::Clean,
} }
} }
pub fn drop_dead(&mut self) {
let mut old = vec![];
std::mem::swap(&mut self.lines, &mut old);
for child in old {
match child {
Partial::String(_) => {
self.lines.push(child);
}
Partial::File(file) => {
let state = file.read().map(|f| f.state).unwrap();
if state != FileState::Dead {
if let Ok(mut css) = file.write() {
css.drop_dead();
}
self.lines.push(Partial::File(file));
}
}
}
}
}
} }
impl std::fmt::Display for CssFile { impl std::fmt::Display for CssFile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.state == FileState::Dead {
return Ok(());
}
f.write_str(format!("\n/* -- {} --- */\n\n", self.path).as_str())?; f.write_str(format!("\n/* -- {} --- */\n\n", self.path).as_str())?;
for line in self.lines.iter() { for line in self.lines.iter() {
match line { match line {
@ -74,6 +100,7 @@ struct Application {
files_map: HashMap<String, HashSet<String>>, files_map: HashMap<String, HashSet<String>>,
fm: HashMap<String, Css>, fm: HashMap<String, Css>,
root_file: Option<Css>, root_file: Option<Css>,
sender: Option<Sender<DebouncedEvent>>,
} }
impl Application { impl Application {
@ -119,11 +146,19 @@ impl Application {
.clone() .clone()
.parent() .parent()
.ok_or_else(|| format!("Not a valid path {:?}", input))?; .ok_or_else(|| format!("Not a valid path {:?}", input))?;
let file = self
.fm let file = if self.fm.contains_key(&file_path) {
.entry(file_path.clone()) self.fm.get(&file_path).unwrap().clone()
.or_insert_with(|| Arc::new(RwLock::new(CssFile::new(file_path.clone())))) } else {
.clone(); let css = Arc::new(RwLock::new(CssFile::new(file_path.clone())));
self.fm.insert(file_path.clone(), css.clone());
if let Some(ref tx) = self.sender {
let path = Path::new(&file_path);
tx.send(DebouncedEvent::Create(path.to_path_buf()))
.map_err(|e| format!("{}", e))?;
}
css
};
if let Ok(mut css) = file.write() { if let Ok(mut css) = file.write() {
css.last_changed = Self::read_timestamp(input)?; css.last_changed = Self::read_timestamp(input)?;
@ -183,6 +218,28 @@ impl Application {
.and_then(|css| css.write().or(Err(false))) .and_then(|css| css.write().or(Err(false)))
} }
fn refresh(&mut self) {
if let Ok(mut root) = self
.root_file
.as_mut()
.ok_or_else(|| false)
.and_then(|f| f.write().map_err(|_| false))
{
root.drop_dead();
}
let mut old = HashMap::new();
std::mem::swap(&mut old, &mut self.fm);
for (key, file) in old.into_iter() {
if file
.read()
.map(|f| f.state != FileState::Dead)
.unwrap_or_default()
{
self.fm.insert(key, file);
}
}
}
fn print(&self) { fn print(&self) {
let css = match self.root_file.as_ref().unwrap().read() { let css = match self.root_file.as_ref().unwrap().read() {
Ok(css) => css, Ok(css) => css,
@ -197,6 +254,10 @@ impl Application {
_ => println!("{}", css), _ => println!("{}", css),
} }
} }
pub fn pipe(&mut self, tx: Sender<DebouncedEvent>) {
self.sender = Some(tx);
}
} }
fn main() -> Result<(), String> { fn main() -> Result<(), String> {
@ -224,6 +285,7 @@ fn main() -> Result<(), String> {
files_map: Default::default(), files_map: Default::default(),
fm: Default::default(), fm: Default::default(),
root_file: None, root_file: None,
sender: None,
}; };
let root_path = app.input.to_string(); let root_path = app.input.to_string();
let root = std::path::Path::new(&root_path); let root = std::path::Path::new(&root_path);
@ -240,18 +302,13 @@ fn main() -> Result<(), String> {
return Ok(()); return Ok(());
} }
let (tx, rx) = channel();
app.pipe(tx.clone());
let mut watcher = watcher(tx.clone(), Duration::from_secs(1)).unwrap();
app.parse()?; app.parse()?;
app.print(); app.print();
let (tx, rx) = channel();
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
for file in app.fm.keys() {
watcher
.watch(file.to_string(), RecursiveMode::NonRecursive)
.unwrap();
}
loop { loop {
match rx.recv() { match rx.recv() {
Ok(DebouncedEvent::NoticeWrite(path)) => { Ok(DebouncedEvent::NoticeWrite(path)) => {
@ -264,9 +321,16 @@ fn main() -> Result<(), String> {
Ok(DebouncedEvent::NoticeRemove(path)) => { Ok(DebouncedEvent::NoticeRemove(path)) => {
app.mark_dead(path.as_path()); app.mark_dead(path.as_path());
watcher.unwatch(path).unwrap(); watcher.unwatch(path).unwrap();
app.refresh();
app.print();
} }
Ok(event) => println!("{:?}", event), Ok(DebouncedEvent::Create(path)) => {
Err(e) => println!("watch error: {:?}", e), if let Err(e) = watcher.watch(path, RecursiveMode::NonRecursive) {
eprintln!("{}", e);
}
}
Ok(_event) => (),
Err(e) => eprintln!("watch error: {:?}", e),
} }
} }
} }