1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use rlua::prelude::*;
use crate::bindings::system::LuaMetadata;
use std::{fs, path};
use std::sync::Arc;

pub struct LuaPath(path::PathBuf);

impl LuaUserData for LuaPath {
    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
        methods.add_method("file_stem", |_, this: &LuaPath, _:() |{
            Ok(this.0.file_stem().map(|p| p.to_str().map(|s| s.to_string())))
        });
        methods.add_method("file_name", |_, this: &LuaPath, _:() |{
            Ok(this.0.file_name().map(|p| p.to_str().map(|s| s.to_string())))
        });
        methods.add_method("ext", |_, this: &LuaPath, _:() |{
            Ok(this.0.extension().map(|p| p.to_str().map(|s| s.to_string())))
        });
        methods.add_method("exists", |_, this: &LuaPath, _:() |{
            Ok(this.0.exists())
        });
        methods.add_method("is_dir", |_, this: &LuaPath, _:() |{
            Ok(this.0.is_dir())
        });
        methods.add_method("is_file", |_, this: &LuaPath, _:() |{
            Ok(this.0.is_file())
        });
        methods.add_method("create_file", |_, this: &LuaPath, _: ()| {
            fs::OpenOptions::new()
                .write(true)
                .create(true)
                .open(this.0.as_path())
                .map(|_| ())
                .map_err(LuaError::external)
        });
        methods.add_method("create_dir", |_, this: &LuaPath, opt: Option<bool>| {
            match opt {
                Some(true) => fs::create_dir_all(this.0.as_path()).map_err(LuaError::external),
                _ => fs::create_dir(this.0.as_path()).map_err(LuaError::external)
            }
        });
        methods.add_method("remove", |_, this: &LuaPath, opt: Option<bool>| {
            if this.0.exists() {
                if this.0.is_file() {
                    return fs::remove_file(&this.0).map_err(LuaError::external);
                } else if this.0.is_dir() {
                    return match opt {
                        Some(true) => fs::remove_dir_all(&this.0).map_err(LuaError::external),
                        _ => fs::remove_dir(&this.0).map_err(LuaError::external)
                    };
                }
            }
            Ok(())
        });
        methods.add_method("is_relative", |_, this: &LuaPath, _:() |{
            Ok(this.0.is_relative())
        });
        methods.add_method("is_absolute", |_, this: &LuaPath, _:() |{
            Ok(this.0.is_absolute())
        });
        methods.add_method("has_root", |_, this: &LuaPath, _:() |{
            Ok(this.0.has_root())
        });
        methods.add_method("parent", |_, this: &LuaPath, _:() |{
            Ok(this.0.parent().map(|p| LuaPath(p.to_path_buf())))
        });
        methods.add_method_mut("push", |_, this: &mut LuaPath, val: String |{
            Ok(this.0.push(&val))
        });
        methods.add_method("join", |_, this: &LuaPath, path: String |{
            Ok(LuaPath(this.0.join(path)))
        });
        methods.add_method("metadata", |_, this: &LuaPath, _:() |{
            Ok(LuaMetadata(this.0.metadata().map_err(LuaError::external)?))
        });
        methods.add_method("canonicalize", |_, this: &LuaPath, _:() |{
            fs::canonicalize(&this.0).map(|can| can.to_str().map(|s| s.to_string())).map_err(LuaError::external)
        });
        methods.add_method("read_dir", |lua, this: &LuaPath, _: ()| {
            match this.0.read_dir() {
                Ok(iter) => {
                    let mut arc_iter = Arc::new(Some(iter));
                    let f = move |_, _: ()| {
                        let result = match Arc::get_mut(&mut arc_iter).expect("entries iterator is mutably borrowed") {
                            Some(iter) => iter.next().map(|entry| entry.map(|e| LuaPath(e.path())).ok()),
                            None => None
                        };
                        if result.is_none() { *Arc::get_mut(&mut arc_iter).unwrap() = None; }
                        Ok(result)
                    };
                    Ok(lua.create_function_mut(f)?)
                }, Err(err) => Err(LuaError::external(err))
            }
        });
        methods.add_meta_method(LuaMetaMethod::ToString, |_, this: &LuaPath, _: ()| {
            Ok(this.0.to_str().map(|s| s.to_string()))
        });
    }
}

pub fn init(lua: &Lua) -> crate::Result<()> {
    let module = lua.create_table()?;

    module.set("empty", lua.create_function( |_, _: ()| {
        Ok(LuaPath(path::PathBuf::new()))
    })? )?;

    module.set("new", lua.create_function( |_, path: String| {
        Ok(LuaPath(path::Path::new(&path).to_path_buf()))
    })? )?;

    lua.globals().set("path", module)?;

    Ok(())
}