Builder = {} function Builder:from_template(path) local builder = { template = loadfile( tostring(path), nil, Env.Sandbox { Template = Template } )(), source = path, } if not builder.template or getmetatable(builder.template) ~= Template then error('invalid template') end builder.source_directory = builder.source:parent() setmetatable(builder, self) self.__index = self local argument_template = { strict = true, { name = "target" }, { name = "template", description = "some description", kind = "property", required = true }, }; for key, value in pairs(builder.template.options) do argument_template[#argument_template + 1] = value end local args = require('arg')(argument_template) for key, _ in pairs(builder.template.options) do builder.template.arguments[key] = args[key] end return builder end function Builder:entries() local entries = {} for _, value in ipairs(self.template.files) do entries[#entries + 1] = { path = self.source_directory / value.path, env = value.env, } end for _, value in ipairs(self.template.directories) do for entry in (self.source_directory / value.path):children() do entries[#entries + 1] = { path = entry, env = value.env, } end end local i = 1 return function() local value = entries[i] i = i + 1 return value end end function Builder:build(args) local instance = Instance:new() for entry in self:entries() do instance:register( entry.path:relative_to_parent(self.source_directory), not entry.path:is_directory() and self:process_file(entry) or nil ) end return instance end function Builder:process_file(file) if not file.path:exists() then error(tostring(file.path) .. ' does not exist.') end local f = file.path:open("r") local raw = f:read("*all") f:close() local content = '' for _, split in ipairs(raw:split_into_template_blocks()) do if split.kind == 'text' then content = content .. split.content else local func, err = load( 'return ' .. split.content, 'macro ' .. tostring(file.path), 't', Env.Sandbox(file.env) ) if err then error(err) end local value = func() if type(value) == 'function' then content = content .. tostring(value()) else content = content .. tostring(value) end end end return content end