diff --git a/lua/README.md b/lua/README.md index 3f4003f..7b70a07 100644 --- a/lua/README.md +++ b/lua/README.md @@ -43,6 +43,12 @@ Use `luarocks` to build the library and install it for your current Lua version: luarocks --local make ``` +If you need to re-generate the protobuf spec files, run: + +```bash +luajit hack/generate_pb.lua +``` + ## Usage ### Basic Usage @@ -50,22 +56,27 @@ luarocks --local make The KCL Lua library provides two main functions for working with KCL configurations: ```lua -local kcl = require("kcl_lib") +local api = require("kcl_lib.api") -- Execute a single KCL file -local result = assert(kcl.run("./config/schema.k")) -print("Configuration result:", result.yaml_result) +local result = api:run("./config/schema.k") +print("Configuration result:", result:yaml()) -- Execute multiple KCL files -local result = assert(kcl.run({ +local result = api:run({ "./config/schema.k", "./config/data.k" -})) -print("Combined configuration:", result.yaml_result) +}) +print("Combined configuration:", result:json()) + +-- Using the raw API to the native service +local raw_api = require("kcl_lib.raw_api") --- Format a KCL file -local formatted_files = assert(kcl.format("./config/unordered.k")) -print("Formatted files:", table.concat(formatted_files, ", ")) +-- Perform a call to a native service function +local result = raw_api:exec_program({ + k_filename_list = { "./config/schema.k" }, +}) +print("Configuration result", result.yaml_result) ``` ## Development diff --git a/lua/hack/generate_pb.lua b/lua/hack/generate_pb.lua new file mode 100644 index 0000000..f39b638 --- /dev/null +++ b/lua/hack/generate_pb.lua @@ -0,0 +1,18 @@ +local io = require("io") + +local protoc = require("protoc") + +local function main() + local p = protoc.new() + p.proto3_optional = true + local fh = assert(io.open("../spec/spec.proto", "r")) + local content = fh:read("*a") + fh:close() + local dump = p:compile(content, "spec.proto") + fh = assert(io.open("./kcl_lib/schema.lua", "w")) + assert(fh:write("return ")) + assert(fh:write(string.format("%q", dump))) + fh:close() +end + +main() diff --git a/lua/kcl_lib-0.12.3-1.rockspec b/lua/kcl_lib-0.12.3-1.rockspec index f9f00ab..1f08a06 100644 --- a/lua/kcl_lib-0.12.3-1.rockspec +++ b/lua/kcl_lib-0.12.3-1.rockspec @@ -19,6 +19,8 @@ description = { dependencies = { "lua >= 5.1", "luarocks-build-rust-mlua = 0.2.0", + "lua-protobuf >= 0.5", + "dkjson >= 2.9", } test_dependencies = { "busted >= 2.2", @@ -32,4 +34,10 @@ build = { ["kcl_lib"] = "kcl_lib_lua", }, target_path = "target", + include = { + "api.lua", + "raw_api.lua", + "types.lua", + "schema.lua", + }, } diff --git a/lua/kcl_lib/api.lua b/lua/kcl_lib/api.lua new file mode 100644 index 0000000..569bea8 --- /dev/null +++ b/lua/kcl_lib/api.lua @@ -0,0 +1,34 @@ +local api = require("kcl_lib.raw_api") +local types = require("kcl_lib.types") + +---@class kcl_lib.API +---@field private raw kcl_lib.RawAPI The object to access the raw API. +local API = {} + +---Create a new API object. +---@return kcl_lib.RawAPI +function API:new() + local o = { + raw = api, + } + setmetatable(o, self) + self.__index = self + o.overrides = {} + return o +end + +---Run a KCL program or set of programs and its output. +---@param file string|string[] The file or list of files to run. +---@return kcl_lib.types.RunResponse +function API:run(file) + if type(file) == "string" then + file = { file } + end + local args = { + k_filename_list = file, + } + local res = self.raw:exec_program(args) + return types.RunResponse:from_exec_program_result(res) +end + +return API:new() diff --git a/lua/kcl_lib/raw_api.lua b/lua/kcl_lib/raw_api.lua new file mode 100644 index 0000000..1703958 --- /dev/null +++ b/lua/kcl_lib/raw_api.lua @@ -0,0 +1,53 @@ +local kcl_lib = require("kcl_lib") +local schema = require("kcl_lib.schema") + +---@class kcl_lib.RawAPI +local RawAPI = {} + +---Create a new API object. +---@return kcl_lib.RawAPI +function RawAPI:new() + local pb = require("pb") + pb.clear() + assert(pb.load(schema)) + local o = { + pb = pb, + client = assert(kcl_lib.new_client(), "failed to create native KCL client"), + } + setmetatable(o, self) + self.__index = self + o.overrides = {} + return o +end + +---Add a method to the raw API. +---@param name string The KCL service function name to call. +---@param arg_name string The name of the argument type that the method accepts. +---@param return_name string The name of the return type that the method returns. +---@return function +local function add_method(name, arg_name, return_name) + return function(self, args) + local arg_type = ".com.kcl.api." .. arg_name + args = assert( + self.pb.encode(arg_type, args), + "failed to encode argument into " .. arg_type + ) + local res = assert( + self.client:call(name, args), + "failed to perform native call for method " .. name + ) + local return_type = ".com.kcl.api." .. return_name + return assert( + self.pb.decode(return_type, res), + "failed to decode buffer into " .. return_type + ) + end +end + +RawAPI.exec_program = + add_method("KclService.ExecProgram", "ExecProgramArgs", "ExecProgramResult") + +RawAPI.format_path = + add_method("KclService.FormatPath", "FormatPathArgs", "FormatPathResult") + +return RawAPI:new() diff --git a/lua/kcl_lib/schema.lua b/lua/kcl_lib/schema.lua new file mode 100644 index 0000000..7038f23 --- /dev/null +++ b/lua/kcl_lib/schema.lua @@ -0,0 +1,382 @@ +return "\ +N2\1\0186\26\23.com.kcl.api.PingResult\ +\4Ping\18\21.com.kcl.api.PingArgs\18H\26\29.com.kcl.api.ListMethodResult\ +\ +ListMethod\18\27.com.kcl.api.ListMethodArgs\ +\14BuiltinService2\13\0186\26\23.com.kcl.api.PingResult\ +\4Ping\18\21.com.kcl.api.PingArgs\18H\26\29.com.kcl.api.GetVersionResult\ +\ +GetVersion\18\27.com.kcl.api.GetVersionArgs\18N\26\31.com.kcl.api.ParseProgramResult\ +\12ParseProgram\18\29.com.kcl.api.ParseProgramArgs\18E\26\28.com.kcl.api.ParseFileResult\ +\9ParseFile\18\26.com.kcl.api.ParseFileArgs\18K\26\30.com.kcl.api.LoadPackageResult\ +\11LoadPackage\18\28.com.kcl.api.LoadPackageArgs\18L\26\30.com.kcl.api.ListOptionsResult\ +\11ListOptions\18\29.com.kcl.api.ParseProgramArgs\18Q\26 .com.kcl.api.ListVariablesResult\ +\13ListVariables\18\30.com.kcl.api.ListVariablesArgs\18K\26\30.com.kcl.api.ExecProgramResult\ +\11ExecProgram\18\28.com.kcl.api.ExecProgramArgs\18N\26\31.com.kcl.api.BuildProgramResult\ +\12BuildProgram\18\29.com.kcl.api.BuildProgramArgs\18M\26\30.com.kcl.api.ExecProgramResult\ +\12ExecArtifact\18\29.com.kcl.api.ExecArtifactArgs\18N\26\31.com.kcl.api.OverrideFileResult\ +\12OverrideFile\18\29.com.kcl.api.OverrideFileArgs\18f\26'.com.kcl.api.GetSchemaTypeMappingResult\ +\20GetSchemaTypeMapping\18%.com.kcl.api.GetSchemaTypeMappingArgs\18H\26\29.com.kcl.api.FormatCodeResult\ +\ +FormatCode\18\27.com.kcl.api.FormatCodeArgs\18H\26\29.com.kcl.api.FormatPathResult\ +\ +FormatPath\18\27.com.kcl.api.FormatPathArgs\18B\26\27.com.kcl.api.LintPathResult\ +\8LintPath\18\25.com.kcl.api.LintPathArgs\18N\26\31.com.kcl.api.ValidateCodeResult\ +\12ValidateCode\18\29.com.kcl.api.ValidateCodeArgs\18N\26\31.com.kcl.api.ListDepFilesResult\ +\12ListDepFiles\18\29.com.kcl.api.ListDepFilesArgs\18]\26$.com.kcl.api.LoadSettingsFilesResult\ +\17LoadSettingsFiles\18\".com.kcl.api.LoadSettingsFilesArgs\18<\26\25.com.kcl.api.RenameResult\ +\6Rename\18\23.com.kcl.api.RenameArgs\18H\26\29.com.kcl.api.RenameCodeResult\ +\ +RenameCode\18\27.com.kcl.api.RenameCodeArgs\0186\26\23.com.kcl.api.TestResult\ +\4Test\18\21.com.kcl.api.TestArgs\18`\26%.com.kcl.api.UpdateDependenciesResult\ +\18UpdateDependencies\18#.com.kcl.api.UpdateDependenciesArgs\ +\ +KclServiceb\6proto3\ +\ +spec.proto\18\11com.kcl.apiB\20Z\5.;api\2\ +KclLib.API\"1\18\16\24\1(\9 \1\ +\8pkg_name\18\16\24\2(\9 \1\ +\8pkg_path\ +\11ExternalPkg\"'\18\12\24\1(\9 \1\ +\4name\18\13\24\2(\9 \1\ +\5value\ +\8Argument\"L\18\13\24\1(\9 \1\ +\5level\18\12\24\2(\9 \1\ +\4code\18&\24\3(\11 \3\ +\8messages2\20.com.kcl.api.Message\ +\5Error\":\18\11\24\1(\9 \1\ +\3msg\18\"\24\2(\11 \1\ +\3pos2\21.com.kcl.api.Position\ +\7Message\"\25\18\13\24\1(\9 \1\ +\5value\ +\8PingArgs\"\27\18\13\24\1(\9 \1\ +\5value\ +\ +PingResult\"\16\ +\14GetVersionArgs\"\\\18\15\24\1(\9 \1\ +\7version\18\16\24\2(\9 \1\ +\8checksum\18\15\24\3(\9 \1\ +\7git_sha\18\20\24\4(\9 \1\ +\12version_info\ +\16GetVersionResult\"\16\ +\14ListMethodArgs\",\18\24\24\1(\9 \3\ +\16method_name_list\ +\16ListMethodResult\"^\18\12\24\1(\9 \1\ +\4path\18\14\24\2(\9 \1\ +\6source\18/\24\3(\11 \3\ +\13external_pkgs2\24.com.kcl.api.ExternalPkg\ +\13ParseFileArgs\"U\18\16\24\1(\9 \1\ +\8ast_json\18\12\24\2(\9 \3\ +\4deps\18\"\24\3(\11 \3\ +\6errors2\18.com.kcl.api.Error\ +\15ParseFileResult\"c\18\13\24\1(\9 \3\ +\5paths\18\15\24\2(\9 \3\ +\7sources\18/\24\3(\11 \3\ +\13external_pkgs2\24.com.kcl.api.ExternalPkg\ +\16ParseProgramArgs\"Y\18\16\24\1(\9 \1\ +\8ast_json\18\13\24\2(\9 \3\ +\5paths\18\"\24\3(\11 \3\ +\6errors2\18.com.kcl.api.Error\ +\18ParseProgramResult\"\1\0181\24\1(\11 \1\ +\ +parse_args2\29.com.kcl.api.ParseProgramArgs\18\19\24\2(\8 \1\ +\11resolve_ast\18\20\24\3(\8 \1\ +\12load_builtin\18\22\24\4(\8 \1\ +\14with_ast_index\ +\15LoadPackageArgs\"\7\26A\ +\11ScopesEntry\18\11\24\1(\9 \1\ +\3key\18!\24\2(\11 \1\ +\5value2\18.com.kcl.api.Scope:\0028\1\26C\ +\12SymbolsEntry\18\11\24\1(\9 \1\ +\3key\18\"\24\2(\11 \1\ +\5value2\19.com.kcl.api.Symbol:\0028\1\26N\ +\18NodeSymbolMapEntry\18\11\24\1(\9 \1\ +\3key\18'\24\2(\11 \1\ +\5value2\24.com.kcl.api.SymbolIndex:\0028\1\0264\ +\18SymbolNodeMapEntry\18\11\24\1(\9 \1\ +\3key\18\13\24\2(\9 \1\ +\5value:\0028\1\26V\ +\26FullyQualifiedNameMapEntry\18\11\24\1(\9 \1\ +\3key\18'\24\2(\11 \1\ +\5value2\24.com.kcl.api.SymbolIndex:\0028\1\26K\ +\16PkgScopeMapEntry\18\11\24\1(\9 \1\ +\3key\18&\24\2(\11 \1\ +\5value2\23.com.kcl.api.ScopeIndex:\0028\1\ +\17LoadPackageResult\18\15\24\1(\9 \1\ +\7program\18\13\24\2(\9 \3\ +\5paths\18(\24\3(\11 \3\ +\12parse_errors2\18.com.kcl.api.Error\18'\24\4(\11 \3\ +\11type_errors2\18.com.kcl.api.Error\18:\24\5(\11 \3\ +\6scopes2*.com.kcl.api.LoadPackageResult.ScopesEntry\18<\24\6(\11 \3\ +\7symbols2+.com.kcl.api.LoadPackageResult.SymbolsEntry\18J\24\7(\11 \3\ +\15node_symbol_map21.com.kcl.api.LoadPackageResult.NodeSymbolMapEntry\18J\24\8(\11 \3\ +\15symbol_node_map21.com.kcl.api.LoadPackageResult.SymbolNodeMapEntry\18[\24\9(\11 \3\ +\24fully_qualified_name_map29.com.kcl.api.LoadPackageResult.FullyQualifiedNameMapEntry\18F\24\ +(\11 \3\ +\13pkg_scope_map2/.com.kcl.api.LoadPackageResult.PkgScopeMapEntry\"=\18(\24\2(\11 \3\ +\7options2\23.com.kcl.api.OptionHelp\ +\17ListOptionsResult\"_\18\12\24\1(\9 \1\ +\4name\18\12\24\2(\9 \1\ +\4type\18\16\24\3(\8 \1\ +\8required\18\21\24\4(\9 \1\ +\13default_value\18\12\24\5(\9 \1\ +\4help\ +\ +OptionHelp\"\1\18 \24\1(\11 \1\ +\2ty2\20.com.kcl.api.KclType\18\12\24\2(\9 \1\ +\4name\18'\24\3(\11 \1\ +\5owner2\24.com.kcl.api.SymbolIndex\18%\24\4(\11 \1\ +\3def2\24.com.kcl.api.SymbolIndex\18'\24\5(\11 \3\ +\5attrs2\24.com.kcl.api.SymbolIndex\18\17\24\6(\8 \1\ +\9is_global\ +\6Symbol\"\1\18\12\24\1(\9 \1\ +\4kind\18'\24\2(\11 \1\ +\6parent2\23.com.kcl.api.ScopeIndex\18'\24\3(\11 \1\ +\5owner2\24.com.kcl.api.SymbolIndex\18)\24\4(\11 \3\ +\8children2\23.com.kcl.api.ScopeIndex\18&\24\5(\11 \3\ +\4defs2\24.com.kcl.api.SymbolIndex\ +\5Scope\"1\18\9\24\1(\4 \1\ +\1i\18\9\24\2(\4 \1\ +\1g\18\12\24\3(\9 \1\ +\4kind\ +\11SymbolIndex\"0\18\9\24\1(\4 \1\ +\1i\18\9\24\2(\4 \1\ +\1g\18\12\24\3(\9 \1\ +\4kind\ +\ +ScopeIndex\"\3\18\16\24\1(\9 \1\ +\8work_dir\18\23\24\2(\9 \3\ +\15k_filename_list\18\19\24\3(\9 \3\ +\11k_code_list\18#\24\4(\11 \3\ +\4args2\21.com.kcl.api.Argument\18\17\24\5(\9 \3\ +\9overrides\18\27\24\6(\8 \1\ +\19disable_yaml_result\18\26\24\7(\8 \1\ +\18print_override_ast\18\26\24\8(\8 \1\ +\18strict_range_check\18\20\24\9(\8 \1\ +\12disable_none\18\15\24\ +(\5 \1\ +\7verbose\18\13\24\11(\5 \1\ +\5debug\18\17\24\12(\8 \1\ +\9sort_keys\18/\24\13(\11 \3\ +\13external_pkgs2\24.com.kcl.api.ExternalPkg\18 \24\14(\8 \1\ +\24include_schema_type_path\18\20\24\15(\8 \1\ +\12compile_only\18\19\24\16(\8 \1\ +\11show_hidden\18\21\24\17(\9 \3\ +\13path_selector\18\17\24\18(\8 \1\ +\9fast_eval\ +\15ExecProgramArgs\"g\18\19\24\1(\9 \1\ +\11json_result\18\19\24\2(\9 \1\ +\11yaml_result\18\19\24\3(\9 \1\ +\11log_message\18\19\24\4(\9 \1\ +\11err_message\ +\17ExecProgramResult\"S\18/\24\1(\11 \1\ +\9exec_args2\28.com.kcl.api.ExecProgramArgs\18\14\24\2(\9 \1\ +\6output\ +\16BuildProgramArgs\"\"\18\12\24\1(\9 \1\ +\4path\ +\18BuildProgramResult\"Q\18\12\24\1(\9 \1\ +\4path\18/\24\2(\11 \1\ +\9exec_args2\28.com.kcl.api.ExecProgramArgs\ +\16ExecArtifactArgs\" \18\14\24\1(\9 \1\ +\6source\ +\14FormatCodeArgs\"%\18\17\24\1(\12 \1\ +\9formatted\ +\16FormatCodeResult\"\30\18\12\24\1(\9 \1\ +\4path\ +\14FormatPathArgs\")\18\21\24\1(\9 \3\ +\13changed_paths\ +\16FormatPathResult\"\29\18\13\24\1(\9 \3\ +\5paths\ +\12LintPathArgs\"!\18\15\24\1(\9 \3\ +\7results\ +\14LintPathResult\"E\18\12\24\1(\9 \1\ +\4file\18\13\24\2(\9 \3\ +\5specs\18\20\24\3(\9 \3\ +\12import_paths\ +\16OverrideFileArgs\"N\18\14\24\1(\8 \1\ +\6result\18(\24\2(\11 \3\ +\12parse_errors2\18.com.kcl.api.Error\ +\18OverrideFileResult\"-\18\21\24\1(\8 \1\ +\13merge_program\ +\20ListVariablesOptions\"8\18(\24\1(\11 \3\ +\9variables2\21.com.kcl.api.Variable\ +\12VariableList\"e\18\13\24\1(\9 \3\ +\5files\18\13\24\2(\9 \3\ +\5specs\0182\24\3(\11 \1\ +\7options2!.com.kcl.api.ListVariablesOptions\ +\17ListVariablesArgs\"\1\26K\ +\14VariablesEntry\18\11\24\1(\9 \1\ +\3key\18(\24\2(\11 \1\ +\5value2\25.com.kcl.api.VariableList:\0028\1\ +\19ListVariablesResult\18B\24\1(\11 \3\ +\9variables2/.com.kcl.api.ListVariablesResult.VariablesEntry\18\25\24\2(\9 \3\ +\17unsupported_codes\18(\24\3(\11 \3\ +\12parse_errors2\18.com.kcl.api.Error\"\1\18\13\24\1(\9 \1\ +\5value\18\17\24\2(\9 \1\ +\9type_name\18\14\24\3(\9 \1\ +\6op_sym\18)\24\4(\11 \3\ +\ +list_items2\21.com.kcl.api.Variable\18+\24\5(\11 \3\ +\12dict_entries2\21.com.kcl.api.MapEntry\ +\8Variable\"=\18\11\24\1(\9 \1\ +\3key\18$\24\2(\11 \1\ +\5value2\21.com.kcl.api.Variable\ +\8MapEntry\"`\18/\24\1(\11 \1\ +\9exec_args2\28.com.kcl.api.ExecProgramArgs\18\19\24\2(\9 \1\ +\11schema_name\ +\24GetSchemaTypeMappingArgs\"\1\26N\ +\22SchemaTypeMappingEntry\18\11\24\1(\9 \1\ +\3key\18#\24\2(\11 \1\ +\5value2\20.com.kcl.api.KclType:\0028\1\ +\26GetSchemaTypeMappingResult\18[\24\1(\11 \3\ +\19schema_type_mapping2>.com.kcl.api.GetSchemaTypeMappingResult.SchemaTypeMappingEntry\"\1\26R\ +\22SchemaTypeMappingEntry\18\11\24\1(\9 \1\ +\3key\18'\24\2(\11 \1\ +\5value2\24.com.kcl.api.SchemaTypes:\0028\1\ +#GetSchemaTypeMappingUnderPathResult\18d\24\1(\11 \3\ +\19schema_type_mapping2G.com.kcl.api.GetSchemaTypeMappingUnderPathResult.SchemaTypeMappingEntry\"8\18)\24\1(\11 \3\ +\11schema_type2\20.com.kcl.api.KclType\ +\11SchemaTypes\"\1\18\16\24\1(\9 \1\ +\8datafile\18\12\24\2(\9 \1\ +\4data\18\12\24\3(\9 \1\ +\4file\18\12\24\4(\9 \1\ +\4code\18\14\24\5(\9 \1\ +\6schema\18\22\24\6(\9 \1\ +\14attribute_name\18\14\24\7(\9 \1\ +\6format\18/\24\8(\11 \3\ +\13external_pkgs2\24.com.kcl.api.ExternalPkg\ +\16ValidateCodeArgs\":\18\15\24\1(\8 \1\ +\7success\18\19\24\2(\9 \1\ +\11err_message\ +\18ValidateCodeResult\":\18\12\24\1(\3 \1\ +\4line\18\14\24\2(\3 \1\ +\6column\18\16\24\3(\9 \1\ +\8filename\ +\8Position\"h\18\16\24\1(\9 \1\ +\8work_dir\18\20\24\2(\8 \1\ +\12use_abs_path\18\19\24\3(\8 \1\ +\11include_all\18\23\24\4(\8 \1\ +\15use_fast_parser\ +\16ListDepFilesArgs\"E\18\15\24\1(\9 \1\ +\7pkgroot\18\15\24\2(\9 \1\ +\7pkgpath\18\13\24\3(\9 \3\ +\5files\ +\18ListDepFilesResult\"8\18\16\24\1(\9 \1\ +\8work_dir\18\13\24\2(\9 \3\ +\5files\ +\21LoadSettingsFilesArgs\"z\18/\24\1(\11 \1\ +\15kcl_cli_configs2\22.com.kcl.api.CliConfig\18.\24\2(\11 \3\ +\11kcl_options2\25.com.kcl.api.KeyValuePair\ +\23LoadSettingsFilesResult\"\2\18\13\24\1(\9 \3\ +\5files\18\14\24\2(\9 \1\ +\6output\18\17\24\3(\9 \3\ +\9overrides\18\21\24\4(\9 \3\ +\13path_selector\18\26\24\5(\8 \1\ +\18strict_range_check\18\20\24\6(\8 \1\ +\12disable_none\18\15\24\7(\3 \1\ +\7verbose\18\13\24\8(\8 \1\ +\5debug\18\17\24\9(\8 \1\ +\9sort_keys\18\19\24\ +(\8 \1\ +\11show_hidden\18 \24\11(\8 \1\ +\24include_schema_type_path\18\17\24\12(\8 \1\ +\9fast_eval\ +\9CliConfig\"*\18\11\24\1(\9 \1\ +\3key\18\13\24\2(\9 \1\ +\5value\ +\12KeyValuePair\"]\18\20\24\1(\9 \1\ +\12package_root\18\19\24\2(\9 \1\ +\11symbol_path\18\18\24\3(\9 \3\ +\ +file_paths\18\16\24\4(\9 \1\ +\8new_name\ +\ +RenameArgs\"%\18\21\24\1(\9 \3\ +\13changed_files\ +\12RenameResult\"\1\0262\ +\16SourceCodesEntry\18\11\24\1(\9 \1\ +\3key\18\13\24\2(\9 \1\ +\5value:\0028\1\ +\14RenameCodeArgs\18\20\24\1(\9 \1\ +\12package_root\18\19\24\2(\9 \1\ +\11symbol_path\18B\24\3(\11 \3\ +\12source_codes2,.com.kcl.api.RenameCodeArgs.SourceCodesEntry\18\16\24\4(\9 \1\ +\8new_name\"\1\0263\ +\17ChangedCodesEntry\18\11\24\1(\9 \1\ +\3key\18\13\24\2(\9 \1\ +\5value:\0028\1\ +\16RenameCodeResult\18F\24\1(\11 \3\ +\13changed_codes2/.com.kcl.api.RenameCodeResult.ChangedCodesEntry\"t\18/\24\1(\11 \1\ +\9exec_args2\28.com.kcl.api.ExecProgramArgs\18\16\24\2(\9 \3\ +\8pkg_list\18\18\24\3(\9 \1\ +\ +run_regexp\18\17\24\4(\8 \1\ +\9fail_fast\ +\8TestArgs\"5\18'\24\2(\11 \3\ +\4info2\25.com.kcl.api.TestCaseInfo\ +\ +TestResult\"R\18\12\24\1(\9 \1\ +\4name\18\13\24\2(\9 \1\ +\5error\18\16\24\3(\4 \1\ +\8duration\18\19\24\4(\9 \1\ +\11log_message\ +\12TestCaseInfo\"?\18\21\24\1(\9 \1\ +\13manifest_path\18\14\24\2(\8 \1\ +\6vendor\ +\22UpdateDependenciesArgs\"K\18/\24\3(\11 \3\ +\13external_pkgs2\24.com.kcl.api.ExternalPkg\ +\24UpdateDependenciesResult\"\5\26G\ +\15PropertiesEntry\18\11\24\1(\9 \1\ +\3key\18#\24\2(\11 \1\ +\5value2\20.com.kcl.api.KclType:\0028\1\26E\ +\13ExamplesEntry\18\11\24\1(\9 \1\ +\3key\18#\24\2(\11 \1\ +\5value2\20.com.kcl.api.Example:\0028\1\ +\7KclType\18\12\24\1(\9 \1\ +\4type\18)\24\2(\11 \3\ +\11union_types2\20.com.kcl.api.KclType\18\15\24\3(\9 \1\ +\7default\18\19\24\4(\9 \1\ +\11schema_name\18\18\24\5(\9 \1\ +\ +schema_doc\0188\24\6(\11 \3\ +\ +properties2$.com.kcl.api.KclType.PropertiesEntry\18\16\24\7(\9 \3\ +\8required\18!\24\8(\11 \1\ +\3key2\20.com.kcl.api.KclType\18\"\24\9(\11 \1\ +\4item2\20.com.kcl.api.KclType\18\12\24\ +(\5 \1\ +\4line\18*\24\11(\11 \3\ +\ +decorators2\22.com.kcl.api.Decorator\18\16\24\12(\9 \1\ +\8filename\18\16\24\13(\9 \1\ +\8pkg_path\18\19\24\14(\9 \1\ +\11description\0184\24\15(\11 \3\ +\8examples2\".com.kcl.api.KclType.ExamplesEntry\18)\24\16(\11 \1\ +\11base_schema2\20.com.kcl.api.KclType\18-\24\17H\0 \1\ +\8function(\0112\25.com.kcl.api.FunctionType\0186\24\18H\1 \1\ +\15index_signature(\0112\27.com.kcl.api.IndexSignatureB\11\ +\9_functionB\18\ +\16_index_signature\"_\18&\24\1(\11 \3\ +\6params2\22.com.kcl.api.Parameter\18'\24\2(\11 \1\ +\9return_ty2\20.com.kcl.api.KclType\ +\12FunctionType\";\18\12\24\1(\9 \1\ +\4name\18 \24\2(\11 \1\ +\2ty2\20.com.kcl.api.KclType\ +\9Parameter\"\1\ +\14IndexSignature\18\18\24\1(\9 \1\ +\8key_nameH\0\18!\24\2(\11 \1\ +\3key2\20.com.kcl.api.KclType\18!\24\3(\11 \1\ +\3val2\20.com.kcl.api.KclType\18\17\24\4(\8 \1\ +\9any_otherB\11\ +\9_key_name\"\1\26/\ +\13KeywordsEntry\18\11\24\1(\9 \1\ +\3key\18\13\24\2(\9 \1\ +\5value:\0028\1\ +\9Decorator\18\12\24\1(\9 \1\ +\4name\18\17\24\2(\9 \3\ +\9arguments\0186\24\3(\11 \3\ +\8keywords2$.com.kcl.api.Decorator.KeywordsEntry\">\18\15\24\1(\9 \1\ +\7summary\18\19\24\2(\9 \1\ +\11description\18\13\24\3(\9 \1\ +\5value\ +\7Example" \ No newline at end of file diff --git a/lua/kcl_lib/types.lua b/lua/kcl_lib/types.lua new file mode 100644 index 0000000..dae30f1 --- /dev/null +++ b/lua/kcl_lib/types.lua @@ -0,0 +1,42 @@ +local json = require("dkjson") + +local M = {} + +---@class kcl_lib.types.RunResponse +---@field private inner table The inner ExecProgramResult object this is wrapping. +local RunResponse = {} + +---Create a RunResponse from an ExecProgramResult. +---@param result table The ExecProgramResult to use as the source. +---@return kcl_lib.types.RunResponse +function RunResponse:from_exec_program_result(result) + local o = { + inner = result, + } + setmetatable(o, self) + self.__index = self + o.overrides = {} + return o +end + +---Return the YAML output from the program execution as a string. +---@return string +function RunResponse:yaml() + return self.inner.yaml_result +end + +---Return the JSON output from the program execution as a string. +---@return string +function RunResponse:json() + return self.inner.json_result +end + +---Return the program execution output as a Lua object. +---@return table +function RunResponse:object() + return json.decode(self.inner.json_result) +end + +M.RunResponse = RunResponse + +return M diff --git a/lua/spec/kcl_lib_api_spec.lua b/lua/spec/kcl_lib_api_spec.lua new file mode 100644 index 0000000..13d9be4 --- /dev/null +++ b/lua/spec/kcl_lib_api_spec.lua @@ -0,0 +1,20 @@ +local api = require("kcl_lib.api") + +describe("kcl_lib.api", function() + describe("run", function() + it("takes a file to run and returns its output", function() + local expected = [[app: + replicas: 2]] + local result = api:run("./spec/test_data/schema.k") + assert.are.equal(expected, result:yaml()) + end) + + it("can take a list of files to run", function() + local result = + api:run({ "./spec/test_data/schema.k", "./spec/test_data/data.k" }) + local tbl = result:object() + assert.are.equal(2, tbl.app.replicas) + assert.are.equal(4, tbl.app2.replicas) + end) + end) +end) diff --git a/lua/spec/kcl_lib_raw_api_spec.lua b/lua/spec/kcl_lib_raw_api_spec.lua new file mode 100644 index 0000000..31f7331 --- /dev/null +++ b/lua/spec/kcl_lib_raw_api_spec.lua @@ -0,0 +1,60 @@ +local io = require("io") +local os = require("os") + +local api = require("kcl_lib.raw_api") + +describe("kcl_lib.raw_api", function() + describe("exec_program", function() + it("can call the native function", function() + local expected = [[app: + replicas: 2 +app2: + replicas: 4]] + local args = { + k_filename_list = { + "./spec/test_data/schema.k", + "./spec/test_data/data.k", + }, + } + local result = assert(api:exec_program(args)) + assert.are.equal(expected, result.yaml_result) + end) + end) + + describe("format_path", function() + it("can call the native function", function() + local unformated_file = "/tmp/unformated.k" + local unformated_content = [[ +schema AppConfig: + replicas: int + +app: AppConfig { + replicas: 2 +}]] + local expected_content = [[schema AppConfig: + replicas: int + +app: AppConfig { + replicas: 2 +} +]] + local file = assert( + io.open(unformated_file, "w"), + "failed to open test file for formatting" + ) + file:write(unformated_content) + file:close() + local args = { path = unformated_file } + local result = assert(api:format_path(args)) + assert.are.same({ unformated_file }, result.changed_paths) + file = assert( + io.open(unformated_file, "r"), + "failed to open formatted file for reading" + ) + local data = file:read("*a") + file:close() + assert.are.equal(expected_content, data) + assert(os.remove(unformated_file)) + end) + end) +end) diff --git a/lua/spec/kcl_lib_spec.lua b/lua/spec/kcl_lib_spec.lua index 59b11eb..de40f39 100644 --- a/lua/spec/kcl_lib_spec.lua +++ b/lua/spec/kcl_lib_spec.lua @@ -1,85 +1,9 @@ -local io = require("io") -local os = require("os") - local kcl_lib = require("kcl_lib") describe("kcl_lib", function() - describe("run", function() - it("should take a single path, run it, and return the result", function() - local expected = [[app: - replicas: 2]] - local result = assert(kcl_lib.run("./spec/test_data/schema.k")) - assert.are.equal(expected, result.yaml_result) + describe("new_client", function() + it("creates a new client", function() + assert(kcl_lib.new_client()) end) - - it( - "should take an array of paths, run them, and return the result", - function() - local expected = [[app: - replicas: 2 -app2: - replicas: 4]] - local result = assert(kcl_lib.run({ - "./spec/test_data/schema.k", - "./spec/test_data/data.k", - })) - assert.are.equal(expected, result.yaml_result) - end - ) - end) - - describe("format", function() - local unformated_file = "/tmp/unformated.k" - local unformated_content = [[ -schema AppConfig: - replicas: int - -app: AppConfig { - replicas: 2 -}]] - local expected_content = [[schema AppConfig: - replicas: int - -app: AppConfig { - replicas: 2 -} -]] - - it( - "should take a single path to unformated code, properly format it, and the path", - function() - local file = assert( - io.open(unformated_file, "w"), - "failed to open test file for formatting" - ) - file:write(unformated_content) - file:close() - local result = assert(kcl_lib.format(unformated_file)) - assert.are.same({ unformated_file }, result) - file = assert( - io.open(unformated_file, "r"), - "failed to open formatted file for reading" - ) - local data = file:read("*a") - file:close() - assert.are.equal(expected_content, data) - os.execute("rm " .. unformated_file) - end - ) - - it( - "should take a single path to formated code, do nothing, and return an empty table", - function() - local file = assert( - io.open(unformated_file, "w"), - "failed to open test file for formatting" - ) - file:write(expected_content) - file:close() - local result = assert(kcl_lib.format(unformated_file)) - assert.are.same({}, result) - os.execute("rm " .. unformated_file) - end - ) end) end) diff --git a/lua/src/client.rs b/lua/src/client.rs new file mode 100644 index 0000000..df08510 --- /dev/null +++ b/lua/src/client.rs @@ -0,0 +1,20 @@ +extern crate kcl_api; + +use mlua::{Error, MetaMethod, String as LuaString, UserData, UserDataMethods}; + +#[derive(Default)] +pub struct NativeServiceClient; + +impl UserData for NativeServiceClient { + fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) { + methods.add_method( + "call", + |lua, _this, (name, args): (LuaString, LuaString)| { + kcl_api::call(name.as_bytes(), args.as_bytes()) + .map(|v| lua.create_string(v)) + .map_err(|e| Error::runtime(e.to_string())) + }, + ); + methods.add_meta_function(MetaMethod::Call, |_, ()| Ok(NativeServiceClient::default())); + } +} diff --git a/lua/src/lib.rs b/lua/src/lib.rs index e18da8d..4b558de 100644 --- a/lua/src/lib.rs +++ b/lua/src/lib.rs @@ -2,60 +2,18 @@ extern crate kcl_api; use mlua::prelude::*; -/// Execute KCL code and return the JSON/YAML result. -fn run<'a>(lua: &'a Lua, path: LuaValue) -> LuaResult> { - let api = kcl_api::API::default(); - let k_filename_list = match path { - LuaValue::String(s) => Ok(vec![s.to_str()?.to_owned()]), - LuaValue::Table(t) => t - .sequence_values::() - .collect::, LuaError>>(), - _ => { - return Err(LuaError::runtime( - "invalid argument type for function `run`, expecting string or table", - )); - } - }?; +mod client; - let result = match api.exec_program(&kcl_api::ExecProgramArgs { - k_filename_list, - ..Default::default() - }) { - Ok(r) => r, - Err(e) => return Err(LuaError::external(e)), - }; +pub use client::NativeServiceClient; - let t = lua.create_table()?; - t.set("json_result", lua.create_string(result.json_result)?)?; - t.set("yaml_result", lua.create_string(result.yaml_result)?)?; - t.set("log_message", lua.create_string(result.log_message)?)?; - t.set("err_message", lua.create_string(result.err_message)?)?; - Ok(t) -} - -/// Format KCL code from a file. -fn format<'a>(lua: &'a Lua, path: String) -> LuaResult> { - let api = kcl_api::API::default(); - - let result = match api.format_path(&kcl_api::FormatPathArgs { - path, - ..Default::default() - }) { - Ok(r) => r, - Err(e) => return Err(LuaError::external(e)), - }; - - let t = lua.create_table()?; - for changed_path in result.changed_paths.iter() { - t.push(lua.create_string(changed_path)?)?; - } - Ok(t) +/// Create a new NativeServiceClient instance +fn new_client<'a>(_lua: &'a Lua, _args: ()) -> LuaResult { + Ok(NativeServiceClient::default()) } #[mlua::lua_module] fn kcl_lib(lua: &Lua) -> LuaResult> { let module = lua.create_table()?; - module.set("run", lua.create_function(run)?)?; - module.set("format", lua.create_function(format)?)?; + module.set("new_client", lua.create_function(new_client)?)?; Ok(module) }