diff --git a/server/src/commands/init.rs b/server/src/commands/init.rs index a27ef083..96cbbcb3 100644 --- a/server/src/commands/init.rs +++ b/server/src/commands/init.rs @@ -1,10 +1,10 @@ use std::path::PathBuf; use std::process; -use project::SourceProject; +use project::Project; pub fn init(project_path: &PathBuf) { - match SourceProject::init(project_path) { + match Project::init(project_path) { Ok(_) => { println!("Created new empty project at {}", project_path.display()); }, diff --git a/server/src/commands/serve.rs b/server/src/commands/serve.rs index 16b3b922..210f52f4 100644 --- a/server/src/commands/serve.rs +++ b/server/src/commands/serve.rs @@ -1,19 +1,17 @@ -use std::collections::HashMap; use std::path::PathBuf; use std::process; use std::fs; use rand; -use project::SourceProject; +use project::Project; use web; use session::{Session, SessionConfig}; -use partition::Partition; pub fn serve(project_path: &PathBuf, port: Option) { let server_id = rand::random::(); - let project = match SourceProject::load(project_path) { + let project = match Project::load(project_path) { Ok(v) => { println!("Using project from {}", fs::canonicalize(project_path).unwrap().display()); v @@ -24,21 +22,8 @@ pub fn serve(project_path: &PathBuf, port: Option) { }, }; - let mut partitions = HashMap::new(); - - for (partition_name, partition) in project.partitions.iter() { - let path = fs::canonicalize(project_path.join(&partition.path)).unwrap(); - let target = partition.target.split(".").map(String::from).collect::>(); - - partitions.insert(partition_name.clone(), Partition { - path, - target, - name: partition_name.clone(), - }); - } - let config = SessionConfig { - partitions: partitions.clone(), + partitions: project.partitions.clone(), }; println!("Using session config {:#?}", config); @@ -51,7 +36,7 @@ pub fn serve(project_path: &PathBuf, port: Option) { server_id, rbx_session: session.get_rbx_session(), message_session: session.get_message_session(), - partitions, + partitions: project.partitions, }; println!("Server listening on port {}", web_config.port); diff --git a/server/src/project.rs b/server/src/project.rs index dac531d3..b0b95de7 100644 --- a/server/src/project.rs +++ b/server/src/project.rs @@ -8,6 +8,8 @@ use rand::{self, Rng}; use serde_json; +use partition::Partition; + pub static PROJECT_FILENAME: &'static str = "rojo.json"; #[derive(Debug)] @@ -61,7 +63,7 @@ impl fmt::Display for ProjectInitError { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SourceProjectPartition { /// A slash-separated path to a file or folder, relative to the project's @@ -72,12 +74,14 @@ pub struct SourceProjectPartition { pub target: String, } -/// Represents a project configured by a user for use with Rojo. Holds anything -/// that can be configured with `rojo.json`. -/// -/// In the future, this object will hold dependency information and other handy -/// configurables -#[derive(Clone, Debug, Serialize, Deserialize)] +/// Represents a Rojo project in the format that's most convenient for users to +/// edit. This should generally line up with `Project`, but can diverge when +/// there's either compatibility shims or when the data structures that Rojo +/// want are too verbose to write in JSON but easy to convert from something +/// else. +// +/// Holds anything that can be configured with `rojo.json`. +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(default, rename_all = "camelCase")] pub struct SourceProject { pub name: String, @@ -85,17 +89,98 @@ pub struct SourceProject { pub partitions: HashMap, } -impl SourceProject { +impl Default for SourceProject { + fn default() -> SourceProject { + SourceProject { + name: "new-project".to_string(), + serve_port: 8000, + partitions: HashMap::new(), + } + } +} + +/// Represents a Rojo project in the format that's convenient for Rojo to work +/// with. +#[derive(Debug, Clone)] +pub struct Project { + /// The path to the project file that this project is associated with. + pub project_path: PathBuf, + + /// The name of this project, used for user-facing labels. + pub name: String, + + /// The port that this project will run a web server on. + pub serve_port: u64, + + /// All of the project's partitions, laid out in an expanded way. + pub partitions: HashMap, +} + +impl Project { + fn from_source_project(source_project: SourceProject, project_path: PathBuf) -> Project { + let mut partitions = HashMap::new(); + + { + let project_directory = project_path.parent().unwrap(); + + for (partition_name, partition) in source_project.partitions.into_iter() { + let path = project_directory.join(&partition.path); + let target = partition.target + .split(".") + .map(String::from) + .collect::>(); + + partitions.insert(partition_name.clone(), Partition { + path, + target, + name: partition_name, + }); + } + } + + Project { + project_path, + name: source_project.name, + serve_port: source_project.serve_port, + partitions, + } + } + + fn as_source_project(&self) -> SourceProject { + let mut partitions = HashMap::new(); + + for partition in self.partitions.values() { + let path = partition.path.strip_prefix(&self.project_path) + .unwrap_or_else(|_| &partition.path) + .to_str() + .unwrap() + .to_string(); + + let target = partition.target.join("."); + + partitions.insert(partition.name.clone(), SourceProjectPartition { + path, + target, + }); + } + + SourceProject { + partitions, + name: self.name.clone(), + serve_port: self.serve_port, + } + } + /// Initializes a new project inside the given folder path. - pub fn init>(location: T) -> Result { + pub fn init>(location: T) -> Result { let location = location.as_ref(); - let package_path = location.join(PROJECT_FILENAME); + let project_path = location.join(PROJECT_FILENAME); // We abort if the project file already exists. - fs::metadata(&package_path) + fs::metadata(&project_path) .map_err(|_| ProjectInitError::AlreadyExists)?; - let mut file = File::create(&package_path) + let mut file = File::create(&project_path) .map_err(|_| ProjectInitError::FailedToCreate)?; // Try to give the project a meaningful name. @@ -109,60 +194,53 @@ impl SourceProject { let serve_port = rand::thread_rng().gen_range(2000, 49151); // Configure the project with all of the values we know so far. - let project = SourceProject { + let source_project = SourceProject { name, serve_port, partitions: HashMap::new(), }; - let serialized = serde_json::to_string_pretty(&project).unwrap(); + let serialized = serde_json::to_string_pretty(&source_project).unwrap(); file.write(serialized.as_bytes()) .map_err(|_| ProjectInitError::FailedToWrite)?; - Ok(project) + Ok(Project::from_source_project(source_project, project_path)) } /// Attempts to load a project from the file named PROJECT_FILENAME from the /// given folder. - pub fn load>(location: T) -> Result { - let package_path = location.as_ref().join(Path::new(PROJECT_FILENAME)); + pub fn load>(location: T) -> Result { + let project_path = location.as_ref().join(Path::new(PROJECT_FILENAME)); - fs::metadata(&package_path) - .map_err(|_| ProjectLoadError::DidNotExist(package_path.clone()))?; + fs::metadata(&project_path) + .map_err(|_| ProjectLoadError::DidNotExist(project_path.clone()))?; - let mut file = File::open(&package_path) - .map_err(|_| ProjectLoadError::FailedToOpen(package_path.clone()))?; + let mut file = File::open(&project_path) + .map_err(|_| ProjectLoadError::FailedToOpen(project_path.clone()))?; let mut contents = String::new(); file.read_to_string(&mut contents) - .map_err(|_| ProjectLoadError::FailedToRead(package_path.clone()))?; + .map_err(|_| ProjectLoadError::FailedToRead(project_path.clone()))?; - serde_json::from_str(&contents) - .map_err(|e| ProjectLoadError::InvalidJson(package_path.clone(), e)) + let source_project = serde_json::from_str(&contents) + .map_err(|e| ProjectLoadError::InvalidJson(project_path.clone(), e))?; + + Ok(Project::from_source_project(source_project, project_path)) } /// Saves the given project file to the given folder with the appropriate name. pub fn save>(&self, location: T) -> Result<(), ProjectSaveError> { - let package_path = location.as_ref().join(Path::new(PROJECT_FILENAME)); + let project_path = location.as_ref().join(Path::new(PROJECT_FILENAME)); - let mut file = File::create(&package_path) + let mut file = File::create(&project_path) .map_err(|_| ProjectSaveError::FailedToCreate)?; - let serialized = serde_json::to_string_pretty(self).unwrap(); + let source_project = self.as_source_project(); + let serialized = serde_json::to_string_pretty(&source_project).unwrap(); file.write(serialized.as_bytes()).unwrap(); Ok(()) } } - -impl Default for SourceProject { - fn default() -> SourceProject { - SourceProject { - name: "new-project".to_string(), - serve_port: 8000, - partitions: HashMap::new(), - } - } -}