[oden] Time, path searching, game directory

This commit is contained in:
John Doty 2023-06-30 16:24:54 -07:00
parent 96e95e22ce
commit 26bfcc7a94
8 changed files with 185 additions and 13 deletions

View file

@ -1,4 +1,6 @@
use oden_js::{module::native::NativeModuleBuilder, ContextRef, ValueResult};
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, Mutex};
use std::thread;
@ -69,10 +71,9 @@ impl IoImpl {
let path = path.to_string();
self.thread_pool.execute(Box::new(move || {
// TODO: Actually read the path.
let path = path;
let result = std::fs::read(path);
let result = resolve_path(&path, &[]).and_then(|p| std::fs::read(p));
promise.resolve(move |ctx: &ContextRef| match result {
Ok(v) => ctx.new_array_buffer(v),
Err(err) => Err(err.into()),
@ -100,3 +101,72 @@ impl IoAPI {
Ok(IoAPI {})
}
}
fn append_ext(ext: impl AsRef<OsStr>, path: PathBuf) -> PathBuf {
let mut os_string: OsString = path.into();
os_string.push(".");
os_string.push(ext.as_ref());
os_string.into()
}
/// Resolve a path to an existing file.
pub fn resolve_path(input: &str, extensions: &[&str]) -> std::io::Result<PathBuf> {
let mut path = PathBuf::with_capacity(input.len());
for segment in input.split('/') {
if segment == "." {
continue;
} else if segment == ".." {
if path.parent().is_none() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("{input} contains too many '..'; directory underflow detected"),
));
}
path.pop();
} else if segment.contains(":") || segment.contains('\\') {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("{input} contains invalid characters"),
));
} else {
path.push(segment);
}
}
if path.has_root() {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("{input} is absolute"),
));
}
if path.file_name().is_none() {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("{input} refers to a directory"),
));
}
for prefix in ["game", "src"] {
let mut actual = PathBuf::from(prefix);
actual.push(path.clone());
//eprintln!("Trying '{}'...", actual.to_string_lossy());
match actual.try_exists() {
Ok(true) => return Ok(actual),
_ => (),
}
for extension in extensions.iter() {
let actual = append_ext(extension, actual.clone());
//eprintln!("Trying '{}'...", actual.to_string_lossy());
match actual.try_exists() {
Ok(true) => return Ok(actual),
_ => (),
}
}
}
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("{input} not found or not accessible"),
));
}