nf7/common/luajit_nfile_importer.hh

107 lines
2.8 KiB
C++

#pragma once
#include <filesystem>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <string_view>
#include <lua.hpp>
#include "nf7.hh"
#include "common/future.hh"
#include "common/generic_context.hh"
#include "common/luajit_ref.hh"
#include "common/luajit_thread.hh"
namespace nf7::luajit {
class NFileImporter :
public nf7::luajit::Thread::Importer,
public std::enable_shared_from_this<NFileImporter> {
public:
NFileImporter(const std::filesystem::path& base) noexcept : base_(base) {
}
nf7::Future<std::shared_ptr<luajit::Ref>> Import(
const std::shared_ptr<luajit::Thread>& th, std::string_view name) noexcept {
auto self = shared_from_this();
const auto path = base_ / std::string {name};
auto ljq = th->ljq();
auto ctx = std::make_shared<
nf7::GenericContext>(th->env(), th->initiator(), "imported LuaJIT script", th);
nf7::Future<std::shared_ptr<luajit::Ref>>::Promise pro {ctx};
// create new thread
auto handler = luajit::Thread::CreatePromiseHandler<std::shared_ptr<luajit::Ref>>(
pro, [self, this, path, ljq, ctx](auto L) {
if (lua_gettop(L) <= 1) {
AddImport(path);
return std::make_shared<nf7::luajit::Ref>(ctx, ljq, L);
} else {
throw nf7::Exception {"imported script can return 1 or less results"};
}
});
auto th_sub = std::make_shared<
nf7::luajit::Thread>(ctx, ljq, std::move(handler));
th_sub->Install(*th);
// install new importer for sub thread
auto dir = path;
dir.remove_filename();
th_sub->Install(std::make_shared<NFileImporter>(dir));
// start the thread
ljq->Push(ctx, [pro, path, th_sub](auto L) mutable {
L = th_sub->Init(L);
if (0 == luaL_loadfile(L, path.string().c_str())) {
th_sub->Resume(L, 0);
} else {
pro.Throw<nf7::Exception>(std::string {"import failed: "}+lua_tostring(L, -1));
}
});
return pro.future();
}
void ClearImports() noexcept {
std::unique_lock<std::mutex> _ {mtx_};
imports_.clear();
}
std::filesystem::file_time_type GetLatestMod() const noexcept {
std::unique_lock<std::mutex> _ {mtx_};
std::filesystem::file_time_type ret = {};
for (const auto& p : imports_) {
try {
ret = std::max(ret, std::filesystem::last_write_time(p));
} catch (std::filesystem::filesystem_error&) {
}
}
return ret;
}
private:
const std::filesystem::path base_;
mutable std::mutex mtx_;
std::vector<std::string> imports_;
void AddImport(const std::filesystem::path& p) noexcept {
auto str = p.string();
std::unique_lock<std::mutex> _ {mtx_};
if (imports_.end() == std::find(imports_.begin(), imports_.end(), str)) {
imports_.emplace_back(std::move(str));
}
}
};
} // namespace nf7::luajit