diff --git a/Cargo.lock b/Cargo.lock
index a08937cc..e2939241 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -403,6 +403,11 @@ dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "doc-comment"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "dtoa"
version = "0.4.4"
@@ -1634,6 +1639,7 @@ dependencies = [
"serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "snafu 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1847,6 +1853,25 @@ name = "smallvec"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "snafu"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "snafu-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "snafu-derive"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "snax"
version = "0.2.0"
@@ -2433,6 +2458,7 @@ dependencies = [
"checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc"
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
+"checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97"
"checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e"
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
"checksum encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
@@ -2586,6 +2612,8 @@ dependencies = [
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
"checksum smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86"
+"checksum snafu 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41207ca11f96a62cd34e6b7fdf73d322b25ae3848eb9d38302169724bb32cf27"
+"checksum snafu-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c5e338c8b0577457c9dda8e794b6ad7231c96e25b1b0dd5842d52249020c1c0"
"checksum snax 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7ef82be0baf3ae45701ab992230f1f4889c15984736d87f37558aea3e4e321af"
"checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
diff --git a/Cargo.toml b/Cargo.toml
index 9e203466..e501d3eb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -70,6 +70,7 @@ ritz = "0.1.0"
rlua = "0.16.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
+snafu = "0.6.0"
structopt = "0.3"
termcolor = "1.0.5"
uuid = { version = "0.7", features = ["v4", "serde"] }
diff --git a/rojo-test/build-test-snapshots/build_test__rbxmx_in_folder.snap b/rojo-test/build-test-snapshots/build_test__rbxmx_in_folder.snap
index 4b9740e1..21a7a396 100644
--- a/rojo-test/build-test-snapshots/build_test__rbxmx_in_folder.snap
+++ b/rojo-test/build-test-snapshots/build_test__rbxmx_in_folder.snap
@@ -25,12 +25,14 @@ expression: contents
1
[null]
-
+
+
-
Cool StringValue
-
+
+
Did you know that BaseValue.Changed is different than Instance.Changed?
diff --git a/rojo-test/build-test-snapshots/build_test__rbxmx_ref.rbxmx.snap b/rojo-test/build-test-snapshots/build_test__rbxmx_ref.rbxmx.snap
index 9a796330..26ada371 100644
--- a/rojo-test/build-test-snapshots/build_test__rbxmx_ref.rbxmx.snap
+++ b/rojo-test/build-test-snapshots/build_test__rbxmx_ref.rbxmx.snap
@@ -6,19 +6,22 @@ expression: contents
-
rbxmx_ref
-
+
+
-
Target
-
+
+
Pointed to by ObjectValue
-
Pointer
-
+
+
[1]
diff --git a/src/change_processor.rs b/src/change_processor.rs
index f13241e7..c721260a 100644
--- a/src/change_processor.rs
+++ b/src/change_processor.rs
@@ -178,15 +178,20 @@ fn update_affected_instances(
}
}
}
- InstigatingSource::ProjectNode(instance_name, project_node) => {
+ InstigatingSource::ProjectNode(project_path, instance_name, project_node) => {
// This instance is the direct subject of a project node. Since
// there might be information associated with our instance from
// the project file, we snapshot the entire project node again.
- let snapshot =
- snapshot_project_node(&metadata.context, instance_name, project_node, &vfs)
- .expect("snapshot failed")
- .expect("snapshot did not return an instance");
+ let snapshot = snapshot_project_node(
+ &metadata.context,
+ &project_path,
+ instance_name,
+ project_node,
+ &vfs,
+ )
+ .expect("snapshot failed")
+ .expect("snapshot did not return an instance");
let patch_set = compute_patch_set(&snapshot, &tree, id);
apply_patch_set(tree, patch_set)
diff --git a/src/commands/build.rs b/src/commands/build.rs
index d5021dc1..1e3a03a5 100644
--- a/src/commands/build.rs
+++ b/src/commands/build.rs
@@ -8,7 +8,7 @@ use failure::Fail;
use crate::{
cli::BuildCommand,
common_setup,
- project::ProjectLoadError,
+ project::ProjectError,
vfs::{FsError, RealFetcher, Vfs, WatchMode},
};
@@ -47,7 +47,7 @@ pub enum BuildError {
BinaryModelEncodeError(rbx_binary::EncodeError),
#[fail(display = "{}", _0)]
- ProjectLoadError(#[fail(cause)] ProjectLoadError),
+ ProjectError(#[fail(cause)] ProjectError),
#[fail(display = "{}", _0)]
FsError(#[fail(cause)] FsError),
@@ -57,7 +57,7 @@ impl_from!(BuildError {
io::Error => IoError,
rbx_xml::EncodeError => XmlModelEncodeError,
rbx_binary::EncodeError => BinaryModelEncodeError,
- ProjectLoadError => ProjectLoadError,
+ ProjectError => ProjectError,
FsError => FsError,
});
diff --git a/src/commands/init.rs b/src/commands/init.rs
index c4fe437f..2ea6b5e3 100644
--- a/src/commands/init.rs
+++ b/src/commands/init.rs
@@ -1,37 +1,17 @@
use failure::Fail;
-use crate::{
- cli::{InitCommand, InitKind},
- project::{Project, ProjectInitError},
-};
+use crate::{cli::InitCommand, project::ProjectError};
#[derive(Debug, Fail)]
pub enum InitError {
#[fail(display = "Project init error: {}", _0)]
- ProjectInitError(#[fail(cause)] ProjectInitError),
+ ProjectError(#[fail(cause)] ProjectError),
}
impl_from!(InitError {
- ProjectInitError => ProjectInitError,
+ ProjectError => ProjectError,
});
-pub fn init(options: InitCommand) -> Result<(), InitError> {
- let (project_path, project_kind) = match options.kind {
- InitKind::Place => {
- let path = Project::init_place(&options.path)?;
- (path, "place")
- }
- InitKind::Model => {
- let path = Project::init_model(&options.path)?;
- (path, "model")
- }
- };
-
- println!(
- "Created new {} project file at {}",
- project_kind,
- project_path.display()
- );
-
- Ok(())
+pub fn init(_options: InitCommand) -> Result<(), InitError> {
+ unimplemented!("init command");
}
diff --git a/src/commands/serve.rs b/src/commands/serve.rs
index 06c83614..bc47f981 100644
--- a/src/commands/serve.rs
+++ b/src/commands/serve.rs
@@ -8,7 +8,7 @@ use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
use crate::{
cli::ServeCommand,
- project::ProjectLoadError,
+ project::ProjectError,
serve_session::ServeSession,
vfs::{RealFetcher, Vfs, WatchMode},
web::LiveServer,
@@ -19,11 +19,11 @@ const DEFAULT_PORT: u16 = 34872;
#[derive(Debug, Fail)]
pub enum ServeError {
#[fail(display = "Couldn't load project: {}", _0)]
- ProjectLoad(#[fail(cause)] ProjectLoadError),
+ ProjectError(#[fail(cause)] ProjectError),
}
impl_from!(ServeError {
- ProjectLoadError => ProjectLoad,
+ ProjectError => ProjectError,
});
pub fn serve(options: ServeCommand) -> Result<(), ServeError> {
diff --git a/src/common_setup.rs b/src/common_setup.rs
index 160b4c6c..a744b5b5 100644
--- a/src/common_setup.rs
+++ b/src/common_setup.rs
@@ -6,7 +6,7 @@ use std::path::Path;
use rbx_dom_weak::RbxInstanceProperties;
use crate::{
- project::{Project, ProjectLoadError},
+ project::Project,
snapshot::{
apply_patch_set, compute_patch_set, InstanceContext, InstancePropertiesWithMeta, RojoTree,
},
@@ -19,11 +19,7 @@ pub fn start(
vfs: &Vfs,
) -> (Option, RojoTree) {
log::trace!("Loading project file from {}", fuzzy_project_path.display());
- let maybe_project = match Project::load_fuzzy(fuzzy_project_path) {
- Ok(project) => Some(project),
- Err(ProjectLoadError::NotFound) => None,
- Err(other) => panic!("{}", other), // TODO: return error upward
- };
+ let maybe_project = Project::load_fuzzy(fuzzy_project_path).expect("TODO: Project load failed");
log::trace!("Constructing initial tree");
let mut tree = RojoTree::new(InstancePropertiesWithMeta {
@@ -37,23 +33,13 @@ pub fn start(
let root_id = tree.get_root_id();
- log::trace!("Constructing snapshot context");
- let snapshot_context = InstanceContext::default();
- if let Some(project) = &maybe_project {
- // If the project file defines no plugins, then there's no need to
- // initialize the snapshot plugin context.
- if !project.plugins.is_empty() {
- // TODO: Initialize plugins in instance context
- }
- }
-
log::trace!("Reading project root");
let entry = vfs
.get(fuzzy_project_path)
.expect("could not get project path");
log::trace!("Generating snapshot of instances from VFS");
- let snapshot = snapshot_from_vfs(&snapshot_context, vfs, &entry)
+ let snapshot = snapshot_from_vfs(&InstanceContext::default(), vfs, &entry)
.expect("snapshot failed")
.expect("snapshot did not return an instance");
diff --git a/src/project.rs b/src/project.rs
index 532f6590..3ba7ed7e 100644
--- a/src/project.rs
+++ b/src/project.rs
@@ -1,339 +1,63 @@
use std::{
collections::{BTreeMap, HashMap, HashSet},
- fmt,
- fs::{self, File},
- io,
+ fs, io,
path::{Path, PathBuf},
};
-use failure::Fail;
-use log::warn;
-use rbx_dom_weak::{RbxValue, UnresolvedRbxValue};
-use serde::{Deserialize, Serialize, Serializer};
-
-static DEFAULT_PLACE: &str = include_str!("../assets/place.project.json");
+use rbx_dom_weak::UnresolvedRbxValue;
+use serde::{Deserialize, Serialize};
+use snafu::{ResultExt, Snafu};
pub static PROJECT_FILENAME: &str = "default.project.json";
-/// SourceProject is the format that users author projects on-disk. Since we
-/// want to do things like transforming paths to be absolute before handing them
-/// off to the rest of Rojo, we use this intermediate struct.
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(deny_unknown_fields, rename_all = "camelCase")]
-struct SourceProject {
- name: String,
- tree: SourceProjectNode,
+/// Error type returned by any function that handles projects.
+#[derive(Debug, Snafu)]
+pub struct ProjectError(Error);
- #[serde(skip_serializing_if = "Option::is_none")]
- serve_port: Option,
-
- #[serde(skip_serializing_if = "Option::is_none")]
- serve_place_ids: Option>,
-
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- #[cfg_attr(not(feature = "user-plugins"), serde(skip_deserializing))]
- plugins: Vec,
-}
-
-impl SourceProject {
- /// Consumes the SourceProject and yields a Project, ready for prime-time.
- pub fn into_project(self, project_file_location: &Path) -> Project {
- let tree = self.tree.into_project_node(project_file_location);
-
- let project_folder = project_file_location.parent().unwrap();
- let plugins = self
- .plugins
- .into_iter()
- .map(|path| project_folder.join(path))
- .collect();
-
- Project {
- name: self.name,
- tree,
- serve_port: self.serve_port,
- serve_place_ids: self.serve_place_ids,
- plugins,
- file_location: PathBuf::from(project_file_location),
- }
- }
-}
-
-/// An alternative serializer for `UnresolvedRbxValue` that uses the minimum
-/// representation of the value.
-///
-/// For example, the default Serialize impl might give you:
-///
-/// ```json
-/// {
-/// "Type": "Bool",
-/// "Value": true
-/// }
-/// ```
-///
-/// But in reality, users are expected to write just:
-///
-/// ```json
-/// true
-/// ```
-///
-/// This holds true for other values that might be ambiguous or just have more
-/// complicated representations like enums.
-fn serialize_unresolved_minimal(
- unresolved: &UnresolvedRbxValue,
- serializer: S,
-) -> Result
-where
- S: Serializer,
-{
- match unresolved {
- UnresolvedRbxValue::Ambiguous(_) => unresolved.serialize(serializer),
- UnresolvedRbxValue::Concrete(concrete) => match concrete {
- RbxValue::Bool { value } => value.serialize(serializer),
- RbxValue::CFrame { value } => value.serialize(serializer),
- RbxValue::Color3 { value } => value.serialize(serializer),
- RbxValue::Color3uint8 { value } => value.serialize(serializer),
- RbxValue::Content { value } => value.serialize(serializer),
- RbxValue::Float32 { value } => value.serialize(serializer),
- RbxValue::Int32 { value } => value.serialize(serializer),
- RbxValue::String { value } => value.serialize(serializer),
- RbxValue::UDim { value } => value.serialize(serializer),
- RbxValue::UDim2 { value } => value.serialize(serializer),
- RbxValue::Vector2 { value } => value.serialize(serializer),
- RbxValue::Vector2int16 { value } => value.serialize(serializer),
- RbxValue::Vector3 { value } => value.serialize(serializer),
- RbxValue::Vector3int16 { value } => value.serialize(serializer),
- _ => concrete.serialize(serializer),
- },
- }
-}
-
-/// A wrapper around serialize_unresolved_minimal that handles the HashMap case.
-fn serialize_unresolved_map(
- value: &HashMap,
- serializer: S,
-) -> Result
-where
- S: Serializer,
-{
- use serde::ser::SerializeMap;
-
- #[derive(Serialize)]
- struct Minimal<'a>(
- #[serde(serialize_with = "serialize_unresolved_minimal")] &'a UnresolvedRbxValue,
- );
-
- let mut map = serializer.serialize_map(Some(value.len()))?;
- for (k, v) in value {
- map.serialize_key(k)?;
- map.serialize_value(&Minimal(v))?;
- }
- map.end()
-}
-
-/// Similar to SourceProject, the structure of nodes in the project tree is
-/// slightly different on-disk than how we want to handle them in the rest of
-/// Rojo.
-#[derive(Debug, Clone, Serialize, Deserialize)]
-struct SourceProjectNode {
- #[serde(rename = "$className", skip_serializing_if = "Option::is_none")]
- class_name: Option,
-
- #[serde(
- rename = "$properties",
- default = "HashMap::new",
- skip_serializing_if = "HashMap::is_empty",
- serialize_with = "serialize_unresolved_map"
- )]
- properties: HashMap,
-
- #[serde(
- rename = "$ignoreUnknownInstances",
- skip_serializing_if = "Option::is_none"
- )]
- ignore_unknown_instances: Option,
-
- #[serde(rename = "$path", skip_serializing_if = "Option::is_none")]
- path: Option,
-
- #[serde(flatten)]
- children: BTreeMap,
-}
-
-impl SourceProjectNode {
- /// Consumes the SourceProjectNode and turns it into a ProjectNode.
- pub fn into_project_node(self, project_file_location: &Path) -> ProjectNode {
- let children = self
- .children
- .iter()
- .map(|(key, value)| {
- (
- key.clone(),
- value.clone().into_project_node(project_file_location),
- )
- })
- .collect();
-
- // Make sure that paths are absolute, transforming them by adding the
- // project folder if they're not already absolute.
- let path = self.path.as_ref().map(|source_path| {
- if Path::new(source_path).is_absolute() {
- PathBuf::from(source_path)
- } else {
- let project_folder_location = project_file_location.parent().unwrap();
- project_folder_location.join(source_path)
- }
- });
-
- ProjectNode {
- class_name: self.class_name,
- properties: self.properties,
- ignore_unknown_instances: self.ignore_unknown_instances,
- path,
- children,
- }
- }
-}
-
-#[derive(Debug, Fail)]
-pub enum ProjectLoadError {
- NotFound,
-
- Io {
- #[fail(cause)]
- inner: io::Error,
- path: PathBuf,
- },
+#[derive(Debug, Snafu)]
+enum Error {
+ /// A general IO error occurred.
+ Io { source: io::Error, path: PathBuf },
+ /// An error with JSON parsing occurred.
Json {
- #[fail(cause)]
- inner: serde_json::Error,
+ source: serde_json::Error,
path: PathBuf,
},
}
-impl fmt::Display for ProjectLoadError {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- use self::ProjectLoadError::*;
-
- match self {
- NotFound => write!(formatter, "Project file not found"),
- Io { inner, path } => {
- write!(formatter, "I/O error: {} in path {}", inner, path.display())
- }
- Json { inner, path } => write!(
- formatter,
- "JSON error: {} in path {}",
- inner,
- path.display()
- ),
- }
- }
-}
-
-/// Error returned by Project::init_place and Project::init_model
-#[derive(Debug, Fail)]
-pub enum ProjectInitError {
- AlreadyExists(PathBuf),
- IoError(#[fail(cause)] io::Error),
- SaveError(#[fail(cause)] ProjectSaveError),
- JsonError(#[fail(cause)] serde_json::Error),
-}
-
-impl fmt::Display for ProjectInitError {
- fn fmt(&self, output: &mut fmt::Formatter) -> fmt::Result {
- match self {
- ProjectInitError::AlreadyExists(path) => {
- write!(output, "Path {} already exists", path.display())
- }
- ProjectInitError::IoError(inner) => write!(output, "IO error: {}", inner),
- ProjectInitError::SaveError(inner) => write!(output, "{}", inner),
- ProjectInitError::JsonError(inner) => write!(output, "{}", inner),
- }
- }
-}
-
-/// Error returned by Project::save
-#[derive(Debug, Fail)]
-pub enum ProjectSaveError {
- #[fail(display = "JSON error: {}", _0)]
- JsonError(#[fail(cause)] serde_json::Error),
-
- #[fail(display = "IO error: {}", _0)]
- IoError(#[fail(cause)] io::Error),
-}
-
-#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
-pub struct ProjectNode {
- pub class_name: Option,
- pub children: BTreeMap,
- pub properties: HashMap,
- pub ignore_unknown_instances: Option,
-
- #[serde(serialize_with = "crate::path_serializer::serialize_option_absolute")]
- pub path: Option,
-}
-
-impl ProjectNode {
- fn validate_reserved_names(&self) {
- for (name, child) in &self.children {
- if name.starts_with('$') {
- warn!(
- "Keys starting with '$' are reserved by Rojo to ensure forward compatibility."
- );
- warn!(
- "This project uses the key '{}', which should be renamed.",
- name
- );
- }
-
- child.validate_reserved_names();
- }
- }
-
- fn to_source_node(&self, project_file_location: &Path) -> SourceProjectNode {
- let children = self
- .children
- .iter()
- .map(|(key, value)| (key.clone(), value.to_source_node(project_file_location)))
- .collect();
-
- // If paths are relative to the project file, transform them to look
- // Unixy and write relative paths instead.
- //
- // This isn't perfect, since it means that paths like .. will stay as
- // absolute paths and make projects non-portable. Fixing this probably
- // means keeping the paths relative in the project format and making
- // everywhere else in Rojo do the resolution locally.
- let path = self.path.as_ref().map(|path| {
- let project_folder_location = project_file_location.parent().unwrap();
-
- match path.strip_prefix(project_folder_location) {
- Ok(stripped) => stripped.to_str().unwrap().replace("\\", "/"),
- Err(_) => format!("{}", path.display()),
- }
- });
-
- SourceProjectNode {
- class_name: self.class_name.clone(),
- properties: self.properties.clone(),
- ignore_unknown_instances: self.ignore_unknown_instances,
- children,
- path,
- }
- }
-}
-
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct Project {
+ /// The name of the top-level instance described by the project.
pub name: String,
+
+ /// The tree of instances described by this project. Projects always
+ /// describe at least one instance.
pub tree: ProjectNode,
+
+ /// If specified, sets the default port that `rojo serve` should use when
+ /// using this project for live sync.
+ #[serde(skip_serializing_if = "Option::is_none")]
pub serve_port: Option,
+
+ /// If specified, contains the set of place IDs that this project is
+ /// compatible with when doing live sync.
+ ///
+ /// This setting is intended to help prevent syncing a Rojo project into the
+ /// wrong Roblox place.
+ #[serde(skip_serializing_if = "Option::is_none")]
pub serve_place_ids: Option>,
- pub plugins: Vec,
+
+ /// The path to the file that this project came from. Relative paths in the
+ /// project should be considered relative to the parent of this field, also
+ /// given by `Project::folder_location`.
+ #[serde(skip)]
pub file_location: PathBuf,
}
impl Project {
+ /// Tells whether the given path describes a Rojo project.
pub fn is_project_file(path: &Path) -> bool {
path.file_name()
.and_then(|name| name.to_str())
@@ -341,97 +65,6 @@ impl Project {
.unwrap_or(false)
}
- pub fn init_place(project_fuzzy_path: &Path) -> Result {
- let project_path = Project::pick_path_for_init(project_fuzzy_path)?;
-
- let project_name = if project_fuzzy_path == project_path {
- project_fuzzy_path
- .parent()
- .expect("Path did not have a parent directory")
- .file_name()
- .expect("Path did not have a file name")
- .to_str()
- .expect("Path had invalid Unicode")
- } else {
- project_fuzzy_path
- .file_name()
- .expect("Path did not have a file name")
- .to_str()
- .expect("Path had invalid Unicode")
- };
-
- let mut project = Project::load_from_slice(DEFAULT_PLACE.as_bytes(), &project_path)
- .map_err(ProjectInitError::JsonError)?;
-
- project.name = project_name.to_owned();
-
- project.save().map_err(ProjectInitError::SaveError)?;
-
- Ok(project_path)
- }
-
- pub fn init_model(project_fuzzy_path: &Path) -> Result {
- let project_path = Project::pick_path_for_init(project_fuzzy_path)?;
-
- let project_name = if project_fuzzy_path == project_path {
- project_fuzzy_path
- .parent()
- .expect("Path did not have a parent directory")
- .file_name()
- .expect("Path did not have a file name")
- .to_str()
- .expect("Path had invalid Unicode")
- } else {
- project_fuzzy_path
- .file_name()
- .expect("Path did not have a file name")
- .to_str()
- .expect("Path had invalid Unicode")
- };
-
- let project_folder_path = project_path
- .parent()
- .expect("Path did not have a parent directory");
-
- let tree = ProjectNode {
- path: Some(project_folder_path.join("src")),
- ..Default::default()
- };
-
- let project = Project {
- name: project_name.to_string(),
- tree,
- serve_port: None,
- serve_place_ids: None,
- plugins: Vec::new(),
- file_location: project_path.clone(),
- };
-
- project.save().map_err(ProjectInitError::SaveError)?;
-
- Ok(project_path)
- }
-
- fn pick_path_for_init(project_fuzzy_path: &Path) -> Result {
- let is_exact = project_fuzzy_path.extension().is_some();
-
- let project_path = if is_exact {
- project_fuzzy_path.to_path_buf()
- } else {
- project_fuzzy_path.join(PROJECT_FILENAME)
- };
-
- match fs::metadata(&project_path) {
- Err(error) => match error.kind() {
- io::ErrorKind::NotFound => {}
- _ => return Err(ProjectInitError::IoError(error)),
- },
- Ok(_) => return Err(ProjectInitError::AlreadyExists(project_path)),
- }
-
- Ok(project_path)
- }
-
/// Attempt to locate a project represented by the given path.
///
/// This will find a project if the path refers to a `.project.json` file,
@@ -464,51 +97,40 @@ impl Project {
pub fn load_from_slice(
contents: &[u8],
project_file_location: &Path,
- ) -> Result {
- let parsed: SourceProject = serde_json::from_slice(&contents)?;
-
- Ok(parsed.into_project(project_file_location))
+ ) -> Result {
+ let mut project: Self = serde_json::from_slice(&contents)?;
+ project.file_location = project_file_location.to_path_buf();
+ project.check_compatibility();
+ Ok(project)
}
- pub fn load_fuzzy(fuzzy_project_location: &Path) -> Result {
+ pub fn load_fuzzy(fuzzy_project_location: &Path) -> Result