use std::str;
use std::ascii::AsciiExt;
use std::path::{Path, PathBuf, MAIN_SEPARATOR};
use kailua_env::{Spanned, WithLoc};
use kailua_diag::{Report, Stop};
use kailua_syntax::Chunk;
pub trait Options {
fn set_package_path(&mut self, _path: Spanned<&[u8]>,
_report: &Report) -> Result<(), Option<Stop>> {
Ok(())
}
fn set_package_cpath(&mut self, _path: Spanned<&[u8]>,
_report: &Report) -> Result<(), Option<Stop>> {
Ok(())
}
fn require_chunk(&mut self, _path: Spanned<&[u8]>,
_report: &Report) -> Result<Chunk, Option<Stop>> {
Err(None)
}
}
pub trait FsSource {
fn chunk_from_path(&self, resolved_path: Spanned<&Path>,
report: &Report) -> Result<Option<Chunk>, Option<Stop>>;
fn to_path_buf(&self, path: Spanned<&[u8]>, _report: &Report) -> Result<PathBuf, Option<Stop>> {
if path.is_ascii() {
Ok(Path::new(str::from_utf8(&path).unwrap()).to_owned())
} else {
Err(None)
}
}
}
pub struct FsOptions<S> {
source: S,
root: PathBuf,
package_path: Vec<Vec<u8>>,
package_cpath: Vec<Vec<u8>>,
}
impl<S: FsSource> FsOptions<S> {
pub fn new(source: S, root: PathBuf) -> FsOptions<S> {
FsOptions {
source: source,
root: root,
package_path: vec![b"?.lua".to_vec()],
package_cpath: vec![],
}
}
fn search_file(&self, path: Spanned<&[u8]>, search_paths: &[Vec<u8>], suffix: &[u8],
report: &Report) -> Result<Option<Chunk>, Option<Stop>> {
for template in search_paths {
let mut newpath = Vec::new();
let mut newpathdot = Vec::new();
for (i, e) in template.split(|&b| b == b'?').enumerate() {
if i > 0 {
newpath.extend(path.iter().map(|&b| {
if b == b'.' { MAIN_SEPARATOR as u8 } else { b }
}));
newpathdot.extend(path.iter().cloned());
}
newpath.extend_from_slice(e);
newpathdot.extend_from_slice(e);
}
newpath.extend_from_slice(suffix);
newpathdot.extend_from_slice(suffix);
let newpath = (&newpath[..]).with_loc(path);
let resolved_path = self.root.join(self.source.to_path_buf(newpath, report)?);
let resolved_path = (&*resolved_path).with_loc(path);
trace!("trying to load {:?}", resolved_path);
if let Some(chunk) = self.source.chunk_from_path(resolved_path, report)? {
return Ok(Some(chunk));
}
let newpathdot = (&newpathdot[..]).with_loc(path);
let resolved_path = self.root.join(self.source.to_path_buf(newpathdot, report)?);
let resolved_path = (&*resolved_path).with_loc(path);
trace!("trying to load {:?}", resolved_path);
if let Some(chunk) = self.source.chunk_from_path(resolved_path, report)? {
return Ok(Some(chunk));
}
}
Ok(None)
}
}
impl<S: FsSource> Options for FsOptions<S> {
fn set_package_path(&mut self, path: Spanned<&[u8]>,
_report: &Report) -> Result<(), Option<Stop>> {
self.package_path = path.split(|&b| b == b';').map(|s| s.to_owned()).collect();
Ok(())
}
fn set_package_cpath(&mut self, path: Spanned<&[u8]>,
_report: &Report) -> Result<(), Option<Stop>> {
self.package_cpath = path.split(|&b| b == b';').map(|s| s.to_owned()).collect();
Ok(())
}
fn require_chunk(&mut self, path: Spanned<&[u8]>,
report: &Report) -> Result<Chunk, Option<Stop>> {
if let Some(chunk) = self.search_file(path, &self.package_path, b".kailua", report)? {
return Ok(chunk);
}
if let Some(chunk) = self.search_file(path, &self.package_path, b"", report)? {
return Ok(chunk);
}
if let Some(chunk) = self.search_file(path, &self.package_cpath, b".kailua", report)? {
return Ok(chunk);
}
Err(None)
}
}