forked from rojo-rbx/rojo
Improve error reporting for IO issues
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
* Added new (empty) diagnostic page served from the server
|
* Added new (empty) diagnostic page served from the server
|
||||||
|
* Added better error messages for when a file is missing that's referenced by a Rojo project
|
||||||
|
|
||||||
## [0.5.0 Alpha 2](https://github.com/LPGhatguy/rojo/releases/tag/v0.5.0-alpha.2) (January 28, 2019)
|
## [0.5.0 Alpha 2](https://github.com/LPGhatguy/rojo/releases/tag/v0.5.0-alpha.2) (January 28, 2019)
|
||||||
* Added support for `.model.json` files, compatible with 0.4.x
|
* Added support for `.model.json` files, compatible with 0.4.x
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use failure::Fail;
|
|||||||
use crate::{
|
use crate::{
|
||||||
rbx_session::construct_oneoff_tree,
|
rbx_session::construct_oneoff_tree,
|
||||||
project::{Project, ProjectLoadFuzzyError},
|
project::{Project, ProjectLoadFuzzyError},
|
||||||
imfs::Imfs,
|
imfs::{Imfs, FsError},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
@@ -55,14 +55,18 @@ pub enum BuildError {
|
|||||||
XmlModelEncodeError(rbx_xml::EncodeError),
|
XmlModelEncodeError(rbx_xml::EncodeError),
|
||||||
|
|
||||||
#[fail(display = "Binary model file error")]
|
#[fail(display = "Binary model file error")]
|
||||||
BinaryModelEncodeError(rbx_binary::EncodeError)
|
BinaryModelEncodeError(rbx_binary::EncodeError),
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
FsError(#[fail(cause)] FsError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_from!(BuildError {
|
impl_from!(BuildError {
|
||||||
ProjectLoadFuzzyError => ProjectLoadError,
|
ProjectLoadFuzzyError => ProjectLoadError,
|
||||||
io::Error => IoError,
|
io::Error => IoError,
|
||||||
rbx_xml::EncodeError => XmlModelEncodeError,
|
rbx_xml::EncodeError => XmlModelEncodeError,
|
||||||
rbx_binary::EncodeError => BinaryModelEncodeError
|
rbx_binary::EncodeError => BinaryModelEncodeError,
|
||||||
|
FsError => FsError,
|
||||||
});
|
});
|
||||||
|
|
||||||
pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
|
pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use failure::Fail;
|
|||||||
use crate::{
|
use crate::{
|
||||||
project::{Project, ProjectLoadFuzzyError},
|
project::{Project, ProjectLoadFuzzyError},
|
||||||
web::Server,
|
web::Server,
|
||||||
|
imfs::FsError,
|
||||||
live_session::LiveSession,
|
live_session::LiveSession,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,10 +25,14 @@ pub struct ServeOptions {
|
|||||||
pub enum ServeError {
|
pub enum ServeError {
|
||||||
#[fail(display = "Project load error: {}", _0)]
|
#[fail(display = "Project load error: {}", _0)]
|
||||||
ProjectLoadError(#[fail(cause)] ProjectLoadFuzzyError),
|
ProjectLoadError(#[fail(cause)] ProjectLoadFuzzyError),
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
FsError(#[fail(cause)] FsError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_from!(ServeError {
|
impl_from!(ServeError {
|
||||||
ProjectLoadFuzzyError => ProjectLoadError,
|
ProjectLoadFuzzyError => ProjectLoadError,
|
||||||
|
FsError => FsError,
|
||||||
});
|
});
|
||||||
|
|
||||||
pub fn serve(options: &ServeOptions) -> Result<(), ServeError> {
|
pub fn serve(options: &ServeOptions) -> Result<(), ServeError> {
|
||||||
@@ -38,7 +43,7 @@ pub fn serve(options: &ServeOptions) -> Result<(), ServeError> {
|
|||||||
info!("Found project at {}", project.file_location.display());
|
info!("Found project at {}", project.file_location.display());
|
||||||
info!("Using project {:#?}", project);
|
info!("Using project {:#?}", project);
|
||||||
|
|
||||||
let live_session = Arc::new(LiveSession::new(Arc::clone(&project)).unwrap());
|
let live_session = Arc::new(LiveSession::new(Arc::clone(&project))?);
|
||||||
let server = Server::new(Arc::clone(&live_session));
|
let server = Server::new(Arc::clone(&live_session));
|
||||||
|
|
||||||
let port = options.port
|
let port = options.port
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use reqwest::header::{ACCEPT, USER_AGENT, CONTENT_TYPE, COOKIE};
|
|||||||
use crate::{
|
use crate::{
|
||||||
rbx_session::construct_oneoff_tree,
|
rbx_session::construct_oneoff_tree,
|
||||||
project::{Project, ProjectLoadFuzzyError},
|
project::{Project, ProjectLoadFuzzyError},
|
||||||
imfs::Imfs,
|
imfs::{Imfs, FsError},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Debug, Fail)]
|
||||||
@@ -33,6 +33,9 @@ pub enum UploadError {
|
|||||||
|
|
||||||
#[fail(display = "XML model file error")]
|
#[fail(display = "XML model file error")]
|
||||||
XmlModelEncodeError(rbx_xml::EncodeError),
|
XmlModelEncodeError(rbx_xml::EncodeError),
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
FsError(#[fail(cause)] FsError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_from!(UploadError {
|
impl_from!(UploadError {
|
||||||
@@ -40,6 +43,7 @@ impl_from!(UploadError {
|
|||||||
io::Error => IoError,
|
io::Error => IoError,
|
||||||
reqwest::Error => HttpError,
|
reqwest::Error => HttpError,
|
||||||
rbx_xml::EncodeError => XmlModelEncodeError,
|
rbx_xml::EncodeError => XmlModelEncodeError,
|
||||||
|
FsError => FsError,
|
||||||
});
|
});
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -1,15 +1,41 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
path::{self, Path, PathBuf},
|
path::{self, Path, PathBuf},
|
||||||
|
fmt,
|
||||||
fs,
|
fs,
|
||||||
io,
|
io,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use failure::Fail;
|
||||||
use serde_derive::{Serialize, Deserialize};
|
use serde_derive::{Serialize, Deserialize};
|
||||||
|
|
||||||
use crate::project::{Project, ProjectNode};
|
use crate::project::{Project, ProjectNode};
|
||||||
|
|
||||||
fn add_sync_points(imfs: &mut Imfs, project_node: &ProjectNode) -> io::Result<()> {
|
/// A wrapper around io::Error that also attaches the path associated with the
|
||||||
|
/// error.
|
||||||
|
#[derive(Debug, Fail)]
|
||||||
|
pub struct FsError {
|
||||||
|
#[fail(cause)]
|
||||||
|
inner: io::Error,
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FsError {
|
||||||
|
fn new<P: Into<PathBuf>>(inner: io::Error, path: P) -> FsError {
|
||||||
|
FsError {
|
||||||
|
inner,
|
||||||
|
path: path.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FsError {
|
||||||
|
fn fmt(&self, output: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(output, "{}: {}", self.path.display(), self.inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_sync_points(imfs: &mut Imfs, project_node: &ProjectNode) -> Result<(), FsError> {
|
||||||
match project_node {
|
match project_node {
|
||||||
ProjectNode::Instance(node) => {
|
ProjectNode::Instance(node) => {
|
||||||
for child in node.children.values() {
|
for child in node.children.values() {
|
||||||
@@ -44,7 +70,7 @@ impl Imfs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_roots_from_project(&mut self, project: &Project) -> io::Result<()> {
|
pub fn add_roots_from_project(&mut self, project: &Project) -> Result<(), FsError> {
|
||||||
add_sync_points(self, &project.tree)
|
add_sync_points(self, &project.tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +89,7 @@ impl Imfs {
|
|||||||
self.items.get(path)
|
self.items.get(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_root(&mut self, path: &Path) -> io::Result<()> {
|
pub fn add_root(&mut self, path: &Path) -> Result<(), FsError> {
|
||||||
debug_assert!(path.is_absolute());
|
debug_assert!(path.is_absolute());
|
||||||
debug_assert!(!self.is_within_roots(path));
|
debug_assert!(!self.is_within_roots(path));
|
||||||
|
|
||||||
@@ -84,21 +110,21 @@ impl Imfs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path_created(&mut self, path: &Path) -> io::Result<()> {
|
pub fn path_created(&mut self, path: &Path) -> Result<(), FsError> {
|
||||||
debug_assert!(path.is_absolute());
|
debug_assert!(path.is_absolute());
|
||||||
debug_assert!(self.is_within_roots(path));
|
debug_assert!(self.is_within_roots(path));
|
||||||
|
|
||||||
self.descend_and_read_from_disk(path)
|
self.descend_and_read_from_disk(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path_updated(&mut self, path: &Path) -> io::Result<()> {
|
pub fn path_updated(&mut self, path: &Path) -> Result<(), FsError> {
|
||||||
debug_assert!(path.is_absolute());
|
debug_assert!(path.is_absolute());
|
||||||
debug_assert!(self.is_within_roots(path));
|
debug_assert!(self.is_within_roots(path));
|
||||||
|
|
||||||
self.descend_and_read_from_disk(path)
|
self.descend_and_read_from_disk(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path_removed(&mut self, path: &Path) -> io::Result<()> {
|
pub fn path_removed(&mut self, path: &Path) -> Result<(), FsError> {
|
||||||
debug_assert!(path.is_absolute());
|
debug_assert!(path.is_absolute());
|
||||||
debug_assert!(self.is_within_roots(path));
|
debug_assert!(self.is_within_roots(path));
|
||||||
|
|
||||||
@@ -111,7 +137,7 @@ impl Imfs {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path_moved(&mut self, from_path: &Path, to_path: &Path) -> io::Result<()> {
|
pub fn path_moved(&mut self, from_path: &Path, to_path: &Path) -> Result<(), FsError> {
|
||||||
self.path_removed(from_path)?;
|
self.path_removed(from_path)?;
|
||||||
self.path_created(to_path)?;
|
self.path_created(to_path)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -161,9 +187,9 @@ impl Imfs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn descend_and_read_from_disk(&mut self, path: &Path) -> io::Result<()> {
|
fn descend_and_read_from_disk(&mut self, path: &Path) -> Result<(), FsError> {
|
||||||
let root_path = self.get_root_path(path)
|
let root_path = self.get_root_path(path)
|
||||||
.expect("Tried to mkdirp for path that wasn't within roots!");
|
.expect("Tried to descent and read for path that wasn't within roots!");
|
||||||
|
|
||||||
// If this path is a root, we should read the entire thing.
|
// If this path is a root, we should read the entire thing.
|
||||||
if root_path == path {
|
if root_path == path {
|
||||||
@@ -193,11 +219,13 @@ impl Imfs {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_from_disk(&mut self, path: &Path) -> io::Result<()> {
|
fn read_from_disk(&mut self, path: &Path) -> Result<(), FsError> {
|
||||||
let metadata = fs::metadata(path)?;
|
let metadata = fs::metadata(path)
|
||||||
|
.map_err(|e| FsError::new(e, path))?;
|
||||||
|
|
||||||
if metadata.is_file() {
|
if metadata.is_file() {
|
||||||
let contents = fs::read(path)?;
|
let contents = fs::read(path)
|
||||||
|
.map_err(|e| FsError::new(e, path))?;
|
||||||
let item = ImfsItem::File(ImfsFile {
|
let item = ImfsItem::File(ImfsFile {
|
||||||
path: path.to_path_buf(),
|
path: path.to_path_buf(),
|
||||||
contents,
|
contents,
|
||||||
@@ -218,8 +246,13 @@ impl Imfs {
|
|||||||
|
|
||||||
self.items.insert(path.to_path_buf(), item);
|
self.items.insert(path.to_path_buf(), item);
|
||||||
|
|
||||||
for entry in fs::read_dir(path)? {
|
let dir_children = fs::read_dir(path)
|
||||||
let entry = entry?;
|
.map_err(|e| FsError::new(e, path))?;
|
||||||
|
|
||||||
|
for entry in dir_children {
|
||||||
|
let entry = entry
|
||||||
|
.map_err(|e| FsError::new(e, path))?;
|
||||||
|
|
||||||
let child_path = entry.path();
|
let child_path = entry.path();
|
||||||
|
|
||||||
self.read_from_disk(&child_path)?;
|
self.read_from_disk(&child_path)?;
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
use std::{
|
use std::{
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
io,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
fs_watcher::FsWatcher,
|
fs_watcher::FsWatcher,
|
||||||
imfs::Imfs,
|
imfs::{Imfs, FsError},
|
||||||
message_queue::MessageQueue,
|
message_queue::MessageQueue,
|
||||||
project::Project,
|
project::Project,
|
||||||
rbx_session::RbxSession,
|
rbx_session::RbxSession,
|
||||||
@@ -24,7 +23,7 @@ pub struct LiveSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LiveSession {
|
impl LiveSession {
|
||||||
pub fn new(project: Arc<Project>) -> io::Result<LiveSession> {
|
pub fn new(project: Arc<Project>) -> Result<LiveSession, FsError> {
|
||||||
let imfs = {
|
let imfs = {
|
||||||
let mut imfs = Imfs::new();
|
let mut imfs = Imfs::new();
|
||||||
imfs.add_roots_from_project(&project)?;
|
imfs.add_roots_from_project(&project)?;
|
||||||
|
|||||||
6
test-projects/missing-files/roblox-project.json
Normal file
6
test-projects/missing-files/roblox-project.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "missing-files",
|
||||||
|
"tree": {
|
||||||
|
"$path": "does-not-exist"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user