-
Notifications
You must be signed in to change notification settings - Fork 125
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Abhishek Kumar <[email protected]>
- Loading branch information
1 parent
f112a49
commit 99b8140
Showing
9 changed files
with
336 additions
and
9 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
use serde::{Deserialize, Serialize}; | ||
use std::path::PathBuf; | ||
|
||
#[derive(Serialize, Deserialize, Clone)] | ||
struct JsonFile { | ||
watch: PathBuf, | ||
recursive: Option<bool>, | ||
patterns: Option<Vec<String>>, | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct Config { | ||
path: PathBuf, | ||
recursive: bool, | ||
patterns: Vec<String>, | ||
} | ||
|
||
impl Config { | ||
// Load configuration from file | ||
pub fn load_from_file(file_path: &PathBuf) -> Result<Self, Box<dyn std::error::Error>> { | ||
let file_content = std::fs::read_to_string(file_path)?; | ||
let config: JsonFile = serde_json::from_str(&file_content)?; | ||
Ok(Config { | ||
path: config.watch, | ||
recursive: config.recursive.unwrap_or(false), | ||
patterns: config.patterns.unwrap_or_default(), | ||
}) | ||
} | ||
|
||
// Get the path from configuration | ||
pub fn path(&self) -> &PathBuf { | ||
&self.path | ||
} | ||
|
||
// Check if the configuration is recursive | ||
pub fn is_recursive(&self) -> bool { | ||
self.recursive | ||
} | ||
|
||
// Get the file patterns from configuration | ||
pub fn patterns(&self) -> &Vec<String> { | ||
&self.patterns | ||
} | ||
} | ||
|
||
// Get the configuration file path | ||
pub fn get_config_file() -> Option<PathBuf> { | ||
let current_dir = std::env::current_dir().ok()?; | ||
let config_path = current_dir.join("observer.json"); | ||
|
||
if config_path.exists() { | ||
Some(config_path) | ||
} else { | ||
None | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
use crate::config_manager::Config; | ||
use std::collections::HashMap; | ||
use std::fs::Metadata; | ||
use std::path::PathBuf; | ||
use std::sync::Arc; | ||
use std::time::Duration; | ||
use std::time::SystemTime; | ||
use walkdir::DirEntry; | ||
use walkdir::WalkDir; | ||
|
||
// Define a trait for file handlers | ||
pub trait FileHandler: Send + Sync { | ||
fn handle(&self, file: &File); | ||
} | ||
|
||
// File structure to hold file metadata and data | ||
#[derive(Debug, Clone, PartialEq)] | ||
pub struct File { | ||
name: String, | ||
path: PathBuf, | ||
data: FileData, | ||
} | ||
|
||
// Data structure for file metadata | ||
#[derive(Debug, Clone, PartialEq)] | ||
struct FileData { | ||
last_accesed: SystemTime, | ||
last_modified: SystemTime, | ||
} | ||
|
||
impl File { | ||
// Create a new File instance from a DirEntry | ||
pub fn new(file: &DirEntry) -> Self { | ||
let metadata = file.metadata().unwrap(); | ||
File { | ||
name: file.file_name().to_str().unwrap().to_string(), | ||
path: file.path().to_path_buf(), | ||
data: FileData::new(metadata), | ||
} | ||
} | ||
|
||
// Get the file name | ||
pub fn name(&self) -> String { | ||
Arc::new(&self.name).to_string() | ||
} | ||
|
||
// Get the file extension | ||
pub fn extension(&self) -> Option<String> { | ||
self.path | ||
.extension() | ||
.map(|ext| ext.to_string_lossy().to_string()) | ||
} | ||
|
||
// Get the display path of the file | ||
pub fn ds_path(&self) -> String { | ||
Arc::new(&self.path) | ||
.to_path_buf() | ||
.to_str() | ||
.unwrap() | ||
.to_string() | ||
} | ||
|
||
// Check if the file was deleted | ||
pub fn was_deleted(&self) -> bool { | ||
!self.path.exists() | ||
} | ||
|
||
// Get the last modification time of the file | ||
pub fn last_modification(&self) -> SystemTime { | ||
self.data.last_modified | ||
} | ||
|
||
// Set the last modification time of the file | ||
pub fn set_modification(&mut self, time: SystemTime) { | ||
self.data.last_modified = time; | ||
} | ||
|
||
// Detect file type based on extension | ||
pub fn detect_file_type(&self) -> Option<String> { | ||
self.extension().map(|ext| { | ||
match ext.as_str() { | ||
"k" => "K File", | ||
"mod" => "Mod File", | ||
"JSON" | "json" => "JSON File", | ||
"YAML" | "yaml" => "YAML File", | ||
_ => "Unknown File Type", | ||
} | ||
.to_string() | ||
}) | ||
} | ||
} | ||
|
||
impl FileData { | ||
// Create a new FileData instance from Metadata | ||
pub fn new(metadata: Metadata) -> Self { | ||
FileData { | ||
last_accesed: metadata.accessed().unwrap(), | ||
last_modified: metadata.modified().unwrap(), | ||
} | ||
} | ||
} | ||
|
||
// Define file events | ||
#[derive(Debug)] | ||
pub enum FileEvent { | ||
Modified(File), | ||
} | ||
|
||
// Observer structure to watch files | ||
#[derive(Debug)] | ||
pub struct Observer { | ||
config: Config, | ||
files: HashMap<String, File>, | ||
} | ||
|
||
impl Observer { | ||
// Initialize a new Observer instance with a configuration | ||
pub fn new(config: Config) -> Self { | ||
Observer { | ||
files: get_files(&config), | ||
config, | ||
} | ||
} | ||
|
||
// Iterator for file events | ||
pub fn iter_events(&mut self) -> impl Iterator<Item = FileEvent> + '_ { | ||
let interval = Duration::from_millis(500); | ||
let last_files = self.files.clone(); | ||
std::iter::from_fn(move || { | ||
let current_files = get_files(&self.config); | ||
|
||
let mut events = Vec::new(); | ||
for (name, file) in current_files.iter() { | ||
if let Some(last_file) = last_files.get(name) { | ||
if file.last_modification() > last_file.last_modification() { | ||
events.push(FileEvent::Modified(file.clone())); | ||
} | ||
} | ||
} | ||
std::thread::sleep(interval); | ||
if !events.is_empty() { | ||
self.files = current_files; | ||
Some(events.remove(0)) | ||
} else { | ||
None | ||
} | ||
}) | ||
} | ||
} | ||
|
||
// Get files based on configuration | ||
fn get_files(config: &Config) -> HashMap<String, File> { | ||
let files = match config.is_recursive() { | ||
true => WalkDir::new(config.path()).min_depth(1), | ||
false => WalkDir::new(config.path()).min_depth(1).max_depth(1), | ||
} | ||
.into_iter() | ||
.filter(|x| x.as_ref().unwrap().metadata().unwrap().is_file()) | ||
.map(|x| File::new(&x.unwrap())) | ||
.map(|f| (f.name(), f)) | ||
.collect::<HashMap<_, _>>(); | ||
|
||
if config.patterns().is_empty() { | ||
return files; | ||
} | ||
let mut filtered_files = HashMap::new(); | ||
for (name, file) in files { | ||
let ext = file.extension().unwrap_or_default(); | ||
if config.patterns().contains(&ext) { | ||
filtered_files.insert(name, file); | ||
} | ||
} | ||
filtered_files | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
use crate::config_manager::Config; | ||
use crate::file::{FileEvent, FileHandler, Observer}; | ||
use std::collections::HashMap; | ||
|
||
// HandlerRegistry to register and manage file handlers | ||
pub struct HandlerRegistry { | ||
handlers: HashMap<String, Box<dyn FileHandler>>, | ||
} | ||
|
||
impl HandlerRegistry { | ||
// Create a new HandlerRegistry instance | ||
pub fn new() -> Self { | ||
HandlerRegistry { | ||
handlers: HashMap::new(), | ||
} | ||
} | ||
|
||
// Register a handler for a file type | ||
pub fn register_handler(&mut self, file_type: &str, handler: Box<dyn FileHandler>) { | ||
self.handlers.insert(file_type.to_string(), handler); | ||
} | ||
|
||
// Get a handler for a file type | ||
pub fn get_handler(&self, file_type: &str) -> Option<&Box<dyn FileHandler>> { | ||
self.handlers.get(file_type) | ||
} | ||
|
||
// Handle file event | ||
pub fn handle_event(&self, event: &FileEvent) { | ||
match event { | ||
FileEvent::Modified(file) => { | ||
if let Some(handler) = | ||
self.get_handler(&file.detect_file_type().unwrap_or_default()) | ||
{ | ||
handler.handle(file); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
// KCL Watch System structure to manage the observer and handler registry | ||
pub struct KCLWatchSystem { | ||
observer: Observer, | ||
handler_registry: HandlerRegistry, | ||
} | ||
|
||
impl KCLWatchSystem { | ||
// Create a new KCL Watch System instance with a configuration | ||
pub fn new(config: Config) -> Self { | ||
let observer = Observer::new(config.clone()); | ||
let handler_registry = HandlerRegistry::new(); | ||
KCLWatchSystem { | ||
observer, | ||
handler_registry, | ||
} | ||
} | ||
|
||
// Start the observer | ||
pub fn start_observer(mut self) { | ||
let mut event_iter = self.observer.iter_events(); | ||
let handler_registry = &self.handler_registry; | ||
|
||
std::thread::spawn(move || loop { | ||
if let Some(event) = event_iter.next() { | ||
handler_registry.handle_event(&event); | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.