Tidy up and reorganize things, they're just about in shape

This commit is contained in:
Lucien Greathouse 2018-05-09 00:01:44 -07:00
parent d9aec292fa
commit 5a82879115
9 changed files with 174 additions and 191 deletions

View file

@ -1,7 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::path::PathBuf; use std::path::PathBuf;
use std::process; use std::process;
use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use std::time::Instant; use std::time::Instant;
@ -9,8 +8,7 @@ use rand;
use project::Project; use project::Project;
use web; use web;
use session_config::SessionConfig; use session::{Session, SessionConfig};
use fs_session::FsSession;
use partition::Partition; use partition::Partition;
pub fn serve(project_path: &PathBuf, port: Option<u64>) { pub fn serve(project_path: &PathBuf, port: Option<u64>) {
@ -47,6 +45,7 @@ pub fn serve(project_path: &PathBuf, port: Option<u64>) {
partitions.insert(partition_name.clone(), Partition { partitions.insert(partition_name.clone(), Partition {
path, path,
target, target,
name: partition_name.clone(),
}); });
} }
@ -56,20 +55,8 @@ pub fn serve(project_path: &PathBuf, port: Option<u64>) {
println!("Using session config {:#?}", config); println!("Using session config {:#?}", config);
let session = { let mut session = Session::new(config.clone());
let mut session = FsSession::new(config.clone()); session.start();
session.init();
Arc::new(Mutex::new(session))
};
// TODO: Let FsSession handle the main loop and spawn a bunch of threads
// ...since Rust stable doesn't have a channel select implementation
thread::spawn(move || {
loop {
session.lock().unwrap().step();
}
});
loop { loop {
thread::park(); thread::park();

View file

@ -2,8 +2,6 @@ use std::path::{Path, PathBuf};
use partition::Partition; use partition::Partition;
// TODO: Add lifetime, switch to using Cow<'a, str> instead of String? It's
// possible that it would be too cumbersome!
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, PartialEq, Hash)]
pub struct FileRoute { pub struct FileRoute {
pub partition: String, pub partition: String,
@ -43,7 +41,7 @@ impl FileRoute {
// This FileRoute refers to the partition itself // This FileRoute refers to the partition itself
if partition.target.len() == 0 { if partition.target.len() == 0 {
// We're targeting the game itself! // We're targeting the game!
"game".to_string() "game".to_string()
} else { } else {
partition.target.last().unwrap().clone() partition.target.last().unwrap().clone()

View file

@ -12,13 +12,14 @@ extern crate tempfile;
pub mod commands; pub mod commands;
pub mod file_route; pub mod file_route;
pub mod fs_session;
pub mod id; pub mod id;
pub mod partition; pub mod partition;
pub mod partition_watcher;
pub mod pathext; pub mod pathext;
pub mod project; pub mod project;
pub mod rbx; pub mod rbx;
// pub mod session; pub mod rbx_session;
pub mod session_config; pub mod session;
pub mod vfs_session;
pub mod web; pub mod web;
pub mod web_util; pub mod web_util;

View file

@ -2,6 +2,9 @@ use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Partition { pub struct Partition {
/// The unique name of this partition, used for debugging.
pub name: String,
/// The path on the filesystem that this partition maps to. /// The path on the filesystem that this partition maps to.
pub path: PathBuf, pub path: PathBuf,

View file

@ -0,0 +1,44 @@
use std::sync::mpsc::{channel, Receiver, Sender};
use std::time::Duration;
use std::thread;
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher, watcher};
use partition::Partition;
pub struct PartitionWatcher {
pub watcher: RecommendedWatcher,
pub partition: Partition,
}
impl PartitionWatcher {
pub fn start_new(partition: Partition, tx: Sender<(String, DebouncedEvent)>) -> PartitionWatcher {
let (watch_tx, watch_rx) = channel();
let mut watcher = watcher(watch_tx, Duration::from_millis(100)).unwrap();
watcher.watch(&partition.path, RecursiveMode::Recursive).unwrap();
let partition_name = partition.name.clone();
thread::spawn(move || {
loop {
match watch_rx.recv() {
// TODO: Transform DebouncedEvent to some sort of FileChange object
Ok(event) => match tx.send((partition_name.clone(), event)) {
Ok(_) => {},
Err(_) => break,
},
Err(_) => break,
};
}
});
PartitionWatcher {
partition,
watcher,
}
}
pub fn stop(self) {
}
}

43
server/src/rbx_session.rs Normal file
View file

@ -0,0 +1,43 @@
use std::collections::HashMap;
use id::Id;
use partition::Partition;
use rbx::RbxInstance;
use session::SessionConfig;
pub struct RbxSession {
pub config: SessionConfig,
/// The RbxInstance that represents each partition.
pub partition_instances: HashMap<String, Id>,
/// The store of all instances in the session.
pub instances: HashMap<Id, RbxInstance>,
// pub instances_by_route: HashMap<FileRoute, Id>,
}
impl RbxSession {
pub fn new(config: SessionConfig) -> RbxSession {
RbxSession {
config,
partition_instances: HashMap::new(),
instances: HashMap::new(),
}
}
// fn load_instances(&mut self) {
// for (partition_name, file_item) in &self.partition_files {
// let partition = self.partitions.get(partition_name).unwrap();
// let (root_id, mut instances) = file_to_instance(&file_item, partition);
// // there has to be an std method for this
// // oh well
// for (instance_id, instance) in instances.drain() {
// self.instances.insert(instance_id, instance);
// }
// self.partition_instances.insert(partition_name.clone(), root_id);
// }
// }
}

View file

@ -1,49 +1,45 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::io::Read; use std::sync::{mpsc, Arc, RwLock};
use std::fs::{self, File}; use std::thread;
use id::{Id, get_id}; use id::{Id, get_id};
use file_route::FileRoute; use file_route::FileRoute;
use partition::Partition; use partition::Partition;
use rbx::RbxInstance; use rbx::RbxInstance;
use fs_session::FsSession; use rbx_session::RbxSession;
use session_config::SessionConfig; use vfs_session::{VfsSession, FileItem};
use partition_watcher::PartitionWatcher;
pub struct Session<'a> { #[derive(Debug, Clone, PartialEq)]
pub config: &'a SessionConfig, pub struct SessionConfig {
pub partitions: HashMap<String, Partition>,
/// The RbxInstance that represents each partition.
pub partition_instances: HashMap<String, Id>,
/// The store of all instances in the session.
pub instances: HashMap<Id, RbxInstance>,
// pub instances_by_route: HashMap<FileRoute, Id>,
} }
fn file_to_instance(file_item: &FileItem, partition: &Partition) -> (Id, HashMap<Id, RbxInstance>) { /// Stub trait for middleware
trait Middleware {
}
fn file_to_instances(file_item: &FileItem, partition: &Partition, output: &mut HashMap<Id, RbxInstance>) -> Id {
match file_item { match file_item {
&FileItem::File { ref contents, ref route } => { &FileItem::File { ref contents, ref route } => {
let mut properties = HashMap::new(); let mut properties = HashMap::new();
properties.insert("Value".to_string(), contents.clone()); properties.insert("Value".to_string(), contents.clone());
let primary_id = get_id(); let primary_id = get_id();
let mut instances = HashMap::new();
instances.insert(primary_id, RbxInstance { output.insert(primary_id, RbxInstance {
name: route.name(partition).to_string(), name: route.name(partition).to_string(),
class_name: "StringValue".to_string(), class_name: "StringValue".to_string(),
parent: None, parent: None,
properties, properties,
}); });
(primary_id, instances) primary_id
}, },
&FileItem::Directory { ref children, ref route } => { &FileItem::Directory { ref children, ref route } => {
let primary_id = get_id(); let primary_id = get_id();
let mut instances = HashMap::new();
instances.insert(primary_id, RbxInstance { output.insert(primary_id, RbxInstance {
name: route.name(partition).to_string(), name: route.name(partition).to_string(),
class_name: "Folder".to_string(), class_name: "Folder".to_string(),
parent: None, parent: None,
@ -51,115 +47,66 @@ fn file_to_instance(file_item: &FileItem, partition: &Partition) -> (Id, HashMap
}); });
for child_file_item in children.values() { for child_file_item in children.values() {
let (child_id, mut child_instances) = file_to_instance(child_file_item, partition); let mut child_instances = HashMap::new();
let child_id = file_to_instances(child_file_item, partition, &mut child_instances);
child_instances.get_mut(&child_id).unwrap().parent = Some(primary_id); child_instances.get_mut(&child_id).unwrap().parent = Some(primary_id);
for (instance_id, instance) in child_instances.drain() { for (instance_id, instance) in child_instances.drain() {
instances.insert(instance_id, instance); output.insert(instance_id, instance);
} }
} }
(primary_id, instances) primary_id
} }
} }
} }
pub struct Session {
config: SessionConfig,
vfs_session: Arc<RwLock<VfsSession>>,
rbx_session: Arc<RwLock<RbxSession>>,
middlewares: Vec<Box<Middleware>>,
watchers: Vec<PartitionWatcher>,
}
impl Session { impl Session {
pub fn new() -> Session { pub fn new(config: SessionConfig) -> Session {
Session { Session {
partitions: HashMap::new(), vfs_session: Arc::new(RwLock::new(VfsSession::new(config.clone()))),
partition_instances: HashMap::new(), rbx_session: Arc::new(RwLock::new(RbxSession::new(config.clone()))),
instances: HashMap::new(), middlewares: Vec::new(),
watchers: Vec::new(),
config,
} }
} }
pub fn init(&mut self) { pub fn start(&mut self) {
self.load_files(); let (tx, rx) = mpsc::channel();
self.load_instances();
}
fn load_instances(&mut self) { for partition in self.config.partitions.values() {
for (partition_name, file_item) in &self.partition_files { let watcher = PartitionWatcher::start_new(partition.clone(), tx.clone());
let partition = self.partitions.get(partition_name).unwrap();
let (root_id, mut instances) = file_to_instance(&file_item, partition);
// there has to be an std method for this self.watchers.push(watcher);
// oh well }
for (instance_id, instance) in instances.drain() {
self.instances.insert(instance_id, instance); thread::spawn(move || {
loop {
match rx.recv() {
Ok((partition_name, change)) => {
println!("Got change {:?} on partition {}", change, partition_name);
},
Err(_) => break,
}
} }
});
}
self.partition_instances.insert(partition_name.clone(), root_id); pub fn get_vfs_session(&self) -> Arc<RwLock<VfsSession>> {
} self.vfs_session.clone()
}
pub fn get_rbx_session(&self) -> Arc<RwLock<RbxSession>> {
self.rbx_session.clone()
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use tempfile;
// I'm not exactly sure how I want to structure these tests
// Essentially, I need a bunch of random files to load, and to measure:
// * What FileItems were loaded?
// * Are changes logged to those FileItems correctly?
// * What RbxInstance objects are generated from them?
// * Are changes propagated from FileItem through to those RbxInstances?
// #[test]
// fn file_items_correct() {
// use std::io::Write;
// let root_dir = tempfile::tempdir().unwrap();
// let foo_path = root_dir.path().join("foo.txt");
// let bar_path = root_dir.path().join("bar.tsv");
// {
// let mut foo = File::create(foo_path).unwrap();
// writeln!(foo, "Hello, foo!").unwrap();
// let mut bar = File::create(bar_path).unwrap();
// writeln!(bar, "Hello, bar!").unwrap();
// }
// let mut session = Session::new();
// let partition = Partition {
// path: root_dir.path().to_path_buf(),
// target: vec!["ReplicatedStorage".to_string()],
// };
// session.partitions.insert("agh".to_string(), partition);
// session.load_files();
// assert_eq!(session.partition_files.len(), 1);
// {
// let folder = session.partition_files.values().nth(0).unwrap();
// let children = match folder {
// &FileItem::Directory { ref children, .. } => children,
// _ => panic!("Not a directory!"),
// };
// assert_eq!(children.len(), 2);
// assert!(children.get("foo.txt").is_some());
// assert!(children.get("bar.tsv").is_some());
// }
// session.load_instances();
// assert_eq!(session.instances.len(), 3);
// assert_eq!(session.partition_instances.len(), 1);
// {
// let folder_id = session.partition_instances.values().nth(0).unwrap();
// let folder = session.instances.get(folder_id).unwrap();
// assert_eq!(folder.name, "ReplicatedStorage");
// }
// }
}

View file

@ -1,8 +0,0 @@
use std::collections::HashMap;
use partition::Partition;
#[derive(Debug, Clone, PartialEq)]
pub struct SessionConfig {
pub partitions: HashMap<String, Partition>,
}

View file

@ -1,18 +1,9 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::io::Read; use std::io::Read;
use std::fs::{self, File}; use std::fs::{self, File};
use std::sync::mpsc::{channel, Receiver};
use std::time::Duration;
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher, watcher};
use file_route::FileRoute; use file_route::FileRoute;
use session_config::SessionConfig; use session::SessionConfig;
struct FileWatcher {
watcher: RecommendedWatcher,
rx: Receiver<DebouncedEvent>,
}
/// Represents a file or directory that has been read from the filesystem. /// Represents a file or directory that has been read from the filesystem.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -27,60 +18,33 @@ pub enum FileItem {
}, },
} }
pub struct FsSession { #[derive(Debug, Clone, Copy)]
pub enum FileChange {
Created,
Deleted,
Modified,
}
pub struct VfsSession {
pub config: SessionConfig, pub config: SessionConfig,
/// The in-memory files associated with each partition. /// The in-memory files associated with each partition.
pub partition_files: HashMap<String, FileItem>, pub partition_files: HashMap<String, FileItem>,
watchers: HashMap<String, FileWatcher>,
} }
impl FsSession { impl VfsSession {
pub fn new(config: SessionConfig) -> FsSession { pub fn new(config: SessionConfig) -> VfsSession {
FsSession { VfsSession {
config: config, config: config,
partition_files: HashMap::new(), partition_files: HashMap::new(),
watchers: HashMap::new(),
} }
} }
pub fn init(&mut self) { pub fn read_partitions(&mut self) {
self.load_partitions();
self.watch_partitions();
}
pub fn step(&mut self) {
for (partition_name, watcher) in self.watchers.iter() {
let change_event = match watcher.rx.try_recv() {
Ok(v) => v,
Err(_) => continue,
};
println!("Change event on partition {}: {:?}", partition_name, change_event);
}
}
fn watch_partitions(&mut self) {
for (partition_name, partition) in self.config.partitions.iter() {
let (tx, rx) = channel();
let mut watcher = watcher(tx, Duration::from_millis(300)).unwrap();
watcher.watch(&partition.path, RecursiveMode::Recursive).unwrap();
self.watchers.insert(partition_name.clone(), FileWatcher {
watcher,
rx,
});
}
}
fn load_partitions(&mut self) {
for partition_name in self.config.partitions.keys() { for partition_name in self.config.partitions.keys() {
let route = FileRoute { let route = FileRoute {
partition: partition_name.clone(), partition: partition_name.clone(),
route: vec![], route: Vec::new(),
}; };
let file_item = self.read(&route).expect("Couldn't load partitions"); let file_item = self.read(&route).expect("Couldn't load partitions");
@ -89,6 +53,10 @@ impl FsSession {
} }
} }
pub fn handle_change(&mut self, route: &FileRoute, change: FileChange) {
// TODO: Update in-memory files
}
fn read(&self, route: &FileRoute) -> Result<FileItem, ()> { fn read(&self, route: &FileRoute) -> Result<FileItem, ()> {
let partition_path = &self.config.partitions.get(&route.partition) let partition_path = &self.config.partitions.get(&route.partition)
.ok_or(())?.path; .ok_or(())?.path;