├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── main_darwin.yml │ ├── main_linux.yml │ └── main_windows.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── bench_test.go ├── docs └── api.md ├── example_test.go ├── examples ├── kubernetes │ └── main.go └── plugin │ └── main.go ├── go.mod ├── go.sum ├── hello.k ├── kcl.go ├── kcl_test.go ├── kcl_version.go ├── pkg ├── 3rdparty │ ├── grpc_gateway_util │ │ ├── LICENSE.txt │ │ ├── http_convert.go │ │ └── http_get_query.go │ ├── jsonschema │ │ ├── README.md │ │ ├── draft2019_09_keywords.go │ │ ├── keyword.go │ │ ├── keywords_array.go │ │ ├── keywords_boolean.go │ │ ├── keywords_conditional.go │ │ ├── keywords_core.go │ │ ├── keywords_numeric.go │ │ ├── keywords_object.go │ │ ├── keywords_optional.go │ │ ├── keywords_standard.go │ │ ├── keywords_string.go │ │ ├── schema.go │ │ ├── schema_registry.go │ │ ├── traversal.go │ │ ├── util.go │ │ └── validation_state.go │ └── toml │ │ ├── COPYING │ │ ├── README.md │ │ ├── decode.go │ │ ├── deprecated.go │ │ ├── doc.go │ │ ├── encode.go │ │ ├── error.go │ │ ├── internal │ │ ├── tag │ │ │ ├── add.go │ │ │ └── rm.go │ │ └── tz.go │ │ ├── lex.go │ │ ├── meta.go │ │ ├── parse.go │ │ ├── type_fields.go │ │ └── type_toml.go ├── ast │ ├── ast.go │ ├── expr.go │ ├── json.go │ ├── op.go │ ├── stmt.go │ └── type.go ├── kcl │ ├── api.go │ ├── api_test.go │ ├── hook.go │ ├── opt.go │ ├── opt_test.go │ ├── service.go │ ├── testdata │ │ ├── complicate │ │ │ ├── appops │ │ │ │ ├── projectA │ │ │ │ │ ├── base │ │ │ │ │ │ └── base.k │ │ │ │ │ └── dev │ │ │ │ │ │ └── main.k │ │ │ │ └── projectB │ │ │ │ │ ├── base │ │ │ │ │ └── base.k │ │ │ │ │ └── dev │ │ │ │ │ └── main.k │ │ │ ├── base │ │ │ │ ├── frontend │ │ │ │ │ ├── container │ │ │ │ │ │ ├── container.k │ │ │ │ │ │ └── container_port.k │ │ │ │ │ ├── job │ │ │ │ │ │ └── job.k │ │ │ │ │ └── server │ │ │ │ │ │ └── server.k │ │ │ │ └── render │ │ │ │ │ ├── job │ │ │ │ │ └── job_render.k │ │ │ │ │ └── server │ │ │ │ │ └── server_render.k │ │ │ └── kcl.mod │ │ └── get_schema_ty │ │ │ ├── aaa │ │ │ ├── kcl.mod │ │ │ └── main.k │ │ │ └── bbb │ │ │ ├── kcl.mod │ │ │ └── main.k │ ├── testing_test.go │ ├── type.go │ └── version.go ├── loader │ ├── options.go │ ├── options_test.go │ ├── package.go │ ├── test_data │ │ └── options.k │ └── variables.go ├── logger │ ├── example_test.go │ └── logger.go ├── native │ ├── client.go │ ├── client_test.go │ └── testdata │ │ ├── 1.k │ │ └── 2.k ├── parser │ ├── parser.go │ └── parser_test.go ├── plugin │ ├── api.go │ ├── api_test.go │ ├── error.go │ ├── hello_plugin │ │ ├── api.go │ │ └── api_test.go │ ├── plugin.go │ └── spec.go ├── server │ └── rest_server.go ├── service │ └── kclvm_service.go ├── settings │ ├── a_test.go │ ├── a_test_unix.go │ ├── a_test_windows.go │ ├── kcl.mod │ └── utils_settings_yaml.go ├── source │ └── source.go ├── spec │ └── gpyrpc │ │ ├── gpyrpc.pb.go │ │ ├── gpyrpc.pb.protorpc.go │ │ └── server.go ├── tools │ ├── format │ │ ├── format_test.go │ │ ├── kformat.go │ │ └── testdata │ │ │ └── success │ │ │ ├── hello.formatted │ │ │ ├── hello.k │ │ │ ├── person.formatted │ │ │ └── person.k │ ├── gen │ │ ├── gendoc.go │ │ ├── gendoc_test.go │ │ ├── gengo.go │ │ ├── gengo_test.go │ │ ├── genkcl.go │ │ ├── genkcl_gostruct.go │ │ ├── genkcl_json.go │ │ ├── genkcl_jsonschema.go │ │ ├── genkcl_proto.go │ │ ├── genkcl_terraform.go │ │ ├── genkcl_test.go │ │ ├── genkcl_textproto.go │ │ ├── genkcl_toml.go │ │ ├── genkcl_value.go │ │ ├── genkcl_yaml.go │ │ ├── genopenapi.go │ │ ├── genopenapi_test.go │ │ ├── genpb.go │ │ ├── genpb_test.go │ │ ├── gentoml.go │ │ ├── gentoml_test.go │ │ ├── interface.go │ │ ├── reflect.go │ │ ├── template.go │ │ ├── templates │ │ │ ├── doc │ │ │ │ ├── packageDoc.gotmpl │ │ │ │ ├── schemaDoc.gotmpl │ │ │ │ └── schemaListDoc.gotmpl │ │ │ └── kcl │ │ │ │ ├── config.gotmpl │ │ │ │ ├── data.gotmpl │ │ │ │ ├── document.gotmpl │ │ │ │ ├── header.gotmpl │ │ │ │ ├── index.gotmpl │ │ │ │ ├── schema.gotmpl │ │ │ │ └── validator.gotmpl │ │ ├── testdata │ │ │ ├── doc │ │ │ │ ├── k8s │ │ │ │ │ ├── apps │ │ │ │ │ │ └── deployment.k │ │ │ │ │ ├── core │ │ │ │ │ │ └── podSpec.k │ │ │ │ │ ├── kcl.mod │ │ │ │ │ └── md │ │ │ │ │ │ └── main.md │ │ │ │ ├── pkg │ │ │ │ │ ├── container.k │ │ │ │ │ ├── k8s │ │ │ │ │ │ ├── core │ │ │ │ │ │ │ └── podSpec.k │ │ │ │ │ │ └── deployment.k │ │ │ │ │ ├── kcl.mod │ │ │ │ │ ├── md │ │ │ │ │ │ └── main.md │ │ │ │ │ └── server.k │ │ │ │ └── reimport │ │ │ │ │ ├── a.k │ │ │ │ │ ├── b.k │ │ │ │ │ ├── k8s │ │ │ │ │ └── deployment.k │ │ │ │ │ ├── kcl.mod │ │ │ │ │ └── md │ │ │ │ │ └── main.md │ │ │ ├── genkcldata.go │ │ │ ├── json │ │ │ │ ├── expect.k │ │ │ │ └── input.json │ │ │ ├── jsonschema │ │ │ │ ├── additional │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── allof-validation │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── allof │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── as3 │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── basic │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── casting-options │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── complex-workflow │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── const │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── document │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── document_quota │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── document_quota_0 │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── items │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── multipleof │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── nested-items │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── nested │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── oneof │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── oneof_parent_fields │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── pattern-props │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── pattern │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── ref │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ ├── unsupport-multi-pattern-props │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ │ └── validation │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.json │ │ │ ├── oai │ │ │ │ └── k8s │ │ │ │ │ ├── apps │ │ │ │ │ └── deployment.k │ │ │ │ │ ├── core │ │ │ │ │ └── podSpec.k │ │ │ │ │ └── kcl.mod │ │ │ ├── openapi │ │ │ │ └── app │ │ │ │ │ ├── kcl.mod │ │ │ │ │ └── models │ │ │ │ │ └── schema │ │ │ │ │ └── v1 │ │ │ │ │ ├── accessories │ │ │ │ │ └── database.k │ │ │ │ │ ├── app_configuration.k │ │ │ │ │ ├── monitoring │ │ │ │ │ └── prometheus.k │ │ │ │ │ ├── trait │ │ │ │ │ └── opsrule.k │ │ │ │ │ └── workload │ │ │ │ │ ├── common.k │ │ │ │ │ ├── container │ │ │ │ │ ├── container.k │ │ │ │ │ ├── lifecycle │ │ │ │ │ │ └── lifecycle.k │ │ │ │ │ └── probe │ │ │ │ │ │ └── probe.k │ │ │ │ │ ├── job.k │ │ │ │ │ ├── network │ │ │ │ │ └── port.k │ │ │ │ │ ├── secret │ │ │ │ │ └── secret.k │ │ │ │ │ └── service.k │ │ │ ├── proto │ │ │ │ ├── proto2kcl.k │ │ │ │ ├── proto2kcl.proto │ │ │ │ └── proto2kcl_for_num.k │ │ │ ├── terraform │ │ │ │ ├── expect.k │ │ │ │ └── schema.json │ │ │ ├── textproto │ │ │ │ ├── data.textproto │ │ │ │ └── schema.k │ │ │ ├── toml │ │ │ │ ├── expect.k │ │ │ │ └── input.toml │ │ │ ├── yaml │ │ │ │ ├── basic │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.yaml │ │ │ │ ├── comment │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.yaml │ │ │ │ ├── goreleaser │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.yaml │ │ │ │ ├── identifier │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.yaml │ │ │ │ ├── k8s-configmap │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.yaml │ │ │ │ ├── k8s-deployment │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.yaml │ │ │ │ ├── list │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.yaml │ │ │ │ └── workflow │ │ │ │ │ ├── expect.k │ │ │ │ │ └── input.yaml │ │ │ └── yaml2 │ │ │ │ └── basic │ │ │ │ ├── expect.k │ │ │ │ └── input.yaml │ │ ├── types.go │ │ └── utils.go │ ├── lint │ │ ├── lint.go │ │ ├── lint_test.go │ │ └── testdata │ │ │ └── a.k │ ├── list │ │ ├── dep_parser.go │ │ ├── dep_parser_helper_test.go │ │ ├── dep_parser_test.go │ │ ├── dep_parser_unix_test.go │ │ ├── dep_parser_windows_test.go │ │ ├── import_dep_parser.go │ │ ├── list.go │ │ ├── list_test.go │ │ ├── pkg_info.go │ │ ├── single_app_dep_parser.go │ │ ├── testdata │ │ │ ├── complicate │ │ │ │ ├── appops │ │ │ │ │ ├── projectA │ │ │ │ │ │ ├── base │ │ │ │ │ │ │ └── base.k │ │ │ │ │ │ └── dev │ │ │ │ │ │ │ └── main.k │ │ │ │ │ ├── projectB │ │ │ │ │ │ ├── base │ │ │ │ │ │ │ └── base.k │ │ │ │ │ │ └── dev │ │ │ │ │ │ │ └── main.k │ │ │ │ │ ├── projectC │ │ │ │ │ │ └── dev │ │ │ │ │ │ │ └── main.k │ │ │ │ │ ├── projectD │ │ │ │ │ │ ├── base │ │ │ │ │ │ │ └── base.k │ │ │ │ │ │ └── dev │ │ │ │ │ │ │ └── main.k │ │ │ │ │ ├── projectE │ │ │ │ │ │ └── base │ │ │ │ │ │ │ └── base.k │ │ │ │ │ ├── projectF │ │ │ │ │ │ ├── base │ │ │ │ │ │ │ └── base.k │ │ │ │ │ │ └── dev │ │ │ │ │ │ │ └── main.k │ │ │ │ │ ├── projectG │ │ │ │ │ │ ├── base │ │ │ │ │ │ │ └── base.k │ │ │ │ │ │ └── dev │ │ │ │ │ │ │ └── main.k │ │ │ │ │ ├── projectH │ │ │ │ │ │ ├── base │ │ │ │ │ │ │ ├── base.k │ │ │ │ │ │ │ └── base │ │ │ │ │ │ │ │ └── base.k │ │ │ │ │ │ └── dev │ │ │ │ │ │ │ └── main.k │ │ │ │ │ └── projectI │ │ │ │ │ │ └── dev │ │ │ │ │ │ └── main.k │ │ │ │ ├── base │ │ │ │ │ ├── frontend │ │ │ │ │ │ ├── container │ │ │ │ │ │ │ ├── container.k │ │ │ │ │ │ │ ├── container_port.k │ │ │ │ │ │ │ └── probe │ │ │ │ │ │ │ │ ├── _internal_file.k │ │ │ │ │ │ │ │ ├── exec.k │ │ │ │ │ │ │ │ ├── http.k │ │ │ │ │ │ │ │ ├── probe.k │ │ │ │ │ │ │ │ ├── probe_test.k │ │ │ │ │ │ │ │ ├── readme.md │ │ │ │ │ │ │ │ └── tcp.k │ │ │ │ │ │ ├── invalid_a │ │ │ │ │ │ │ └── invalid_module_A.k │ │ │ │ │ │ ├── invalid_b │ │ │ │ │ │ │ ├── invalid_module_B_1.k │ │ │ │ │ │ │ └── invalid_module_B_2.k │ │ │ │ │ │ ├── job │ │ │ │ │ │ │ └── job.k │ │ │ │ │ │ └── server │ │ │ │ │ │ │ ├── server.k │ │ │ │ │ │ │ └── server │ │ │ │ │ │ │ └── server.k │ │ │ │ │ └── render │ │ │ │ │ │ ├── job │ │ │ │ │ │ └── job_render.k │ │ │ │ │ │ └── server │ │ │ │ │ │ └── server_render.k │ │ │ │ └── kcl.mod │ │ │ ├── kcl_mod_env │ │ │ │ ├── kcl.mod │ │ │ │ ├── kcl.yaml │ │ │ │ ├── main1.k │ │ │ │ └── main2.k │ │ │ ├── module_with_external │ │ │ │ ├── kcl.mod │ │ │ │ └── main.k │ │ │ ├── mymod │ │ │ │ ├── kcl.mod │ │ │ │ └── sub │ │ │ │ │ └── app │ │ │ │ │ └── main.k │ │ │ └── no-kcl-mod │ │ │ │ └── .keep │ │ └── utils.go │ ├── module │ │ └── module.go │ ├── override │ │ ├── kcl.mod │ │ ├── override.go │ │ ├── override_test.go │ │ └── testdata │ │ │ ├── pkg │ │ │ └── pkg.k │ │ │ ├── test.k │ │ │ └── test_with_relative_import.k │ ├── testing │ │ ├── indent.go │ │ ├── reporter.go │ │ ├── reporter_test.go │ │ └── testing.go │ └── validate │ │ ├── test_data │ │ ├── data-failed.json │ │ ├── data.json │ │ └── schema.k │ │ ├── validate.go │ │ └── validate_test.go └── utils │ ├── assert.go │ ├── pkg_path_test.go │ ├── pkg_root.go │ ├── testdata │ ├── a │ │ └── b │ │ │ └── x.k │ ├── kcl.mod │ └── sub │ │ └── main.k │ └── utils.go ├── scripts ├── kclvm.go ├── util_fs.go ├── util_http_get.go ├── util_md5.go ├── util_untargz.go ├── util_unzip.go └── util_zipfs.go └── testdata ├── app0-failed ├── kcl.yaml ├── main.k └── sub │ └── sub.k ├── app0 ├── before │ └── base.k ├── kcl.yaml ├── main.k └── sub │ └── sub.k ├── external ├── external_1 │ ├── kcl.mod │ └── main.k └── external_2 │ ├── kcl.mod │ └── main.k ├── get_schema_type ├── base.k └── schema.k ├── hello ├── hello.k └── hello_test.k ├── import-external └── main.k ├── import_builtin ├── entry │ └── main.k ├── kcl.mod └── sub │ └── main.k ├── kcl.mod ├── lint ├── a.k ├── import.k └── kcl.mod ├── main.k ├── option └── main.k ├── stream ├── one_stream.k └── two_stream.k ├── sub ├── sub.k └── sub_test.k ├── test_module ├── kcl.mod └── pkg │ ├── func.k │ └── func_test.k ├── test_plan ├── kcl.mod └── main.k ├── test_print ├── kcl.mod └── main.k ├── update_dependencies ├── kcl.mod └── main.k ├── vet ├── hello.k ├── hello.k.json ├── sample.k └── sample.k.json └── xappinfo └── settings.yaml /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | 3 | root = true 4 | 5 | # Unix-style newlines with a newline ending every file 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*] 13 | indent_style = tab 14 | 15 | [*.{go,proto}] 16 | charset = utf-8 17 | indent_style = tab 18 | 19 | # Matches the exact files either package.json or .travis.yml 20 | [{package.json,.travis.yml}] 21 | indent_style = space 22 | indent_size = 2 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gomod" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | commit-message: 13 | prefix: "Chore: " 14 | include: "scope" 15 | ignore: 16 | - dependency-name: k8s.io/* 17 | - package-ecosystem: "github-actions" 18 | directory: "/" 19 | schedule: 20 | interval: "weekly" 21 | commit-message: 22 | prefix: "chore: " 23 | include: "scope" 24 | -------------------------------------------------------------------------------- /.github/workflows/main_darwin.yml: -------------------------------------------------------------------------------- 1 | name: build-and-test-macos 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | - "releases/*" 8 | jobs: 9 | build-and-test: 10 | # Ref: https://github.com/actions/runner-images/tree/main/images/macos 11 | # Note: The arch of macos-13-xlarge and macos-14 is arm64 12 | strategy: 13 | matrix: 14 | os: [ macos-13, macos-13-xlarge, macos-14 ] 15 | cgo: [ '1', '0' ] 16 | runs-on: ${{ matrix.os }} 17 | steps: 18 | - name: Git checkout 19 | uses: actions/checkout@v4 20 | 21 | - name: Set up Go 22 | uses: actions/setup-go@v5 23 | with: 24 | go-version-file: go.mod 25 | 26 | - name: Go test 27 | env: 28 | CGO_ENABLED: ${{ matrix.cgo }} 29 | run: go test ./... 30 | -------------------------------------------------------------------------------- /.github/workflows/main_linux.yml: -------------------------------------------------------------------------------- 1 | name: build-and-test-linux 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | - "releases/*" 8 | jobs: 9 | build-and-test: 10 | # Ref: https://github.com/actions/runner-images/tree/main/images/macos 11 | strategy: 12 | matrix: 13 | os: [ ubuntu-22.04, ubuntu-latest ] 14 | cgo: [ '1', '0' ] 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - name: Git checkout 18 | uses: actions/checkout@v4 19 | 20 | - name: Set up Go 21 | uses: actions/setup-go@v5 22 | with: 23 | go-version-file: go.mod 24 | 25 | - name: Go test 26 | env: 27 | CGO_ENABLED: ${{ matrix.cgo }} 28 | run: go test ./... 29 | 30 | - run: go test -v -coverprofile=profile.cov ./... 31 | - uses: shogo82148/actions-goveralls@v1 32 | with: 33 | path-to-profile: profile.cov 34 | -------------------------------------------------------------------------------- /.github/workflows/main_windows.yml: -------------------------------------------------------------------------------- 1 | name: build-and-test-windows 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | - "releases/*" 8 | jobs: 9 | build-and-test: 10 | strategy: 11 | matrix: 12 | os: [ windows-latest ] 13 | cgo: [ '1', '0' ] 14 | runs-on: ${{ matrix.os }} 15 | steps: 16 | - name: Git checkout 17 | uses: actions/checkout@v4 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v5 21 | with: 22 | go-version-file: go.mod 23 | 24 | - name: Go test 25 | env: 26 | CGO_ENABLED: ${{ matrix.cgo }} 27 | run: go test ./... 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.o 3 | *.so 4 | 5 | *.exe 6 | *.lib 7 | *.dll 8 | 9 | a.out* 10 | zz_* 11 | /_build* 12 | *.dylib 13 | *.lock 14 | .kclvm 15 | 16 | **/target/ 17 | **/.DS_Store 18 | **/.vscode 19 | __pycache__ 20 | /vendor 21 | profile.cov 22 | 23 | .idea/ 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2023 The KCL Authors. All rights reserved. 2 | 3 | PROJECT_NAME = kcl-go 4 | 5 | PWD:=$(shell pwd) 6 | 7 | BUILD_IMAGE:=kcllang/kcl 8 | 9 | # export DOCKER_DEFAULT_PLATFORM=linux/amd64 10 | # or 11 | # --platform linux/amd64 12 | 13 | RUN_IN_DOCKER:=docker run -it --rm 14 | RUN_IN_DOCKER+=-v ~/.ssh:/root/.ssh 15 | RUN_IN_DOCKER+=-v ~/.gitconfig:/root/.gitconfig 16 | RUN_IN_DOCKER+=-v ${PWD}:/root/kcl 17 | RUN_IN_DOCKER+=-w /root/kcl ${BUILD_IMAGE} 18 | 19 | clean: 20 | -rm -rf ./_build 21 | 22 | test: 23 | go test ./... 24 | 25 | fmt: 26 | go fmt ./... 27 | 28 | gen-doc: 29 | go install github.com/princjef/gomarkdoc/cmd/gomarkdoc@latest 30 | gomarkdoc --output api.md . 31 | mv api.md docs 32 | 33 | # ---------------- 34 | # Docker 35 | # ---------------- 36 | 37 | sh-in-docker: 38 | ${RUN_IN_DOCKER} bash 39 | -------------------------------------------------------------------------------- /examples/kubernetes/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | kcl "kcl-lang.io/kcl-go" 7 | ) 8 | 9 | func main() { 10 | yaml := kcl.MustRun("kubernetes.k", kcl.WithCode(code)).GetRawYamlResult() 11 | fmt.Println(yaml) 12 | } 13 | 14 | const code = ` 15 | apiVersion = "apps/v1" 16 | kind = "Deployment" 17 | metadata = { 18 | name = "nginx" 19 | labels.app = "nginx" 20 | } 21 | spec = { 22 | replicas = 3 23 | selector.matchLabels = metadata.labels 24 | template.metadata.labels = metadata.labels 25 | template.spec.containers = [ 26 | { 27 | name = metadata.name 28 | image = "${metadata.name}:1.14.2" 29 | ports = [{ containerPort = 80 }] 30 | } 31 | ] 32 | } 33 | ` 34 | -------------------------------------------------------------------------------- /examples/plugin/main.go: -------------------------------------------------------------------------------- 1 | //go:build cgo 2 | // +build cgo 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | 9 | "kcl-lang.io/kcl-go/pkg/kcl" // Import the native API 10 | _ "kcl-lang.io/kcl-go/pkg/plugin/hello_plugin" // Import the hello plugin 11 | ) 12 | 13 | func main() { 14 | yaml := kcl.MustRun("main.k", kcl.WithCode(code)).GetRawYamlResult() 15 | fmt.Println(yaml) 16 | } 17 | 18 | const code = ` 19 | import kcl_plugin.hello 20 | 21 | name = "kcl" 22 | three = hello.add(1,2) 23 | ` 24 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module kcl-lang.io/kcl-go 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/chai2010/jsonv v1.1.3 7 | github.com/chai2010/protorpc v1.1.4 8 | github.com/emicklei/proto v1.14.1 9 | github.com/getkin/kin-openapi v0.132.0 10 | github.com/goccy/go-yaml v1.17.1 11 | github.com/golang/protobuf v1.5.4 12 | github.com/google/go-cmp v0.7.0 13 | github.com/iancoleman/strcase v0.3.0 14 | github.com/julienschmidt/httprouter v1.3.0 15 | github.com/mitchellh/mapstructure v1.5.0 16 | github.com/protocolbuffers/txtpbfmt v0.0.0-20240416193709-1e18ef0a7fdc 17 | github.com/qri-io/jsonpointer v0.1.1 18 | github.com/stretchr/testify v1.10.0 19 | github.com/wk8/go-ordered-map/v2 v2.1.8 20 | github.com/yuin/goldmark v1.7.12 21 | golang.org/x/tools v0.33.0 22 | google.golang.org/grpc v1.72.1 23 | google.golang.org/protobuf v1.36.6 24 | gopkg.in/yaml.v3 v3.0.1 25 | kcl-lang.io/lib v0.11.2 26 | ) 27 | 28 | require ( 29 | github.com/bahlo/generic-list-go v0.2.0 // indirect 30 | github.com/buger/jsonparser v1.1.1 // indirect 31 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 32 | github.com/ebitengine/purego v0.7.1 // indirect 33 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 34 | github.com/go-openapi/swag v0.23.0 // indirect 35 | github.com/gofrs/flock v0.12.1 // indirect 36 | github.com/golang/snappy v0.0.4 // indirect 37 | github.com/josharian/intern v1.0.0 // indirect 38 | github.com/mailru/easyjson v0.7.7 // indirect 39 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 40 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect 41 | github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect 42 | github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect 43 | github.com/perimeterx/marshmallow v1.1.5 // indirect 44 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 45 | golang.org/x/mod v0.24.0 // indirect 46 | golang.org/x/net v0.40.0 // indirect 47 | golang.org/x/sync v0.14.0 // indirect 48 | golang.org/x/sys v0.33.0 // indirect 49 | golang.org/x/text v0.25.0 // indirect 50 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect 51 | ) 52 | -------------------------------------------------------------------------------- /hello.k: -------------------------------------------------------------------------------- 1 | name = "kcl" 2 | age = 1 3 | two = 2 4 | 5 | schema Person: 6 | name: str = "kcl" 7 | age: int = 1 8 | 9 | x0 = Person {} 10 | x1 = Person { 11 | age = 101 12 | } 13 | -------------------------------------------------------------------------------- /kcl_version.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package kcl 4 | 5 | import "kcl-lang.io/kcl-go/scripts" 6 | 7 | // KclvmAbiVersion is the current kclvm ABI version. 8 | const KclvmAbiVersion = scripts.KclvmAbiVersion 9 | -------------------------------------------------------------------------------- /pkg/3rdparty/grpc_gateway_util/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Gengo, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Gengo, Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from this 16 | software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /pkg/3rdparty/jsonschema/README.md: -------------------------------------------------------------------------------- 1 | # jsonschema 2 | 3 | This package is a fork of the [jsonschema](https://github.com/qri-io/jsonschema) 4 | package, which is a Go implementation of the JSON Schema specification. 5 | 6 | Thanks to the original authors for their great work, we are able to use this package to 7 | parse JSON Schema and support keywords in different versions of the specification easily. 8 | We also make some modifications to support our needs, such as make some field public, 9 | use orderedmap in properties keyword to keep the order, etc. 10 | 11 | ## License 12 | 13 | ``` 14 | The MIT License (MIT) 15 | 16 | Copyright (c) 2017 Brendan O'Brien 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in 26 | all copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 34 | THE SOFTWARE. 35 | ``` 36 | -------------------------------------------------------------------------------- /pkg/3rdparty/jsonschema/schema_registry.go: -------------------------------------------------------------------------------- 1 | package jsonschema 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | var sr *SchemaRegistry 10 | 11 | // SchemaRegistry maintains a lookup table between schema string references 12 | // and actual schemas 13 | type SchemaRegistry struct { 14 | schemaLookup map[string]*Schema 15 | contextLookup map[string]*Schema 16 | } 17 | 18 | // GetSchemaRegistry provides an accessor to a globally available schema registry 19 | func GetSchemaRegistry() *SchemaRegistry { 20 | if sr == nil { 21 | sr = &SchemaRegistry{ 22 | schemaLookup: map[string]*Schema{}, 23 | contextLookup: map[string]*Schema{}, 24 | } 25 | } 26 | return sr 27 | } 28 | 29 | // ResetSchemaRegistry resets the main SchemaRegistry 30 | func ResetSchemaRegistry() { 31 | sr = nil 32 | } 33 | 34 | // Get fetches a schema from the top level context registry or fetches it from a remote 35 | func (sr *SchemaRegistry) Get(ctx context.Context, uri string) *Schema { 36 | uri = strings.TrimRight(uri, "#") 37 | schema := sr.schemaLookup[uri] 38 | if schema == nil { 39 | fetchedSchema := &Schema{} 40 | err := FetchSchema(ctx, uri, fetchedSchema) 41 | if err != nil { 42 | schemaDebug(fmt.Sprintf("[SchemaRegistry] Fetch error: %s", err.Error())) 43 | return nil 44 | } 45 | 46 | fetchedSchema.DocPath = uri 47 | // TODO(arqu): meta validate schema 48 | schema = fetchedSchema 49 | sr.schemaLookup[uri] = schema 50 | } 51 | return schema 52 | } 53 | 54 | // GetKnown fetches a schema from the top level context registry 55 | func (sr *SchemaRegistry) GetKnown(uri string) *Schema { 56 | uri = strings.TrimRight(uri, "#") 57 | return sr.schemaLookup[uri] 58 | } 59 | 60 | // GetLocal fetches a schema from the local context registry 61 | func (sr *SchemaRegistry) GetLocal(uri string) *Schema { 62 | uri = strings.TrimRight(uri, "#") 63 | return sr.contextLookup[uri] 64 | } 65 | 66 | // Register registers a schema to the top level context 67 | func (sr *SchemaRegistry) Register(sch *Schema) { 68 | if sch.DocPath == "" { 69 | return 70 | } 71 | sr.schemaLookup[sch.DocPath] = sch 72 | } 73 | 74 | // RegisterLocal registers a schema to a local context 75 | func (sr *SchemaRegistry) RegisterLocal(sch *Schema) { 76 | if sch.Id != "" && IsLocalSchemaID(sch.Id) { 77 | sr.contextLookup[sch.Id] = sch 78 | } 79 | 80 | if sch.HasKeyword("$anchor") { 81 | anchorKeyword := sch.Keywords["$anchor"].(*Anchor) 82 | anchorURI := sch.DocPath + "#" + string(*anchorKeyword) 83 | if sr.contextLookup == nil { 84 | sr.contextLookup = map[string]*Schema{} 85 | } 86 | sr.contextLookup[anchorURI] = sch 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /pkg/3rdparty/jsonschema/traversal.go: -------------------------------------------------------------------------------- 1 | package jsonschema 2 | 3 | // JSONPather makes validators traversible by JSON-pointers, 4 | // which is required to support references in JSON schemas. 5 | type JSONPather interface { 6 | // JSONProp take a string references for a given JSON property 7 | // implementations must return any matching property of that name 8 | // or nil if no such subproperty exists. 9 | // Note this also applies to array values, which are expected to interpret 10 | // valid numbers as an array index 11 | JSONProp(name string) interface{} 12 | } 13 | 14 | // JSONContainer is an interface that enables tree traversal by listing 15 | // the immideate children of an object 16 | type JSONContainer interface { 17 | // JSONChildren should return all immidiate children of this element 18 | JSONChildren() map[string]JSONPather 19 | } 20 | 21 | func walkJSON(elem JSONPather, fn func(elem JSONPather) error) error { 22 | if err := fn(elem); err != nil { 23 | return err 24 | } 25 | 26 | if con, ok := elem.(JSONContainer); ok { 27 | for _, ch := range con.JSONChildren() { 28 | if err := walkJSON(ch, fn); err != nil { 29 | return err 30 | } 31 | } 32 | } 33 | 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /pkg/3rdparty/jsonschema/util.go: -------------------------------------------------------------------------------- 1 | package jsonschema 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "net/url" 10 | "os" 11 | "strings" 12 | ) 13 | 14 | var showDebug = os.Getenv("JSON_SCHEMA_DEBUG") == "1" 15 | 16 | // schemaDebug provides a logging interface 17 | // which is off by defauly but can be activated 18 | // for debuging purposes 19 | func schemaDebug(message string, args ...interface{}) { 20 | if showDebug { 21 | if message[len(message)-1] != '\n' { 22 | message += "\n" 23 | } 24 | fmt.Printf(message, args...) 25 | } 26 | } 27 | 28 | // SafeResolveURL resolves a string url against the current context url 29 | func SafeResolveURL(ctxURL, resURL string) (string, error) { 30 | cu, err := url.Parse(ctxURL) 31 | if err != nil { 32 | return "", err 33 | } 34 | u, err := url.Parse(resURL) 35 | if err != nil { 36 | return "", err 37 | } 38 | resolvedURL := cu.ResolveReference(u) 39 | if resolvedURL.Scheme == "file" && cu.Scheme != "file" { 40 | return "", fmt.Errorf("cannot access file resources from network context") 41 | } 42 | resolvedURLString := resolvedURL.String() 43 | return resolvedURLString, nil 44 | } 45 | 46 | // IsLocalSchemaID validates if a given Id is a local Id 47 | func IsLocalSchemaID(id string) bool { 48 | splitID := strings.Split(id, "#") 49 | if len(splitID) > 1 && len(splitID[0]) > 0 && splitID[0][0] != '#' { 50 | return false 51 | } 52 | return id != "#" && !strings.HasPrefix(id, "#/") && strings.Contains(id, "#") 53 | } 54 | 55 | // FetchSchema downloads and loads a schema from a remote location 56 | func FetchSchema(ctx context.Context, uri string, schema *Schema) error { 57 | schemaDebug(fmt.Sprintf("[FetchSchema] Fetching: %s", uri)) 58 | u, err := url.Parse(uri) 59 | if err != nil { 60 | return err 61 | } 62 | // TODO(arqu): support other schemas like file or ipfs 63 | if u.Scheme == "http" || u.Scheme == "https" { 64 | var req *http.Request 65 | if ctx != nil { 66 | req, _ = http.NewRequestWithContext(ctx, "GET", u.String(), nil) 67 | } else { 68 | req, _ = http.NewRequest("GET", u.String(), nil) 69 | } 70 | client := &http.Client{} 71 | res, err := client.Do(req) 72 | if err != nil { 73 | return err 74 | } 75 | body, err := io.ReadAll(res.Body) 76 | if err != nil { 77 | return err 78 | } 79 | if schema == nil { 80 | schema = &Schema{} 81 | } 82 | return json.Unmarshal(body, schema) 83 | } 84 | if u.Scheme == "file" { 85 | body, err := os.ReadFile(u.Path) 86 | if err != nil { 87 | return err 88 | } 89 | if schema == nil { 90 | schema = &Schema{} 91 | } 92 | return json.Unmarshal(body, schema) 93 | } 94 | return fmt.Errorf("URI scheme %s is not supported for uri: %s", u.Scheme, uri) 95 | } 96 | -------------------------------------------------------------------------------- /pkg/3rdparty/toml/COPYING: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 TOML authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /pkg/3rdparty/toml/deprecated.go: -------------------------------------------------------------------------------- 1 | package toml 2 | 3 | import ( 4 | "encoding" 5 | "io" 6 | ) 7 | 8 | // TextMarshaler is an alias for encoding.TextMarshaler. 9 | // 10 | // Deprecated: use encoding.TextMarshaler 11 | type TextMarshaler encoding.TextMarshaler 12 | 13 | // TextUnmarshaler is an alias for encoding.TextUnmarshaler. 14 | // 15 | // Deprecated: use encoding.TextUnmarshaler 16 | type TextUnmarshaler encoding.TextUnmarshaler 17 | 18 | // DecodeReader is an alias for NewDecoder(r).Decode(v). 19 | // 20 | // Deprecated: use NewDecoder(reader).Decode(&value). 21 | func DecodeReader(r io.Reader, v any) (MetaData, error) { return NewDecoder(r).Decode(v) } 22 | 23 | // PrimitiveDecode is an alias for MetaData.PrimitiveDecode(). 24 | // 25 | // Deprecated: use MetaData.PrimitiveDecode. 26 | func PrimitiveDecode(primValue Primitive, v any) error { 27 | md := MetaData{decoded: make(map[string]struct{})} 28 | return md.unify(primValue.undecoded, rvalue(v)) 29 | } 30 | -------------------------------------------------------------------------------- /pkg/3rdparty/toml/doc.go: -------------------------------------------------------------------------------- 1 | // Package toml implements decoding and encoding of TOML files. 2 | // 3 | // This package supports TOML v1.0.0, as specified at https://toml.io 4 | // 5 | // The kcl-lang.io/kcl-go/pkg/3rdparty/toml/cmd/tomlv package implements a TOML validator, 6 | // and can be used to verify if TOML document is valid. It can also be used to 7 | // print the type of each key. 8 | package toml 9 | -------------------------------------------------------------------------------- /pkg/3rdparty/toml/internal/tag/add.go: -------------------------------------------------------------------------------- 1 | package tag 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "strconv" 7 | "time" 8 | 9 | "kcl-lang.io/kcl-go/pkg/3rdparty/toml/internal" 10 | ) 11 | 12 | // Add JSON tags to a data structure as expected by toml-test. 13 | func Add(key string, tomlData any) any { 14 | // Switch on the data type. 15 | switch orig := tomlData.(type) { 16 | default: 17 | panic(fmt.Sprintf("Unknown type: %T", tomlData)) 18 | 19 | // A table: we don't need to add any tags, just recurse for every table 20 | // entry. 21 | case map[string]any: 22 | typed := make(map[string]any, len(orig)) 23 | for k, v := range orig { 24 | typed[k] = Add(k, v) 25 | } 26 | return typed 27 | 28 | // An array: we don't need to add any tags, just recurse for every table 29 | // entry. 30 | case []map[string]any: 31 | typed := make([]map[string]any, len(orig)) 32 | for i, v := range orig { 33 | typed[i] = Add("", v).(map[string]any) 34 | } 35 | return typed 36 | case []any: 37 | typed := make([]any, len(orig)) 38 | for i, v := range orig { 39 | typed[i] = Add("", v) 40 | } 41 | return typed 42 | 43 | // Datetime: tag as datetime. 44 | case time.Time: 45 | switch orig.Location() { 46 | default: 47 | return tag("datetime", orig.Format("2006-01-02T15:04:05.999999999Z07:00")) 48 | case internal.LocalDatetime: 49 | return tag("datetime-local", orig.Format("2006-01-02T15:04:05.999999999")) 50 | case internal.LocalDate: 51 | return tag("date-local", orig.Format("2006-01-02")) 52 | case internal.LocalTime: 53 | return tag("time-local", orig.Format("15:04:05.999999999")) 54 | } 55 | 56 | // Tag primitive values: bool, string, int, and float64. 57 | case bool: 58 | return tag("bool", fmt.Sprintf("%v", orig)) 59 | case string: 60 | return tag("string", orig) 61 | case int64: 62 | return tag("integer", fmt.Sprintf("%d", orig)) 63 | case float64: 64 | switch { 65 | case math.IsNaN(orig): 66 | return tag("float", "nan") 67 | case math.IsInf(orig, 1): 68 | return tag("float", "inf") 69 | case math.IsInf(orig, -1): 70 | return tag("float", "-inf") 71 | default: 72 | return tag("float", strconv.FormatFloat(orig, 'f', -1, 64)) 73 | } 74 | } 75 | } 76 | 77 | func tag(typeName string, data any) map[string]any { 78 | return map[string]any{ 79 | "type": typeName, 80 | "value": data, 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /pkg/3rdparty/toml/internal/tz.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import "time" 4 | 5 | // Timezones used for local datetime, date, and time TOML types. 6 | // 7 | // The exact way times and dates without a timezone should be interpreted is not 8 | // well-defined in the TOML specification and left to the implementation. These 9 | // defaults to current local timezone offset of the computer, but this can be 10 | // changed by changing these variables before decoding. 11 | // 12 | // TODO: 13 | // Ideally we'd like to offer people the ability to configure the used timezone 14 | // by setting Decoder.Timezone and Encoder.Timezone; however, this is a bit 15 | // tricky: the reason we use three different variables for this is to support 16 | // round-tripping – without these specific TZ names we wouldn't know which 17 | // format to use. 18 | // 19 | // There isn't a good way to encode this right now though, and passing this sort 20 | // of information also ties in to various related issues such as string format 21 | // encoding, encoding of comments, etc. 22 | // 23 | // So, for the time being, just put this in internal until we can write a good 24 | // comprehensive API for doing all of this. 25 | // 26 | // The reason they're exported is because they're referred from in e.g. 27 | // internal/tag. 28 | // 29 | // Note that this behaviour is valid according to the TOML spec as the exact 30 | // behaviour is left up to implementations. 31 | var ( 32 | localOffset = func() int { _, o := time.Now().Zone(); return o }() 33 | LocalDatetime = time.FixedZone("datetime-local", localOffset) 34 | LocalDate = time.FixedZone("date-local", localOffset) 35 | LocalTime = time.FixedZone("time-local", localOffset) 36 | ) 37 | -------------------------------------------------------------------------------- /pkg/3rdparty/toml/type_toml.go: -------------------------------------------------------------------------------- 1 | package toml 2 | 3 | // tomlType represents any Go type that corresponds to a TOML type. 4 | // While the first draft of the TOML spec has a simplistic type system that 5 | // probably doesn't need this level of sophistication, we seem to be militating 6 | // toward adding real composite types. 7 | type tomlType interface { 8 | typeString() string 9 | } 10 | 11 | // typeEqual accepts any two types and returns true if they are equal. 12 | func typeEqual(t1, t2 tomlType) bool { 13 | if t1 == nil || t2 == nil { 14 | return false 15 | } 16 | return t1.typeString() == t2.typeString() 17 | } 18 | 19 | func typeIsTable(t tomlType) bool { 20 | return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) 21 | } 22 | 23 | type tomlBaseType string 24 | 25 | func (btype tomlBaseType) typeString() string { return string(btype) } 26 | func (btype tomlBaseType) String() string { return btype.typeString() } 27 | 28 | var ( 29 | tomlInteger tomlBaseType = "Integer" 30 | tomlFloat tomlBaseType = "Float" 31 | tomlDatetime tomlBaseType = "Datetime" 32 | tomlString tomlBaseType = "String" 33 | tomlBool tomlBaseType = "Bool" 34 | tomlArray tomlBaseType = "Array" 35 | tomlHash tomlBaseType = "Hash" 36 | tomlArrayHash tomlBaseType = "ArrayHash" 37 | ) 38 | 39 | // typeOfPrimitive returns a tomlType of any primitive value in TOML. 40 | // Primitive values are: Integer, Float, Datetime, String and Bool. 41 | // 42 | // Passing a lexer item other than the following will cause a BUG message 43 | // to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime. 44 | func (p *parser) typeOfPrimitive(lexItem item) tomlType { 45 | switch lexItem.typ { 46 | case itemInteger: 47 | return tomlInteger 48 | case itemFloat: 49 | return tomlFloat 50 | case itemDatetime: 51 | return tomlDatetime 52 | case itemString, itemStringEsc: 53 | return tomlString 54 | case itemMultilineString: 55 | return tomlString 56 | case itemRawString: 57 | return tomlString 58 | case itemRawMultilineString: 59 | return tomlString 60 | case itemBool: 61 | return tomlBool 62 | } 63 | p.bug("Cannot infer primitive type of lex item '%s'.", lexItem) 64 | panic("unreachable") 65 | } 66 | -------------------------------------------------------------------------------- /pkg/ast/ast.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | // Module is an abstract syntax tree for a single KCL file. 4 | type Module struct { 5 | Filename string `json:"filename"` 6 | Pkg string `json:"pkg"` 7 | Doc *Node[string] `json:"doc"` 8 | Body []*Node[Stmt] `json:"body"` 9 | Comments []*Node[Comment] `json:"comments"` 10 | } 11 | 12 | // NewModule creates a new Module instance 13 | func NewModule() *Module { 14 | return &Module{ 15 | Body: make([]*Node[Stmt], 0), 16 | Comments: make([]*Node[Comment], 0), 17 | } 18 | } 19 | 20 | // Node is the file, line and column number information that all AST nodes need to contain. 21 | // In fact, column and end_column are the counts of character. For example, `\t` is counted as 1 character, 22 | // so it is recorded as 1 here, but generally col is 4. 23 | type Node[T any] struct { 24 | ID AstIndex `json:"id,omitempty"` 25 | Node T `json:"node,omitempty"` 26 | Pos 27 | } 28 | 29 | // AstIndex represents a unique identifier for AST nodes. 30 | type AstIndex string 31 | 32 | // Pos denotes the struct tuple (filename, line, column, end_line, end_column). 33 | type Pos struct { 34 | Filename string `json:"filename,omitempty"` 35 | Line int64 `json:"line,omitempty"` 36 | Column int64 `json:"column,omitempty"` 37 | EndLine int64 `json:"end_line,omitempty"` 38 | EndColumn int64 `json:"end_column,omitempty"` 39 | } 40 | 41 | // Comment node. 42 | type Comment struct { 43 | Text string 44 | } 45 | -------------------------------------------------------------------------------- /pkg/kcl/api_test.go: -------------------------------------------------------------------------------- 1 | //go:build !rpc && cgo 2 | // +build !rpc,cgo 3 | 4 | package kcl 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | 10 | assert2 "github.com/stretchr/testify/assert" 11 | 12 | "kcl-lang.io/kcl-go/pkg/plugin" 13 | _ "kcl-lang.io/kcl-go/pkg/plugin/hello_plugin" 14 | ) 15 | 16 | const code = ` 17 | import kcl_plugin.hello 18 | 19 | name = "kcl" 20 | sum = hello.add(option("a"), option("b")) 21 | ` 22 | const codeWithPlugin = ` 23 | import kcl_plugin.my_plugin 24 | 25 | value1 = my_plugin.config_append({key1 = "value1"}, "key2", "value2") 26 | value2 = my_plugin.list_append([1, 2, 3], 4) 27 | ` 28 | 29 | func TestNativeRun(t *testing.T) { 30 | yaml := MustRun("main.k", WithCode(code), WithOptions("a=1", "b=2")).GetRawYamlResult() 31 | fmt.Println(yaml) 32 | } 33 | 34 | func TestNativeRunWithPlugin(t *testing.T) { 35 | plugin.RegisterPlugin(plugin.Plugin{ 36 | Name: "my_plugin", 37 | MethodMap: map[string]plugin.MethodSpec{ 38 | "config_append": { 39 | Body: func(args *plugin.MethodArgs) (*plugin.MethodResult, error) { 40 | config := args.MapArg(0) 41 | k := args.StrArg(1) 42 | v := args.StrArg(2) 43 | config[k] = v 44 | return &plugin.MethodResult{V: config}, nil 45 | }, 46 | }, 47 | "list_append": { 48 | Body: func(args *plugin.MethodArgs) (*plugin.MethodResult, error) { 49 | values := args.ListArg(0) 50 | v := args.Arg(1) 51 | values = append(values, v) 52 | return &plugin.MethodResult{V: values}, nil 53 | }, 54 | }, 55 | }, 56 | }) 57 | 58 | yaml := MustRun("main.k", WithCode(codeWithPlugin)).GetRawYamlResult() 59 | assert2.Equal(t, yaml, "value1:\n key1: value1\n key2: value2\nvalue2:\n- 1\n- 2\n- 3\n- 4") 60 | } 61 | -------------------------------------------------------------------------------- /pkg/kcl/hook.go: -------------------------------------------------------------------------------- 1 | package kcl 2 | 3 | import ( 4 | "encoding/json" 5 | "strings" 6 | 7 | "gopkg.in/yaml.v3" 8 | "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" 9 | ) 10 | 11 | var ( 12 | DefaultHooks Hooks = []Hook{ 13 | &typeAttributeHook{}, 14 | } 15 | ) 16 | 17 | type Hook interface { 18 | Do(o *Option, r *gpyrpc.ExecProgram_Result) error 19 | } 20 | 21 | type Hooks []Hook 22 | 23 | type typeAttributeHook struct{} 24 | 25 | func (t *typeAttributeHook) Do(o *Option, r *gpyrpc.ExecProgram_Result) error { 26 | // Deal the `_type` attribute 27 | if o != nil && r != nil && !o.fullTypePath && o.IncludeSchemaTypePath { 28 | return resultTypeAttributeHook(r) 29 | } 30 | return nil 31 | } 32 | 33 | func resultTypeAttributeHook(r *gpyrpc.ExecProgram_Result) error { 34 | // Modify the _type fields 35 | var data []map[string]interface{} 36 | var mapData map[string]interface{} 37 | // Unmarshal the JSON string into a Node 38 | if err := json.Unmarshal([]byte(r.JsonResult), &data); err == nil { 39 | modifyTypeList(data) 40 | marshal(r, data) 41 | return nil 42 | } 43 | 44 | if err := json.Unmarshal([]byte(r.JsonResult), &mapData); err == nil { 45 | modifyType(mapData) 46 | marshal(r, mapData) 47 | return nil 48 | } 49 | 50 | return nil 51 | } 52 | 53 | func marshal(r *gpyrpc.ExecProgram_Result, value interface{}) { 54 | // Marshal the modified Node back to YAML 55 | yamlOutput, _ := yaml.Marshal(value) 56 | // Marshal the modified Node back to JSON 57 | jsonOutput, _ := json.Marshal(&value) 58 | r.JsonResult = string(jsonOutput) 59 | r.YamlResult = string(yamlOutput) 60 | } 61 | 62 | func modifyTypeList(dataList []map[string]interface{}) { 63 | for _, data := range dataList { 64 | modifyType(data) 65 | } 66 | } 67 | 68 | func modifyType(data map[string]interface{}) { 69 | for key, value := range data { 70 | if key == "_type" { 71 | if v, ok := data[key].(string); ok { 72 | parts := strings.Split(v, ".") 73 | data[key] = parts[len(parts)-1] 74 | } 75 | continue 76 | } 77 | 78 | if nestedMap, ok := value.(map[string]interface{}); ok { 79 | modifyType(nestedMap) 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /pkg/kcl/opt_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package kcl 4 | 5 | import ( 6 | "os" 7 | "testing" 8 | ) 9 | 10 | func TestWithSettings(t *testing.T) { 11 | opt := WithSettings("../../testdata/app0/kcl.yaml") 12 | tAssert(t, opt.Err == nil, opt.JSONString()) 13 | } 14 | 15 | func TestWithKFilenames(t *testing.T) { 16 | WithKFilenames("hello.k") 17 | } 18 | 19 | func TestWithOptions(t *testing.T) { 20 | WithOptions("key1=value1", "key2=value2") 21 | } 22 | 23 | func TestWithOverrides(t *testing.T) { 24 | WithOverrides("__main__:name.field1=value1", "__main__:name.field2=value2") 25 | } 26 | 27 | func TestWithPrintOverridesAST(t *testing.T) { 28 | WithPrintOverridesAST(true) 29 | } 30 | 31 | func TestWithWorkDir(t *testing.T) { 32 | wd, _ := os.Getwd() 33 | WithWorkDir(wd) 34 | } 35 | 36 | func TestWithDisableNone(t *testing.T) { 37 | WithDisableNone(true) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/kcl/service.go: -------------------------------------------------------------------------------- 1 | package kcl 2 | 3 | import ( 4 | "kcl-lang.io/lib/go/api" 5 | "kcl-lang.io/lib/go/native" 6 | ) 7 | 8 | // Service returns the interaction interface between KCL Go SDK and KCL Rust core. 9 | func Service() api.ServiceClient { 10 | return native.NewNativeServiceClient() 11 | } 12 | -------------------------------------------------------------------------------- /pkg/kcl/testdata/complicate/appops/projectA/base/base.k: -------------------------------------------------------------------------------- 1 | import base.frontend.server 2 | 3 | demo = server.Server { 4 | name: "demo" 5 | mainContainer: {} 6 | } 7 | -------------------------------------------------------------------------------- /pkg/kcl/testdata/complicate/appops/projectA/dev/main.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/kcl/testdata/complicate/appops/projectA/dev/main.k -------------------------------------------------------------------------------- /pkg/kcl/testdata/complicate/appops/projectB/base/base.k: -------------------------------------------------------------------------------- 1 | import base.frontend.job 2 | 3 | demo = server.Job { 4 | name: "demo" 5 | mainContainer: {} 6 | } 7 | -------------------------------------------------------------------------------- /pkg/kcl/testdata/complicate/appops/projectB/dev/main.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/kcl/testdata/complicate/appops/projectB/dev/main.k -------------------------------------------------------------------------------- /pkg/kcl/testdata/complicate/base/frontend/container/container.k: -------------------------------------------------------------------------------- 1 | schema Container: 2 | name: str = "main" 3 | command?: [str] 4 | ports?: [ContainerPort] 5 | -------------------------------------------------------------------------------- /pkg/kcl/testdata/complicate/base/frontend/container/container_port.k: -------------------------------------------------------------------------------- 1 | schema ContainerPort: 2 | name?: str 3 | protocol: "TCP" | "UDP" | "SCTP" = "TCP" 4 | containerPort: int 5 | -------------------------------------------------------------------------------- /pkg/kcl/testdata/complicate/base/frontend/job/job.k: -------------------------------------------------------------------------------- 1 | import base.frontend.container 2 | 3 | schema Job: 4 | name: str 5 | type: "Job" | "CronJob" = "Job" 6 | mainContainer: container.Container 7 | sideCars?: [container.Container] 8 | -------------------------------------------------------------------------------- /pkg/kcl/testdata/complicate/base/frontend/server/server.k: -------------------------------------------------------------------------------- 1 | import base.frontend.container 2 | 3 | schema Server: 4 | name: str 5 | type: "Deployment" | "StatefulSet" | "DaemonSet" = "Deployment" 6 | mainContainer: container.Container 7 | sideCars?: [container.Container] 8 | -------------------------------------------------------------------------------- /pkg/kcl/testdata/complicate/base/render/job/job_render.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/kcl/testdata/complicate/base/render/job/job_render.k -------------------------------------------------------------------------------- /pkg/kcl/testdata/complicate/base/render/server/server_render.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/kcl/testdata/complicate/base/render/server/server_render.k -------------------------------------------------------------------------------- /pkg/kcl/testdata/complicate/kcl.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/kcl/testdata/complicate/kcl.mod -------------------------------------------------------------------------------- /pkg/kcl/testdata/get_schema_ty/aaa/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aaa" 3 | edition = "0.0.1" 4 | version = "0.0.1" 5 | 6 | -------------------------------------------------------------------------------- /pkg/kcl/testdata/get_schema_ty/aaa/main.k: -------------------------------------------------------------------------------- 1 | import bbb as b 2 | 3 | a = b.B { 4 | name: "b instance in a" 5 | } 6 | -------------------------------------------------------------------------------- /pkg/kcl/testdata/get_schema_ty/bbb/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bbb" 3 | edition = "0.0.1" 4 | version = "0.0.1" 5 | 6 | -------------------------------------------------------------------------------- /pkg/kcl/testdata/get_schema_ty/bbb/main.k: -------------------------------------------------------------------------------- 1 | schema B: 2 | name: str -------------------------------------------------------------------------------- /pkg/kcl/testing_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package kcl 4 | 5 | import ( 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | func tAssert(tb testing.TB, condition bool, args ...interface{}) { 11 | if !condition { 12 | tb.Helper() 13 | if msg := fmt.Sprint(args...); msg != "" { 14 | tb.Fatalf("Assert failed, %s", msg) 15 | } else { 16 | tb.Fatalf("Assert failed") 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pkg/kcl/version.go: -------------------------------------------------------------------------------- 1 | package kcl 2 | 3 | import ( 4 | "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" 5 | ) 6 | 7 | type VersionResult = gpyrpc.GetVersion_Result 8 | 9 | // GetVersion returns the KCL service version information. 10 | func GetVersion() (*VersionResult, error) { 11 | svc := Service() 12 | resp, err := svc.GetVersion(&gpyrpc.GetVersion_Args{}) 13 | if err != nil { 14 | return nil, err 15 | } 16 | return resp, nil 17 | } 18 | -------------------------------------------------------------------------------- /pkg/loader/options.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "kcl-lang.io/kcl-go/pkg/kcl" 5 | "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" 6 | ) 7 | 8 | type OptionHelps = []*gpyrpc.OptionHelp 9 | type ListOptionsArgs = gpyrpc.ParseProgram_Args 10 | type ListOptionsResult = gpyrpc.ListOptions_Result 11 | 12 | // ListFileOptions provides users with the ability to parse kcl program and get all option 13 | // calling information. 14 | func ListFileOptions(filename string) (OptionHelps, error) { 15 | svc := kcl.Service() 16 | resp, err := svc.ListOptions(&gpyrpc.ParseProgram_Args{ 17 | Paths: []string{filename}, 18 | }) 19 | if err != nil { 20 | return nil, err 21 | } 22 | return resp.Options, nil 23 | } 24 | 25 | // ListOptions provides users with the ability to parse kcl program and get all option 26 | // calling information. 27 | func ListOptions(args *ListOptionsArgs) (*ListOptionsResult, error) { 28 | svc := kcl.Service() 29 | return svc.ListOptions(args) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/loader/options_test.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | ) 7 | 8 | func TestLoadFileOptions(t *testing.T) { 9 | options, err := ListFileOptions(filepath.Join(".", "test_data", "options.k")) 10 | if err != nil { 11 | t.Errorf("ListFileOptions failed with string source: %v", err) 12 | } 13 | if len(options) != 3 { 14 | t.Errorf("ListFileOptions failed with string source: %v", options) 15 | } 16 | if options[2].Type != "str" { 17 | t.Errorf("ListFileOptions failed with string source: %v", options) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pkg/loader/package.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "kcl-lang.io/kcl-go/pkg/kcl" 5 | "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" 6 | ) 7 | 8 | type LoadPackageArgs = gpyrpc.LoadPackage_Args 9 | type LoadPackageResult = gpyrpc.LoadPackage_Result 10 | 11 | // LoadPackage provides users with the ability to parse KCL program and semantic model 12 | // information including symbols, types, definitions, etc. 13 | func LoadPackage(args *LoadPackageArgs) (*LoadPackageResult, error) { 14 | svc := kcl.Service() 15 | return svc.LoadPackage(args) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/loader/test_data/options.k: -------------------------------------------------------------------------------- 1 | a = option("key1") 2 | b = option("key2") 3 | config = { 4 | metadata.name = option("name", type="str") 5 | } 6 | -------------------------------------------------------------------------------- /pkg/loader/variables.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "kcl-lang.io/kcl-go/pkg/kcl" 5 | "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" 6 | ) 7 | 8 | type ListVariablesArgs = gpyrpc.ListVariables_Args 9 | type ListVariablesResult = gpyrpc.ListVariables_Result 10 | 11 | // ListVariables provides users with the ability to parse KCL program and get all variables by specs. 12 | func ListVariables(args *ListVariablesArgs) (*ListVariablesResult, error) { 13 | svc := kcl.Service() 14 | return svc.ListVariables(args) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/logger/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package logger_test 4 | 5 | import ( 6 | "log" 7 | "os" 8 | 9 | "kcl-lang.io/kcl-go/pkg/logger" 10 | ) 11 | 12 | func Example() { 13 | var logger = logger.GetLogger() 14 | 15 | logger.SetLevel("DEBUG") 16 | logger.Debug("1+1=2") 17 | logger.Info("hello") 18 | } 19 | 20 | func ExampleNewStdLogger() { 21 | var logger = logger.NewStdLogger(os.Stderr, "", "", log.Lshortfile) 22 | 23 | logger.Debug("1+1=2") 24 | logger.Info("hello") 25 | } 26 | -------------------------------------------------------------------------------- /pkg/native/client.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "kcl-lang.io/lib/go/native" 5 | ) 6 | 7 | type NativeServiceClient = native.NativeServiceClient 8 | 9 | var ( 10 | NewNativeServiceClient = native.NewNativeServiceClient 11 | ) 12 | -------------------------------------------------------------------------------- /pkg/native/testdata/1.k: -------------------------------------------------------------------------------- 1 | a = "b" 2 | -------------------------------------------------------------------------------- /pkg/native/testdata/2.k: -------------------------------------------------------------------------------- 1 | c = "d" 2 | -------------------------------------------------------------------------------- /pkg/parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | 8 | "kcl-lang.io/kcl-go/pkg/ast" 9 | "kcl-lang.io/kcl-go/pkg/kcl" 10 | "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" 11 | ) 12 | 13 | type ParseProgramArgs = gpyrpc.ParseProgram_Args 14 | type ParseProgramResult = gpyrpc.ParseProgram_Result 15 | 16 | // ParseFileASTJson parses the source code from the specified file or Reader 17 | // and returns the JSON representation of the Abstract Syntax Tree (AST). 18 | // The source code can be provided directly as a string or []byte, 19 | // or indirectly via a filename or an io.Reader. 20 | // If src is nil, the function reads the content from the provided filename. 21 | func ParseFileASTJson(filename string, src interface{}) (result string, err error) { 22 | var code string 23 | if src != nil { 24 | switch src := src.(type) { 25 | case []byte: 26 | code = string(src) 27 | case string: 28 | code = src 29 | case io.Reader: 30 | d, err := io.ReadAll(src) 31 | if err != nil { 32 | return "", err 33 | } 34 | code = string(d) 35 | default: 36 | return "", fmt.Errorf("unsupported src type: %T", src) 37 | } 38 | } 39 | svc := kcl.Service() 40 | resp, err := svc.ParseFile(&gpyrpc.ParseFile_Args{ 41 | Path: filename, 42 | Source: code, 43 | }) 44 | if err != nil { 45 | return "", err 46 | } 47 | return resp.AstJson, nil 48 | } 49 | 50 | // ParseFile parses the source code from the specified file or Reader 51 | // and returns the Go structure representation of the Abstract Syntax 52 | // Tree (AST). The source code can be provided directly as a string or 53 | // []byte, or indirectly via a filename or an io.Reader. If src is nil, 54 | // the function reads the content from the provided filename. 55 | func ParseFile(filename string, src interface{}) (m *ast.Module, err error) { 56 | astJson, err := ParseFileASTJson(filename, src) 57 | if err != nil { 58 | return nil, err 59 | } 60 | m = ast.NewModule() 61 | err = json.Unmarshal([]byte(astJson), m) 62 | if err != nil { 63 | return nil, err 64 | } 65 | return 66 | } 67 | 68 | // Parse KCL program with entry files and return the AST JSON string. 69 | func ParseProgram(args *ParseProgramArgs) (*ParseProgramResult, error) { 70 | svc := kcl.Service() 71 | return svc.ParseProgram(args) 72 | } 73 | -------------------------------------------------------------------------------- /pkg/plugin/api.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The KCL Authors. All rights reserved. 2 | 3 | //go:build cgo 4 | // +build cgo 5 | 6 | package plugin 7 | 8 | import ( 9 | "kcl-lang.io/lib/go/plugin" 10 | ) 11 | 12 | var ( 13 | // Register register a new kcl plugin. 14 | RegisterPlugin = plugin.RegisterPlugin 15 | // GetPlugin get plugin object by name. 16 | GetPlugin = plugin.GetPlugin 17 | // GetMethodSpec get plugin method by name. 18 | GetMethodSpec = plugin.GetMethodSpec 19 | // ResetPlugin reset all kcl plugin state. 20 | ResetPlugin = plugin.ResetPlugin 21 | ) 22 | -------------------------------------------------------------------------------- /pkg/plugin/api_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The KCL Authors. All rights reserved. 2 | 3 | //go:build cgo 4 | // +build cgo 5 | 6 | package plugin 7 | 8 | import ( 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func init() { 14 | RegisterPlugin(Plugin{ 15 | Name: "strings", 16 | ResetFunc: func() {}, 17 | MethodMap: map[string]MethodSpec{ 18 | "join": { 19 | Type: &MethodType{}, 20 | Body: func(args *MethodArgs) (*MethodResult, error) { 21 | var ss []string 22 | for i := range args.Args { 23 | ss = append(ss, args.StrArg(i)) 24 | } 25 | return &MethodResult{V: strings.Join(ss, ".")}, nil 26 | }, 27 | }, 28 | "panic": { 29 | Type: &MethodType{}, 30 | Body: func(args *MethodArgs) (*MethodResult, error) { 31 | panic(args.Args) 32 | }, 33 | }, 34 | }, 35 | }) 36 | } 37 | 38 | func TestPlugin_strings_join(t *testing.T) { 39 | result_json := Invoke("kcl_plugin.strings.join", []interface{}{"KCL", "KCL", 123}, nil) 40 | if result_json != `"KCL.KCL.123"` { 41 | t.Fatal(result_json) 42 | } 43 | } 44 | 45 | func TestPlugin_strings_panic(t *testing.T) { 46 | result_json := Invoke("kcl_plugin.strings.panic", []interface{}{"KCL", "KCL", 123}, nil) 47 | if result_json != `{"__kcl_PanicInfo__":"[KCL KCL 123]"}` { 48 | t.Fatal(result_json) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pkg/plugin/error.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The KCL Authors. All rights reserved. 2 | 3 | //go:build cgo 4 | // +build cgo 5 | 6 | package plugin 7 | 8 | import ( 9 | "kcl-lang.io/lib/go/plugin" 10 | ) 11 | 12 | type PanicInfo = plugin.PanicInfo 13 | 14 | type BacktraceFrame = plugin.BacktraceFrame 15 | 16 | var ( 17 | JSONError = plugin.JSONError 18 | ) 19 | -------------------------------------------------------------------------------- /pkg/plugin/hello_plugin/api.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The KCL Authors. All rights reserved. 2 | 3 | //go:build cgo 4 | // +build cgo 5 | 6 | package hello_plugin 7 | 8 | import ( 9 | "kcl-lang.io/kcl-go/pkg/plugin" 10 | ) 11 | 12 | func init() { 13 | plugin.RegisterPlugin(plugin.Plugin{ 14 | Name: "hello", 15 | MethodMap: map[string]plugin.MethodSpec{ 16 | "add": { 17 | Body: func(args *plugin.MethodArgs) (*plugin.MethodResult, error) { 18 | v := args.IntArg(0) + args.IntArg(1) 19 | return &plugin.MethodResult{V: v}, nil 20 | }, 21 | }, 22 | }, 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/plugin/hello_plugin/api_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The KCL Authors. All rights reserved. 2 | 3 | //go:build cgo 4 | // +build cgo 5 | 6 | package hello_plugin 7 | 8 | import ( 9 | "testing" 10 | 11 | "kcl-lang.io/kcl-go/pkg/plugin" 12 | ) 13 | 14 | func TestPlugin_add(t *testing.T) { 15 | result_json := plugin.Invoke("kcl_plugin.hello.add", []interface{}{111, 22}, nil) 16 | if result_json != "133" { 17 | t.Fatal(result_json) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pkg/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The KCL Authors. All rights reserved. 2 | 3 | //go:build cgo 4 | // +build cgo 5 | 6 | package plugin 7 | 8 | import ( 9 | "kcl-lang.io/lib/go/plugin" 10 | ) 11 | 12 | var ( 13 | GetInvokeJsonProxyPtr = plugin.GetInvokeJsonProxyPtr 14 | Invoke = plugin.Invoke 15 | InvokeJson = plugin.InvokeJson 16 | ) 17 | -------------------------------------------------------------------------------- /pkg/plugin/spec.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | //go:build cgo 4 | // +build cgo 5 | 6 | package plugin 7 | 8 | import ( 9 | "kcl-lang.io/lib/go/plugin" 10 | ) 11 | 12 | // Plugin represents a KCL Plugin with metadata and methods. 13 | // It contains the plugin name, version, a reset function, and a map of methods. 14 | type Plugin = plugin.Plugin 15 | 16 | // MethodSpec defines the specification for a KCL Plugin method. 17 | // It includes the method type and the body function which executes the method logic. 18 | type MethodSpec = plugin.MethodSpec 19 | 20 | // MethodType describes the type of a KCL Plugin method's arguments, keyword arguments, and result. 21 | // It specifies the types of positional arguments, keyword arguments, and the result type. 22 | type MethodType = plugin.MethodType 23 | 24 | // MethodArgs represents the arguments passed to a KCL Plugin method. 25 | // It includes a list of positional arguments and a map of keyword arguments. 26 | type MethodArgs = plugin.MethodArgs 27 | 28 | // MethodResult represents the result returned from a KCL Plugin method. 29 | // It holds the value of the result. 30 | type MethodResult = plugin.MethodResult 31 | 32 | var ( 33 | // ParseMethodArgs parses JSON strings for positional and keyword arguments 34 | // and returns a MethodArgs object. 35 | // args_json: JSON string of positional arguments 36 | // kwargs_json: JSON string of keyword arguments 37 | ParseMethodArgs = plugin.ParseMethodArgs 38 | ) 39 | -------------------------------------------------------------------------------- /pkg/service/kclvm_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import "kcl-lang.io/lib/go/api" 4 | 5 | // KCL service client interface. 6 | // Deprecated: use `ServiceClient` at `kcl-lang.io/lib/go/api` 7 | type KclvmService interface { 8 | api.ServiceClient 9 | } 10 | -------------------------------------------------------------------------------- /pkg/settings/a_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package settings 4 | 5 | import ( 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestLoadFile(t *testing.T) { 11 | f, err := LoadFile("../../testdata/app0/kcl.yaml", nil) 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | 16 | f.To_ExecProgram_Args() 17 | _ = f 18 | } 19 | 20 | func _TestLoadFile_testFormat(t *testing.T) { 21 | // kcl_options: -D key1=val -D key2=2 -D key3=4.4 -D key4=[1,2,3] -D key5={'key':'value'} -S app.value 22 | f, err := LoadFile("../../../test/grammar/option/complex_type_option/settings.yaml", nil) 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | 27 | f.To_ExecProgram_Args() 28 | _ = f 29 | } 30 | 31 | func TestLoadFile_xtype(t *testing.T) { 32 | _, err := LoadFile("settings.yaml", "") 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | _, err = LoadFile("settings.yaml", strings.NewReader("")) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | _, err = LoadFile("settings.yaml", []byte("")) 41 | if err != nil { 42 | t.Fatal(err) 43 | } 44 | } 45 | 46 | func tAssert(t *testing.T, ok bool, a ...interface{}) { 47 | if !ok { 48 | t.Helper() 49 | t.Fatal(a...) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /pkg/settings/a_test_unix.go: -------------------------------------------------------------------------------- 1 | //go:build linux || darwin 2 | // +build linux darwin 3 | 4 | package settings 5 | 6 | import ( 7 | "os" 8 | "path/filepath" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func TestLoadFile_to_ExecProgram_Args(t *testing.T) { 14 | s := ` 15 | kcl_cli_configs: 16 | file: 17 | - /abs_file.k 18 | - sub_main.k 19 | - ${KCL_MOD}/file2.k 20 | - ../../base/base.k 21 | disable_none: false 22 | package_maps: 23 | k8s: ../vendor/k8s 24 | ` 25 | f, err := LoadFile("./sub/settings.yaml", []byte(s)) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | 30 | pwd, _ := os.Getwd() 31 | x := f.To_ExecProgram_Args() 32 | 33 | tAssertEQ(t, len(x.KFilenameList), 4) 34 | tAssertEQ(t, x.KFilenameList[0], "/abs_file.k") 35 | tAssertEQ(t, x.KFilenameList[1], filepath.Join(pwd, "sub", "sub_main.k")) 36 | tAssertEQ(t, x.KFilenameList[2], filepath.Join(pwd, "file2.k")) 37 | tAssertEQ(t, x.KFilenameList[3], filepath.Join(pwd, "..", "base", "base.k")) 38 | tAssertEQ(t, x.ExternalPkgs[1].PkgName, "k8s") 39 | tAssertEQ(t, x.ExternalPkgs[1].PkgPath, "../vendor/k8s") 40 | } 41 | 42 | func tAssertEQ(t *testing.T, x, y interface{}) { 43 | if !reflect.DeepEqual(x, y) { 44 | t.Helper() 45 | t.Fatalf("not equal:\n x = %v\n y = %v\n", x, y) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /pkg/settings/a_test_windows.go: -------------------------------------------------------------------------------- 1 | package settings 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func TestLoadFile_to_ExecProgram_Args(t *testing.T) { 11 | s := ` 12 | kcl_cli_configs: 13 | file: 14 | - C:/abs_file.k 15 | - sub_main.k 16 | - ${KCL_MOD}/file2.k 17 | - ../../base/base.k 18 | disable_none: false 19 | ` 20 | f, err := LoadFile("./sub/settings.yaml", []byte(s)) 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | 25 | pwd, _ := os.Getwd() 26 | x := f.To_ExecProgram_Args() 27 | 28 | tAssertEQ(t, len(x.KFilenameList), 4) 29 | tAssertEQ(t, x.KFilenameList[0], "C:/abs_file.k") 30 | tAssertEQ(t, x.KFilenameList[1], filepath.Join(pwd, "sub", "sub_main.k")) 31 | tAssertEQ(t, x.KFilenameList[2], filepath.Join(pwd, "file2.k")) 32 | tAssertEQ(t, x.KFilenameList[3], filepath.Join(pwd, "..", "base", "base.k")) 33 | } 34 | 35 | func tAssertEQ(t *testing.T, x, y interface{}) { 36 | if !reflect.DeepEqual(x, y) { 37 | t.Helper() 38 | t.Fatalf("not equal:\n x = %v\n y = %v\n", x, y) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pkg/settings/kcl.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/settings/kcl.mod -------------------------------------------------------------------------------- /pkg/source/source.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | ) 8 | 9 | func ReadSource(filename string, src interface{}) (data []byte, err error) { 10 | if src == nil { 11 | src, err = os.ReadFile(filename) 12 | if err != nil { 13 | return 14 | } 15 | } 16 | switch src := src.(type) { 17 | case []byte: 18 | return src, nil 19 | case string: 20 | return []byte(src), nil 21 | case io.Reader: 22 | d, err := io.ReadAll(src) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return d, nil 27 | default: 28 | return nil, fmt.Errorf("unsupported src type: %T", src) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pkg/tools/format/kformat.go: -------------------------------------------------------------------------------- 1 | package format 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | 7 | "kcl-lang.io/kcl-go/pkg/kcl" 8 | "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" 9 | ) 10 | 11 | func FormatCode(code interface{}) ([]byte, error) { 12 | var codeStr string 13 | switch code := code.(type) { 14 | case []byte: 15 | codeStr = string(code) 16 | case string: 17 | codeStr = code 18 | case io.Reader: 19 | var p []byte 20 | _, err := code.Read(p) 21 | if err != nil { 22 | return nil, err 23 | } 24 | codeStr = string(p) 25 | default: 26 | return nil, errors.New("unsupported source code format. valid formats: []byte, string, io.Reader") 27 | } 28 | 29 | svc := kcl.Service() 30 | resp, err := svc.FormatCode(&gpyrpc.FormatCode_Args{ 31 | Source: codeStr, 32 | }) 33 | if err != nil { 34 | return nil, err 35 | } 36 | return resp.Formatted, nil 37 | } 38 | 39 | func FormatPath(path string) (changedPaths []string, err error) { 40 | svc := kcl.Service() 41 | resp, err := svc.FormatPath(&gpyrpc.FormatPath_Args{ 42 | Path: path, 43 | }) 44 | if err != nil { 45 | return nil, err 46 | } 47 | return resp.ChangedPaths, nil 48 | } 49 | -------------------------------------------------------------------------------- /pkg/tools/format/testdata/success/hello.formatted: -------------------------------------------------------------------------------- 1 | a = a + 1 2 | -------------------------------------------------------------------------------- /pkg/tools/format/testdata/success/hello.k: -------------------------------------------------------------------------------- 1 | a=a+1 2 | 3 | 4 | -------------------------------------------------------------------------------- /pkg/tools/format/testdata/success/person.formatted: -------------------------------------------------------------------------------- 1 | schema Person: 2 | name: str 3 | 4 | -------------------------------------------------------------------------------- /pkg/tools/format/testdata/success/person.k: -------------------------------------------------------------------------------- 1 | schema Person: 2 | name:str 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /pkg/tools/gen/genkcl.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "path/filepath" 7 | "strings" 8 | 9 | "kcl-lang.io/kcl-go/pkg/source" 10 | ) 11 | 12 | type GenKclOptions struct { 13 | Mode Mode 14 | CastingOption CastingOption 15 | UseIntegersForNumbers bool 16 | } 17 | 18 | // Mode is the mode of kcl schema code generation. 19 | type Mode int 20 | 21 | const ( 22 | ModeAuto Mode = iota 23 | ModeGoStruct 24 | ModeJsonSchema 25 | ModeTerraformSchema 26 | ModeJson 27 | ModeYaml 28 | ModeToml 29 | ModeProto 30 | ModeTextProto 31 | ) 32 | 33 | type kclGenerator struct { 34 | opts *GenKclOptions 35 | } 36 | 37 | // GenKcl translate other formats to kcl schema code. Now support go struct and json schema. 38 | func GenKcl(w io.Writer, filename string, src interface{}, opts *GenKclOptions) error { 39 | return newKclGenerator(opts).GenSchema(w, filename, src) 40 | } 41 | 42 | func newKclGenerator(opts *GenKclOptions) *kclGenerator { 43 | if opts == nil { 44 | opts = new(GenKclOptions) 45 | } 46 | return &kclGenerator{ 47 | opts: opts, 48 | } 49 | } 50 | 51 | func (k *kclGenerator) GenSchema(w io.Writer, filename string, src interface{}) error { 52 | if k.opts.Mode == ModeAuto { 53 | code, err := source.ReadSource(filename, src) 54 | if err != nil { 55 | return err 56 | } 57 | codeStr := string(code) 58 | switch filepath.Ext(filename) { 59 | case ".json": 60 | switch { 61 | case strings.Contains(codeStr, "$schema"): 62 | k.opts.Mode = ModeJsonSchema 63 | case strings.Contains(codeStr, "\"provider_schemas\""): 64 | k.opts.Mode = ModeTerraformSchema 65 | default: 66 | k.opts.Mode = ModeJson 67 | } 68 | case ".yaml", "yml": 69 | k.opts.Mode = ModeYaml 70 | case ".toml": 71 | k.opts.Mode = ModeToml 72 | case ".go": 73 | k.opts.Mode = ModeGoStruct 74 | case ".proto": 75 | k.opts.Mode = ModeProto 76 | case ".textproto": 77 | k.opts.Mode = ModeTextProto 78 | default: 79 | return errors.New("failed to detect mode") 80 | } 81 | } 82 | 83 | switch k.opts.Mode { 84 | case ModeGoStruct: 85 | return k.genSchemaFromGoStruct(w, filename, src) 86 | case ModeJsonSchema: 87 | return k.genSchemaFromJsonSchema(w, filename, src) 88 | case ModeTerraformSchema: 89 | return k.genSchemaFromTerraformSchema(w, filename, src) 90 | case ModeJson: 91 | return k.genKclFromJsonData(w, filename, src) 92 | case ModeYaml: 93 | return k.genKclFromYaml(w, filename, src) 94 | case ModeToml: 95 | return k.genKclFromToml(w, filename, src) 96 | case ModeProto: 97 | return k.genKclFromProto(w, filename, src) 98 | default: 99 | return errors.New("unknown mode") 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /pkg/tools/gen/genkcl_json.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/goccy/go-yaml" 7 | "kcl-lang.io/kcl-go/pkg/source" 8 | ) 9 | 10 | func (k *kclGenerator) genKclFromJsonData(w io.Writer, filename string, src interface{}) error { 11 | code, err := source.ReadSource(filename, src) 12 | if err != nil { 13 | return err 14 | } 15 | 16 | // as yaml can be viewed as a superset of json, 17 | // we can handle json data like yaml. 18 | yamlData := &yaml.MapSlice{} 19 | if err = yaml.UnmarshalWithOptions(code, yamlData, yaml.UseOrderedMap(), yaml.UseJSONUnmarshaler()); err != nil { 20 | return err 21 | } 22 | 23 | // convert to kcl 24 | result := convertKclFromYaml(yamlData) 25 | 26 | // generate kcl code 27 | return k.genKcl(w, kclFile{Config: []config{ 28 | {Data: result}, 29 | }}) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/tools/gen/genkcl_toml.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "io" 5 | "reflect" 6 | "strings" 7 | 8 | "github.com/goccy/go-yaml" 9 | "kcl-lang.io/kcl-go/pkg/3rdparty/toml" 10 | "kcl-lang.io/kcl-go/pkg/source" 11 | ) 12 | 13 | func (k *kclGenerator) genKclFromToml(w io.Writer, filename string, src interface{}) error { 14 | code, err := source.ReadSource(filename, src) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | // as yaml can be viewed as a superset of json, 20 | // we can handle json data like yaml. 21 | yamlData := &yaml.MapSlice{} 22 | mappingData := make(map[string]any) 23 | meta, err := toml.Decode(string(code), &mappingData) 24 | if err != nil { 25 | return err 26 | } 27 | for _, key := range meta.Keys() { 28 | key := key.String() 29 | mapSliceSet(yamlData, key, mapGet(mappingData, key)) 30 | } 31 | // convert to kcl 32 | result := convertKclFromYaml(yamlData) 33 | 34 | // generate kcl code 35 | return k.genKcl(w, kclFile{Config: []config{ 36 | {Data: result}, 37 | }}) 38 | } 39 | 40 | func mapGet(m interface{}, key string) interface{} { 41 | keys := strings.Split(key, ".") 42 | value := reflect.ValueOf(m) 43 | if value.Kind() != reflect.Map { 44 | return nil 45 | } 46 | for _, k := range keys { 47 | elem := value.MapIndex(reflect.ValueOf(k)) 48 | if !elem.IsValid() { 49 | return nil 50 | } 51 | value = elem.Elem() 52 | if value.Kind() == reflect.Interface { 53 | value = value.Elem() 54 | } 55 | } 56 | return value.Interface() 57 | } 58 | 59 | func mapSliceSet(m *yaml.MapSlice, key string, value interface{}) { 60 | keys := strings.Split(key, ".") 61 | currentMap := m 62 | for i, k := range keys { 63 | found := false 64 | for j, item := range *currentMap { 65 | if kkey, ok := item.Key.(string); ok && kkey == k { 66 | found = true 67 | if i == len(keys)-1 { 68 | (*currentMap)[j] = yaml.MapItem{Key: k, Value: value} 69 | return 70 | } else { 71 | if mp, ok := item.Value.(yaml.MapSlice); ok { 72 | currentMap = &mp 73 | break 74 | } else if mp, ok := item.Value.(*yaml.MapSlice); ok { 75 | currentMap = mp 76 | break 77 | } 78 | } 79 | } 80 | } 81 | if !found { 82 | if i == len(keys)-1 { 83 | if reflect.TypeOf(value).Kind() == reflect.Map { 84 | newMap := make(yaml.MapSlice, 0) 85 | *currentMap = append(*currentMap, yaml.MapItem{Key: k, Value: &newMap}) 86 | currentMap = &newMap 87 | } else { 88 | *currentMap = append(*currentMap, yaml.MapItem{Key: k, Value: value}) 89 | } 90 | } else { 91 | newMap := make(yaml.MapSlice, 0) 92 | *currentMap = append(*currentMap, yaml.MapItem{Key: k, Value: &newMap}) 93 | currentMap = &newMap 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /pkg/tools/gen/genpb_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The KCL Authors. All rights reserved. 2 | 3 | package gen_test 4 | 5 | import ( 6 | "fmt" 7 | "log" 8 | "testing" 9 | 10 | "kcl-lang.io/kcl-go/pkg/tools/gen" 11 | ) 12 | 13 | var _ = gen.GenProto 14 | 15 | func TestGenProto(t *testing.T) { 16 | const code = ` 17 | import units 18 | 19 | #kclvm/genpb: option go_package = kcl_gen/_/hello 20 | #kclvm/genpb: option pb_package = kcl_gen._.hello 21 | 22 | type NumberMultiplier = units.NumberMultiplier 23 | 24 | schema Person: 25 | """Person Example""" 26 | name: str = "kcl" 27 | age: int = 2 28 | friends?: [str] = None 29 | movies?: {str: Movie} = None 30 | 31 | schema Movie: 32 | desc: str = "" 33 | size: NumberMultiplier = 2M 34 | kind?: "Superhero" | "War" | "Unknown" 35 | unknown1?: int | str = None 36 | unknown2?: any = None 37 | ` 38 | 39 | pbCode, err := gen.GenProto("hello.k", code, nil) 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | fmt.Println(pbCode) 44 | 45 | // Output: 46 | // syntax = "proto3"; 47 | // 48 | // package kcl_gen._.hello; 49 | // 50 | // option go_package = "kcl_gen/_/hello"; 51 | // 52 | // import "google/protobuf/any.proto"; 53 | // 54 | // // Person Example 55 | // message Person { 56 | // string name = 1; // kcl-type: str 57 | // int64 age = 2; // kcl-type: int 58 | // repeated string friends = 3; // kcl-type: [str] 59 | // map movies = 4; // kcl-type: {str:Movie} 60 | // } 61 | // 62 | // message Movie { 63 | // string desc = 1; // kcl-type: str 64 | // int64 size = 2; // kcl-type: units.NumberMultiplier 65 | // string kind = 3; // kcl-type: "Superhero"|"War"|"Unknown" 66 | // google.protobuf.Any unknown1 = 4; // kcl-type: int|str 67 | // google.protobuf.Any unknown2 = 5; // kcl-type: any 68 | // } 69 | } 70 | -------------------------------------------------------------------------------- /pkg/tools/gen/gentoml.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "github.com/goccy/go-yaml" 5 | "kcl-lang.io/kcl-go/pkg/3rdparty/toml" 6 | ) 7 | 8 | // Marshal returns a TOML representation of the Go value. 9 | func MarshalTOML(data *yaml.MapSlice) ([]byte, error) { 10 | return toml.Marshal(data) 11 | } 12 | 13 | // MarshalYamlMapSliceToTOML convert an ordered yaml data to an ordered toml 14 | func MarshalYamlMapSliceToTOML(data *yaml.MapSlice) ([]byte, error) { 15 | return toml.Marshal(data) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/tools/gen/interface.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "io" 5 | 6 | pb "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" 7 | ) 8 | 9 | type Generator interface { 10 | GenFromSource(w io.Writer, filename string, src interface{}) error 11 | GenFromTypes(w io.Writer, types ...*pb.KclType) 12 | GenSchema(w io.Writer, typ *pb.KclType) 13 | GetTypeName(typ *pb.KclType) string 14 | } 15 | -------------------------------------------------------------------------------- /pkg/tools/gen/templates/doc/packageDoc.gotmpl: -------------------------------------------------------------------------------- 1 | {{- define "packageDoc" -}} 2 | 3 | {{- $Data := .Data -}} 4 | {{- $EscapeHtml := .EscapeHtml -}} 5 | # {{if ne $Data.Name ""}}{{$Data.Name}}{{else}}main{{end}}{{/* the package name should not be empty, issue: https://github.com/kcl-lang/kpm/issues/171 */}} 6 | {{if ne $Data.Description ""}} 7 | ## Overview 8 | 9 | {{escapeHtml $Data.Description .EscapeHtml}} 10 | {{end}} 11 | ## Index 12 | 13 | {{ indexContent $Data }} 14 | {{- if or $Data.SchemaList $Data.SubPackageList}} 15 | ## Schemas 16 | 17 | {{template "schemaListDoc" (arr $Data $EscapeHtml) }} 18 | {{- end -}} 19 | 20 | {{end}} 21 | -------------------------------------------------------------------------------- /pkg/tools/gen/templates/doc/schemaDoc.gotmpl: -------------------------------------------------------------------------------- 1 | {{- define "schemaDoc" -}} 2 | 3 | {{- $Data := index . 0 -}} 4 | {{- $EscapeHtml := index . 1 -}} 5 | ### {{$Data.KclExtensions.XKclModelType.Type}} 6 | {{if ne $Data.Description ""}} 7 | {{escapeHtml $Data.Description $EscapeHtml}} 8 | {{end}} 9 | #### Attributes 10 | 11 | | name | type | description | default value | 12 | | --- | --- | --- | --- | 13 | {{range $name, $property := $Data.Properties}}|**{{$name}}**{{if containsString $Data.Required $name }} `required`{{end}}{{if $property.ReadOnly}} `readOnly`{{end}}|{{kclType $property $EscapeHtml}}|{{if ne $property.Description ""}}{{escapeHtml $property.Description $EscapeHtml}}{{end}}|{{escapeHtml $property.Default $EscapeHtml}}| 14 | {{end}}{{if ne (len $Data.Examples) 0}}#### Examples 15 | 16 | {{range $name, $example := $Data.Examples}}{{if $example.Summary}}**$example.Summary** 17 | {{end}}{{if $example.Description}}$example.Description 18 | {{end}}{{if $example.Value}}``` 19 | {{$example.Value}} 20 | ```{{end}} 21 | {{end}}{{if ne (len $Data.ReferencedBy) 0}}#### Referenced By 22 | {{range $index, $schema := $Data.ReferencedBy}} 23 | - {{$schema}} 24 | {{end}} 25 | {{end}} 26 | {{end -}} 27 | {{- end -}} 28 | -------------------------------------------------------------------------------- /pkg/tools/gen/templates/doc/schemaListDoc.gotmpl: -------------------------------------------------------------------------------- 1 | {{- define "schemaListDoc" -}} 2 | 3 | {{- $Data := index . 0 -}} 4 | {{- $EscapeHtml := index . 1 -}} 5 | {{- if $Data.SchemaList }}{{range $i, $schema := $Data.SchemaList }}{{template "schemaDoc" (arr $schema $EscapeHtml) }} 6 | {{- end -}} 7 | {{- end -}} 8 | 9 | {{- if $Data.SubPackageList}}{{range $i, $pkg := $Data.SubPackageList }}{{template "schemaListDoc" (arr $pkg $EscapeHtml) }} 10 | {{- end -}} 11 | {{- end -}} 12 | 13 | {{- end -}} 14 | -------------------------------------------------------------------------------- /pkg/tools/gen/templates/kcl/config.gotmpl: -------------------------------------------------------------------------------- 1 | {{- if .Var }}{{ formatName .Var }}{{- if .IsUnion }}{{ ": " }}{{- else }}{{ " = " }}{{- end }}{{- end }}{{- if .Name }}{{ .Name }}{{ " " }}{{- end }}{{- if .Data }}{{- "{\n" }} 2 | {{- range .Data -}} 3 | {{- indentLines (include "data" .) " " }} 4 | {{- end -}} 5 | {{- "}" }}{{- else }}{}{{- end }} 6 | -------------------------------------------------------------------------------- /pkg/tools/gen/templates/kcl/data.gotmpl: -------------------------------------------------------------------------------- 1 | {{ formatName .Key }}{{ " = " }} 2 | {{- if isKclConfig .Value }}{{ template "config" .Value -}} 3 | {{- else if isKclData .Value }} 4 | {{- "{\n" }} 5 | {{- range .Value -}} 6 | {{- indentLines (include "data" .) " " }} 7 | {{- end }} 8 | {{- "}\n" }} 9 | {{- else if isArray .Value }} 10 | {{- "[\n" }} 11 | {{- range .Value -}} 12 | {{- if isKclData . }} 13 | {{- " {\n" }} 14 | {{- range . -}} 15 | {{- indentLines (include "data" .) " " }} 16 | {{- end }} 17 | {{- " }\n" }} 18 | {{- else if isKclConfig . }}{{ indentLines (include "config" .) " " -}} 19 | {{- else }} 20 | {{- indentLines (formatValue .) " " }}{{- "\n" }} 21 | {{- end }} 22 | {{- end }} 23 | {{- "]\n" }} 24 | {{- else }} 25 | {{- formatValue .Value }}{{- "\n" }} 26 | {{- end }} -------------------------------------------------------------------------------- /pkg/tools/gen/templates/kcl/document.gotmpl: -------------------------------------------------------------------------------- 1 | {{- if .Description }} 2 | {{- indentLines .Description " " }} 3 | {{- else }} 4 | {{- indentLines .Name " " }} 5 | {{- end }} 6 | 7 | {{- if .Properties }} 8 | 9 | Attributes 10 | ---------- 11 | {{- range .Properties }} 12 | {{ formatName .Name }} : {{ formatType .Type }}, {{ if .Required }}required{{ else }}optional{{ end }} 13 | {{- if .HasDefault }}, default is {{ formatValueWithEscape .DefaultValue false}}{{ end }} 14 | {{- if .Description }}{{ "\n" }}{{ indentLines .Description " " }}{{ end }} 15 | {{- end -}} 16 | 17 | {{- end -}} 18 | -------------------------------------------------------------------------------- /pkg/tools/gen/templates/kcl/header.gotmpl: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | {{- if .Imports }} 7 | {{- range .Imports }} 8 | import {{ .PkgPath }}{{- if .Alias }}{{ " as " }}{{ .Alias }}{{- end }} 9 | {{- end }} 10 | {{- end -}} 11 | -------------------------------------------------------------------------------- /pkg/tools/gen/templates/kcl/index.gotmpl: -------------------------------------------------------------------------------- 1 | {{ template "header" . }} 2 | 3 | {{ range .Schemas -}} 4 | {{ template "schema" . }} 5 | {{ end -}} 6 | 7 | {{ range .Data -}} 8 | {{ template "data" . -}} 9 | {{ end -}} 10 | 11 | {{ range .Config -}} 12 | {{ template "config" . -}} 13 | {{ end -}} 14 | {{- if .ExtraCode }}{{ .ExtraCode }}{{- end }} -------------------------------------------------------------------------------- /pkg/tools/gen/templates/kcl/schema.gotmpl: -------------------------------------------------------------------------------- 1 | schema {{ formatName .Name }}: 2 | r"""{{- "\n" }} 3 | {{- template "document" . }} 4 | """{{- "\n" }} 5 | 6 | {{- range .Properties }} 7 | {{ formatName .Name }}{{ if not .Required }}?{{ end }}: {{ formatType .Type }}{{ if .HasDefault }} = {{ formatValue .DefaultValue }}{{ end }} 8 | {{- end }} 9 | {{- if .HasIndexSignature }}{{- "\n [" }} 10 | {{- if .IndexSignature.Alias }}{{ formatName .IndexSignature.Alias }}: {{ else }}...{{ end }} 11 | {{- "str]: " }}{{ formatType .IndexSignature.Type }} 12 | {{- end }} 13 | 14 | {{- if .Validations }} 15 | 16 | check: 17 | {{- template "validator" .Validations }} 18 | {{- end }} 19 | -------------------------------------------------------------------------------- /pkg/tools/gen/templates/kcl/validator.gotmpl: -------------------------------------------------------------------------------- 1 | {{- range . -}} 2 | {{- if .Maximum }} 3 | {{ formatName .Name }} {{ if .ExclusiveMaximum }}<{{ else }}<={{ end }} {{ .Maximum }}{{ if not .Required }} if {{ formatName .Name }}{{ end }} 4 | {{- end }} 5 | {{- if .Minimum }} 6 | {{ formatName .Name }} {{ if .ExclusiveMinimum }}>{{ else }}>={{ end }} {{ .Minimum }}{{ if not .Required }} if {{ formatName .Name }}{{ end }} 7 | {{- end }} 8 | {{- if .MaxLength }} 9 | len({{ formatName .Name }}) <= {{ .MaxLength }}{{ if not .Required }} if {{ formatName .Name }}{{ end }} 10 | {{- end }} 11 | {{- if .MinLength }} 12 | len({{ formatName .Name }}) >= {{ .MinLength }}{{ if not .Required }} if {{ formatName .Name }}{{ end }} 13 | {{- end }} 14 | {{- if .Regex }} 15 | regex.match({{ formatName .Name }}, r"{{ .Regex }}"){{ if not .Required }} if {{ formatName .Name }}{{ end }} 16 | {{- end }} 17 | {{- if .MultiplyOf }} 18 | multiplyof({{ formatName .Name }}, {{ .MultiplyOf }}){{ if not .Required }} if {{ formatName .Name }}{{ end }} 19 | {{- end }} 20 | {{- if .Unique }} 21 | isunique({{ formatName .Name }}){{ if not .Required }} if {{ formatName .Name }}{{ end }} 22 | {{- end }} 23 | {{- if .AllOf }} 24 | {{- template "validator" .AllOf }} 25 | {{- end }} 26 | {{- end -}} 27 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/k8s/apps/deployment.k: -------------------------------------------------------------------------------- 1 | import k8s.core 2 | 3 | schema Deployment: 4 | metadata: str 5 | podSpec: core.PodSpec 6 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/k8s/core/podSpec.k: -------------------------------------------------------------------------------- 1 | schema PodSpec: 2 | image: str 3 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/k8s/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/k8s/md/main.md: -------------------------------------------------------------------------------- 1 | # main 2 | 3 | ## Index 4 | 5 | - apps 6 | - [Deployment](#deployment) 7 | - core 8 | - [PodSpec](#podspec) 9 | 10 | ## Schemas 11 | 12 | ### Deployment 13 | 14 | #### Attributes 15 | 16 | | name | type | description | default value | 17 | | --- | --- | --- | --- | 18 | |**metadata** `required`|str||| 19 | |**podSpec** `required`|any||| 20 | ### PodSpec 21 | 22 | #### Attributes 23 | 24 | | name | type | description | default value | 25 | | --- | --- | --- | --- | 26 | |**image** `required`|str||| 27 | 28 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/pkg/container.k: -------------------------------------------------------------------------------- 1 | schema Container: 2 | """ 3 | Container is the common user interface for long-running services. 4 | 5 | Attributes 6 | ---------- 7 | name: str, required 8 | The name of the long-running container. 9 | """ 10 | name: str 11 | image: str 12 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/pkg/k8s/core/podSpec.k: -------------------------------------------------------------------------------- 1 | schema PodSpec: 2 | image: str 3 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/pkg/k8s/deployment.k: -------------------------------------------------------------------------------- 1 | import k8s.core 2 | 3 | schema Deployment: 4 | metadata: str 5 | podSpec: core.PodSpec 6 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/pkg/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/pkg/server.k: -------------------------------------------------------------------------------- 1 | import k8s.deployment 2 | import units 3 | 4 | schema Server: 5 | """ 6 | Server is the common user interface for long-running 7 | services adopting the best practice of Kubernetes. 8 | 9 | Attributes 10 | ---------- 11 | workloadType : str, default is "Deployment", required 12 | Use this attribute to specify which kind of long-running service you want. 13 | Valid values: Deployment, CafeDeployment. 14 | See also: kusion_models/core/v1/workload_metadata.k. 15 | name : str, required 16 | A Server-level attribute. 17 | The name of the long-running service. 18 | See also: kusion_models/core/v1/metadata.k. 19 | labels : {str:str}, optional 20 | A Server-level attribute. 21 | The labels of the long-running service. Contains : pairs. 22 | See also: kusion_models/core/v1/metadata.k. 23 | 24 | Examples 25 | -------- 26 | myCustomApp = AppConfiguration { 27 | name = "componentName" 28 | } 29 | 30 | """ 31 | workloadType : str = "Deployment" 32 | name: str 33 | labels?: {str: str} 34 | containers: [Container] 35 | age: int 36 | height: float 37 | port: int | str 38 | mainContainer: Container = Container { 39 | name = "main" 40 | image = "image" 41 | } 42 | antiSelf: bool 43 | others: any 44 | litStr: "abc" 45 | litBool: True 46 | litInt: 123 47 | litFloat: 1.11 48 | union: "abc" | 123 | True | 1.11 | Container | units.NumberMultiplier | 1M 49 | union2: "abc" | "def" = "abc" 50 | dictAny: {str:} 51 | listAny: [] 52 | backendWorkload: deployment.Deployment 53 | numMultiplier: units.NumberMultiplier = 1M 54 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/reimport/a.k: -------------------------------------------------------------------------------- 1 | import k8s 2 | 3 | schema A: 4 | attr: k8s.Deployment 5 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/reimport/b.k: -------------------------------------------------------------------------------- 1 | import k8s.deployment 2 | 3 | schema B: 4 | attr: deployment.Deployment 5 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/reimport/k8s/deployment.k: -------------------------------------------------------------------------------- 1 | schema Deployment: 2 | metadata: str 3 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/reimport/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/doc/reimport/md/main.md: -------------------------------------------------------------------------------- 1 | # main 2 | 3 | ## Index 4 | 5 | - [A](#a) 6 | - [B](#b) 7 | - k8s 8 | - [Deployment](#deployment) 9 | 10 | ## Schemas 11 | 12 | ### A 13 | 14 | #### Attributes 15 | 16 | | name | type | description | default value | 17 | | --- | --- | --- | --- | 18 | |**attr** `required`|[Deployment](#deployment)||| 19 | ### B 20 | 21 | #### Attributes 22 | 23 | | name | type | description | default value | 24 | | --- | --- | --- | --- | 25 | |**attr** `required`|[Deployment](#deployment)||| 26 | ### Deployment 27 | 28 | #### Attributes 29 | 30 | | name | type | description | default value | 31 | | --- | --- | --- | --- | 32 | |**metadata** `required`|str||| 33 | 34 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/genkcldata.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | // Person Example 4 | type Person struct { 5 | Name string `kcl:"name=name,type=str"` // kcl-type: str 6 | Age int `kcl:"name=age,type=int"` // kcl-type: int 7 | Friends []string `kcl:"name=friends,type=[str]"` // kcl-type: [str] 8 | Movies map[string]*Movie `kcl:"name=movies,type={str:Movie}"` // kcl-type: {str:Movie} 9 | MapInterface map[string]map[string]interface{} 10 | Ep *Employee 11 | Com Company 12 | StarInt *int 13 | StarMap map[string]string 14 | Inter interface{} 15 | } 16 | 17 | type Movie struct { 18 | Desc string `kcl:"nam=desc,type=str"` // kcl-type: str 19 | Size int `kcl:"name=size,type=units.NumberMultiplier"` // kcl-type: units.NumberMultiplier 20 | Kind string `kcl:"name=kind,type=str(Superhero)|str(War)|str(Unknown)"` // kcl-type: "Superhero"|"War"|"Unknown" 21 | Unknown1 interface{} `kcl:"name=unknown1,type=int|str"` // kcl-type: int|str 22 | Unknown2 interface{} `kcl:"name=unknown2,type=any"` // kcl-type: any 23 | } 24 | 25 | type Employee struct { 26 | Name string `kcl:"name=name,type=str"` // kcl-type: str 27 | Age int `kcl:"name=age,type=int"` // kcl-type: int 28 | Friends []string `kcl:"name=friends,type=[str]"` // kcl-type: [str] 29 | Movies map[string]*Movie `kcl:"name=movies,type={str:Movie}"` // kcl-type: {str:Movie} 30 | BankCard int `kcl:"name=bankCard,type=int"` // kcl-type: int 31 | Nationality string `kcl:"name=nationality,type=str"` // kcl-type: str 32 | } 33 | 34 | type Company struct { 35 | Name string `kcl:"name=name,type=str"` // kcl-type: str 36 | Employees []*Employee `kcl:"name=employees,type=[Employee]"` // kcl-type: [Employee] 37 | Persons *Person `kcl:"name=persons,type=Person"` // kcl-type: Person 38 | } 39 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/json/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | { 7 | apiVersion = "apps/v1" 8 | kind = "Deployment" 9 | metadata = { 10 | name = "nginx-deployment" 11 | labels = { 12 | app = "nginx" 13 | } 14 | } 15 | spec = { 16 | replicas = 3 17 | selector = { 18 | matchLabels = { 19 | app = "nginx" 20 | } 21 | } 22 | template = { 23 | metadata = { 24 | labels = { 25 | app = "nginx" 26 | } 27 | } 28 | spec = { 29 | containers = [ 30 | { 31 | name = "nginx-container" 32 | image = "nginx:latest" 33 | ports = [ 34 | { 35 | containerPort = 80 36 | } 37 | ] 38 | } 39 | ] 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/json/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "apps/v1", 3 | "kind": "Deployment", 4 | "metadata": { 5 | "name": "nginx-deployment", 6 | "labels": { 7 | "app": "nginx" 8 | } 9 | }, 10 | "spec": { 11 | "replicas": 3, 12 | "selector": { 13 | "matchLabels": { 14 | "app": "nginx" 15 | } 16 | }, 17 | "template": { 18 | "metadata": { 19 | "labels": { 20 | "app": "nginx" 21 | } 22 | }, 23 | "spec": { 24 | "containers": [ 25 | { 26 | "name": "nginx-container", 27 | "image": "nginx:latest", 28 | "ports": [ 29 | { 30 | "containerPort": 80 31 | } 32 | ] 33 | } 34 | ] 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/additional/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema Ethernet: 7 | r""" 8 | Ethernet 9 | 10 | Attributes 11 | ---------- 12 | name : str, optional 13 | socket : EthernetSocket, optional, default is {HTTP = 80, HTTPS = 443} 14 | """ 15 | 16 | name?: str 17 | socket?: EthernetSocket = {HTTP = 80, HTTPS = 443} 18 | 19 | schema EthernetSocket: 20 | r""" 21 | EthernetSocket 22 | """ 23 | 24 | [...str]: int 25 | 26 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/additional/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/ethernet.json", 4 | "type": "object", 5 | "properties": { 6 | "name": { 7 | "type": "string" 8 | }, 9 | "socket": { 10 | "type": "object", 11 | "additionalProperties": { 12 | "type": "integer" 13 | }, 14 | "default": { 15 | "HTTP": 80, 16 | "HTTPS": 443 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/allof-validation/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | import regex 6 | 7 | schema Config: 8 | r""" 9 | Schema for representing a config information. 10 | 11 | Attributes 12 | ---------- 13 | name : str, required 14 | price : float, optional 15 | """ 16 | 17 | name: str 18 | price?: float 19 | 20 | check: 21 | regex.match(name, r"198.160") 22 | regex.match(name, r"198.161") 23 | regex.match(name, r"198.162") 24 | price >= 0 if price 25 | 26 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/allof-validation/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/config", 4 | "description": "Schema for representing a config information.", 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string", 9 | "allOf": [ 10 | { 11 | "pattern": "198.160" 12 | }, 13 | { 14 | "pattern": "198.161" 15 | }, 16 | { 17 | "pattern": "198.162" 18 | } 19 | ] 20 | }, 21 | "price": { 22 | "type": "number", 23 | "minimum": 0 24 | } 25 | }, 26 | "required": [ 27 | "name" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/allof/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema Shop: 7 | r""" 8 | Schema for representing a shop information. 9 | In this test case, we use some logic keywords like "oneOf" that can't be directly converted at the moment. To make it still work, we'll convert it into "any" type. 10 | 11 | Attributes 12 | ---------- 13 | products : Clothing | [Clothing] | "empty", optional 14 | """ 15 | 16 | products?: Clothing | [Clothing] | "empty" 17 | 18 | schema Clothing: 19 | r""" 20 | Clothing 21 | 22 | Attributes 23 | ---------- 24 | $type : "clothing", required, default is "clothing" 25 | material : str, optional 26 | name : str, required 27 | price : float, optional 28 | """ 29 | 30 | $type: "clothing" = "clothing" 31 | material?: str 32 | name: str 33 | price?: float 34 | 35 | check: 36 | price >= 0 if price 37 | 38 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/allof/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/shop", 4 | "description": "Schema for representing a shop information.\nIn this test case, we use some logic keywords like \"oneOf\" that can't be directly converted at the moment. To make it still work, we'll convert it into \"any\" type.", 5 | "$defs": { 6 | "product": { 7 | "type": "object", 8 | "properties": { 9 | "name": { 10 | "type": "string" 11 | }, 12 | "price": { 13 | "type": "number", 14 | "minimum": 0 15 | } 16 | }, 17 | "required": [ 18 | "name" 19 | ] 20 | }, 21 | "clothing": { 22 | "allOf": [ 23 | { 24 | "$ref": "#/$defs/product" 25 | }, 26 | { 27 | "type": "object", 28 | "properties": { 29 | "type": { 30 | "const": "clothing" 31 | }, 32 | "material": { 33 | "type": "string" 34 | } 35 | }, 36 | "required": [ 37 | "type" 38 | ] 39 | } 40 | ] 41 | } 42 | }, 43 | "type": "object", 44 | "properties": { 45 | "products": { 46 | "oneOf": [ 47 | { 48 | "$ref": "#/$defs/clothing" 49 | }, 50 | { 51 | "type": "array", 52 | "items": { 53 | "$ref": "#/$defs/clothing" 54 | } 55 | }, 56 | { 57 | "const": "empty" 58 | } 59 | ] 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/basic/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema Book: 7 | r""" 8 | Book 9 | 10 | Attributes 11 | ---------- 12 | title : str, required 13 | authors : [str], optional 14 | price : float, optional 15 | available : bool, optional, default is True 16 | category : "Fiction" | "Science" | "History", optional 17 | $rule : str, optional 18 | """ 19 | 20 | title: str 21 | authors?: [str] 22 | price?: float 23 | available?: bool = True 24 | category?: "Fiction" | "Science" | "History" 25 | $rule?: str 26 | 27 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/basic/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/book.json", 4 | "type": "object", 5 | "properties": { 6 | "title": { 7 | "type": "string" 8 | }, 9 | "authors": { 10 | "type": "array", 11 | "items": { 12 | "type": "string" 13 | } 14 | }, 15 | "price": { 16 | "type": "number" 17 | }, 18 | "available": { 19 | "type": "boolean", 20 | "default": true 21 | }, 22 | "category": { 23 | "type": "string", 24 | "enum": [ 25 | "Fiction", 26 | "Science", 27 | "History" 28 | ] 29 | }, 30 | "rule": { 31 | "type": "string" 32 | } 33 | }, 34 | "required": [ 35 | "title" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/casting-options/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema Book: 7 | r""" 8 | Book 9 | 10 | Attributes 11 | ---------- 12 | name : str, optional, default is "AnonymousType" 13 | castingOption : "originalName" | "snakeCase" | "camelCase", optional, default is "originalName" 14 | title : str, required 15 | authors : [str], optional 16 | price : float, optional 17 | available : bool, optional, default is True 18 | category : "Fiction" | "Science" | "History", optional 19 | $rule : str, optional 20 | """ 21 | 22 | name?: str = "AnonymousType" 23 | castingOption?: "originalName" | "snakeCase" | "camelCase" = "originalName" 24 | title: str 25 | authors?: [str] 26 | price?: float 27 | available?: bool = True 28 | category?: "Fiction" | "Science" | "History" 29 | $rule?: str 30 | 31 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/casting-options/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/book.json", 4 | "type": "object", 5 | "properties": { 6 | "name": { 7 | "type": "string", 8 | "default": "AnonymousType" 9 | }, 10 | "castingOption": { 11 | "type": "string", 12 | "enum": ["originalName", "snakeCase", "camelCase"], 13 | "default": "originalName" 14 | }, 15 | "title": { 16 | "type": "string" 17 | }, 18 | "authors": { 19 | "type": "array", 20 | "items": { 21 | "type": "string" 22 | } 23 | }, 24 | "price": { 25 | "type": "number" 26 | }, 27 | "available": { 28 | "type": "boolean", 29 | "default": true 30 | }, 31 | "category": { 32 | "type": "string", 33 | "enum": [ 34 | "Fiction", 35 | "Science", 36 | "History" 37 | ] 38 | }, 39 | "rule": { 40 | "type": "string" 41 | } 42 | }, 43 | "required": [ 44 | "title" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/const/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema Book: 7 | r""" 8 | Book 9 | 10 | Attributes 11 | ---------- 12 | title : str, required 13 | authors : [str], optional 14 | status : "available", optional, default is "available" 15 | """ 16 | 17 | title: str 18 | authors?: [str] 19 | status?: "available" = "available" 20 | 21 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/const/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/book.json", 4 | "type": "object", 5 | "properties": { 6 | "title": { 7 | "type": "string" 8 | }, 9 | "authors": { 10 | "type": "array", 11 | "items": { 12 | "type": "string" 13 | } 14 | }, 15 | "status": { 16 | "const": "available" 17 | } 18 | }, 19 | "required": [ 20 | "title" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/document/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema MyOrder: 7 | r""" 8 | Schema for representing an order information. 9 | It contains the order number, list of items and the current status. 10 | 11 | Attributes 12 | ---------- 13 | id : str, required 14 | The unique order number. 15 | items : [str], optional 16 | List of items in the order. 17 | status : "processing" | "done", required, default is "processing" 18 | Current status of the order. 19 | """ 20 | 21 | id: str 22 | items?: [str] 23 | status: "processing" | "done" = "processing" 24 | 25 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/document/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/my-order.json", 4 | "title": "My Order", 5 | "description": "Schema for representing an order information.\r\nIt contains the order number, list of items and the current status.", 6 | "type": "object", 7 | "properties": { 8 | "id": { 9 | "description": "The unique order number.", 10 | "type": "string" 11 | }, 12 | "items": { 13 | "description": "List of items in the order.", 14 | "type": "array", 15 | "items": { 16 | "type": "string" 17 | } 18 | }, 19 | "status": { 20 | "description": "Current status of the order.", 21 | "enum": ["processing", "done"], 22 | "default": "processing" 23 | } 24 | }, 25 | "required": [ 26 | "id", "status" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/document_quota/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema Input: 7 | r""" 8 | Input 9 | 10 | Attributes 11 | ---------- 12 | foo : str, required, default is "more \"quotes\"" 13 | """ 14 | 15 | foo: str = r"""more "quotes" """ 16 | 17 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/document_quota/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "foo": { 5 | "type": "string", 6 | "default": "more \"quotes\"" 7 | } 8 | }, 9 | "required": [ 10 | "foo" 11 | ] 12 | } -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/document_quota_0/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "foo": { 5 | "type": "string", 6 | "default": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque pellentesque ligula consequat, aliquam elit vitae, dapibus velit. Donec posuere venenatis diam, et viverra mi facilisis nec. Mauris eget velit eu risus ornare accumsan. Suspendisse rutrum vehicula tellus. Morbi lobortis lorem eget odio consequat dapibus. Etiam metus arcu, tincidunt id enim eget, mollis tempor purus. Sed finibus ante id odio dignissim mollis. Fusce auctor sit amet risus nec dictum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec ornare orci nec fermentum accumsan. Vivamus fermentum dolor non ligula consequat, vitae pellentesque quam accumsan. Ut lobortis facilisis pharetra. Nullam tristique orci in nisi tempor, ut consequat metus molestie.\n\nSuspendisse justo quam, fermentum id ultrices sed, fringilla non eros. Ut leo risus, efficitur id posuere sit amet, sagittis vitae risus. Nullam mattis arcu quis ligula tempus ullamcorper. Donec vel nibh vel sem fermentum efficitur id a velit. Maecenas congue iaculis arcu et consectetur. Maecenas gravida nisl vitae eros dignissim, rutrum malesuada felis pulvinar. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;\n\nCurabitur vitae augue et sapien pulvinar scelerisque. Proin eleifend varius feugiat. Nullam sagittis ante at ligula pharetra, vitae molestie turpis aliquet. Donec vitae odio nec tortor accumsan condimentum. Phasellus suscipit semper tortor, ac sodales orci varius non. Praesent iaculis ultrices dui in fringilla. Sed tristique libero sed sapien sagittis eleifend.\n\nCurabitur id fermentum ex, ac aliquam ligula. Duis tellus neque, luctus eu tristique eget, viverra vitae magna. Maecenas facilisis mauris quam, eu sodales elit vulputate quis. Suspendisse nulla ipsum, auctor ut efficitur sed, rhoncus ut nisi. Donec viverra libero a rhoncus bibendum. Nullam sit amet metus nisi. Nunc venenatis eu ante quis egestas.\n\nUt sollicitudin pellentesque sem in consectetur. Nunc imperdiet lacus in venenatis consectetur. Vestibulum sed turpis tempor, mattis velit eu, hendrerit quam. Morbi ac pretium orci, ut mattis felis. Phasellus id faucibus orci, lobortis bibendum eros. Nunc nec libero consectetur, elementum tortor vitae, cursus orci. Curabitur nisl velit, auctor eu rutrum id, suscipit cursus est." 7 | } 8 | }, 9 | "required": [ 10 | "foo" 11 | ] 12 | } -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/items/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema Book: 7 | r""" 8 | Book 9 | 10 | Attributes 11 | ---------- 12 | title : str, optional 13 | authors : [str], optional 14 | """ 15 | 16 | title?: str 17 | authors?: [str] 18 | 19 | check: 20 | len(authors) <= 5 if authors 21 | len(authors) >= 1 if authors 22 | isunique(authors) if authors 23 | 24 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/items/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/book.json", 4 | "type": "object", 5 | "properties": { 6 | "title": { 7 | "type": "string" 8 | }, 9 | "authors": { 10 | "type": "array", 11 | "items": { 12 | "type": "string" 13 | }, 14 | "minItems": 1, 15 | "maxItems": 5, 16 | "uniqueItems": true 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/multipleof/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema Book: 7 | r""" 8 | Book 9 | 10 | Attributes 11 | ---------- 12 | title : str, optional 13 | price : int, optional 14 | """ 15 | 16 | title?: str 17 | price?: int 18 | 19 | check: 20 | multiplyof(price, 10) if price 21 | 22 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/multipleof/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/book.json", 4 | "type": "object", 5 | "properties": { 6 | "title": { 7 | "type": "string" 8 | }, 9 | "price": { 10 | "type": "integer", 11 | "multipleOf": 10 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/nested/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema Book: 7 | r""" 8 | Book 9 | 10 | Attributes 11 | ---------- 12 | title : str, optional 13 | author : BookAuthor, optional 14 | """ 15 | 16 | title?: str 17 | author?: BookAuthor 18 | 19 | schema BookAuthor: 20 | r""" 21 | BookAuthor 22 | 23 | Attributes 24 | ---------- 25 | name : str, optional 26 | address : str, optional 27 | """ 28 | 29 | name?: str 30 | address?: str 31 | 32 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/nested/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/book.json", 4 | "type": "object", 5 | "properties": { 6 | "title": { 7 | "type": "string" 8 | }, 9 | "author": { 10 | "type": "object", 11 | "properties": { 12 | "name": { 13 | "type": "string" 14 | }, 15 | "address": { 16 | "type": "string" 17 | } 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/oneof/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema Book: 7 | r""" 8 | Book 9 | 10 | Attributes 11 | ---------- 12 | title : str, optional 13 | author : str | [str] | int, optional 14 | category : BookCategoryOneOf0 | BookCategoryOneOf1, optional 15 | """ 16 | 17 | title?: str 18 | author?: str | [str] | int 19 | category?: BookCategoryOneOf0 | BookCategoryOneOf1 20 | 21 | schema BookCategoryOneOf0: 22 | r""" 23 | BookCategoryOneOf0 24 | 25 | Attributes 26 | ---------- 27 | name : str, optional 28 | """ 29 | 30 | name?: str 31 | 32 | schema BookCategoryOneOf1: 33 | r""" 34 | BookCategoryOneOf1 35 | 36 | Attributes 37 | ---------- 38 | title : str, optional 39 | """ 40 | 41 | title?: str 42 | 43 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/oneof/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/book.json", 4 | "type": "object", 5 | "properties": { 6 | "title": { 7 | "type": "string" 8 | }, 9 | "author": { 10 | "$comment": "oneOf for types", 11 | "oneOf": [ 12 | { 13 | "type": "string" 14 | }, 15 | { 16 | "type": "array", 17 | "items": { 18 | "type": "string" 19 | } 20 | }, 21 | { 22 | "type": "integer" 23 | } 24 | ] 25 | }, 26 | "category": { 27 | "$comment": "oneOf for objects", 28 | "oneOf": [ 29 | { 30 | "type": "object", 31 | "properties": { 32 | "name": { 33 | "type": "string" 34 | } 35 | } 36 | }, 37 | { 38 | "type": "object", 39 | "properties": { 40 | "title": { 41 | "type": "string" 42 | } 43 | } 44 | } 45 | ] 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/pattern-props/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | import regex 6 | 7 | schema Book: 8 | r""" 9 | Book 10 | 11 | Attributes 12 | ---------- 13 | title : str, required 14 | authors : BookAuthors, optional 15 | """ 16 | 17 | title: str 18 | authors?: BookAuthors 19 | 20 | schema BookAuthors: 21 | r""" 22 | BookAuthors 23 | """ 24 | 25 | [key: str]: BookAuthorsPatternProperties0 26 | 27 | check: 28 | regex.match(key, r"^[a-zA-Z]+$") 29 | 30 | schema BookAuthorsPatternProperties0: 31 | r""" 32 | BookAuthorsPatternProperties0 33 | 34 | Attributes 35 | ---------- 36 | firstName : str, optional 37 | lastName : str, optional 38 | """ 39 | 40 | firstName?: str 41 | lastName?: str 42 | 43 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/pattern-props/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/book.json", 4 | "type": "object", 5 | "properties": { 6 | "title": { 7 | "type": "string" 8 | }, 9 | "authors": { 10 | "patternProperties": { 11 | "^[a-zA-Z]+$": { 12 | "type": "object", 13 | "properties": { 14 | "firstName": { 15 | "type": "string" 16 | }, 17 | "lastName": { 18 | "type": "string" 19 | } 20 | } 21 | } 22 | } 23 | } 24 | }, 25 | "required": [ 26 | "title" 27 | ] 28 | } -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/pattern/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | import regex 6 | 7 | schema Cronjob: 8 | r""" 9 | Cronjob 10 | 11 | Attributes 12 | ---------- 13 | name : str, optional 14 | schedule : str, optional, default is "5 0 * * *" 15 | command : str, optional 16 | """ 17 | 18 | name?: str 19 | schedule?: str = "5 0 * * *" 20 | command?: str 21 | 22 | check: 23 | regex.match(schedule, r"^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$") if schedule 24 | 25 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/pattern/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/cronjob.json", 4 | "type": "object", 5 | "properties": { 6 | "name": { 7 | "type": "string" 8 | }, 9 | "schedule": { 10 | "type": "string", 11 | "default": "5 0 * * *", 12 | "pattern": "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$" 13 | }, 14 | "command": { 15 | "type": "string" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/ref/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema Customer: 7 | r""" 8 | Customer 9 | 10 | Attributes 11 | ---------- 12 | name : str, optional 13 | address : Address, optional 14 | """ 15 | 16 | name?: str 17 | address?: Address 18 | 19 | schema Address: 20 | r""" 21 | Address 22 | 23 | Attributes 24 | ---------- 25 | city : str, optional 26 | state : State, optional 27 | """ 28 | 29 | city?: str 30 | state?: State 31 | 32 | schema State: 33 | r""" 34 | State 35 | 36 | Attributes 37 | ---------- 38 | name : str, optional 39 | """ 40 | 41 | name?: str 42 | 43 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/ref/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/customer.json", 4 | "type": "object", 5 | "$defs": { 6 | "address": { 7 | "type": "object", 8 | "properties": { 9 | "city": { 10 | "type": "string" 11 | }, 12 | "state": { 13 | "$ref": "#/$defs/state" 14 | } 15 | } 16 | }, 17 | "state": { 18 | "type": "object", 19 | "properties": { 20 | "name": { 21 | "type": "string" 22 | } 23 | } 24 | } 25 | }, 26 | "properties": { 27 | "name": { 28 | "type": "string" 29 | }, 30 | "address": { 31 | "$ref": "#/$defs/address" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/unsupport-multi-pattern-props/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema Book: 7 | r""" 8 | Book 9 | 10 | Attributes 11 | ---------- 12 | title : str, required 13 | authors : BookAuthors, optional 14 | """ 15 | 16 | title: str 17 | authors?: BookAuthors 18 | 19 | schema BookAuthors: 20 | r""" 21 | BookAuthors 22 | """ 23 | 24 | [...str]: any 25 | 26 | schema BookAuthorsPatternProperties0: 27 | r""" 28 | BookAuthorsPatternProperties0 29 | 30 | Attributes 31 | ---------- 32 | name : str, optional 33 | """ 34 | 35 | name?: str 36 | 37 | schema BookAuthorsPatternProperties1: 38 | r""" 39 | BookAuthorsPatternProperties1 40 | 41 | Attributes 42 | ---------- 43 | firstName : str, optional 44 | lastName : str, optional 45 | """ 46 | 47 | firstName?: str 48 | lastName?: str 49 | 50 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/unsupport-multi-pattern-props/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/book.json", 4 | "type": "object", 5 | "properties": { 6 | "title": { 7 | "type": "string" 8 | }, 9 | "authors": { 10 | "patternProperties": { 11 | "^[a-zA-Z]+$": { 12 | "type": "object", 13 | "properties": { 14 | "firstName": { 15 | "type": "string" 16 | }, 17 | "lastName": { 18 | "type": "string" 19 | } 20 | } 21 | }, 22 | "^[0-9]+$": { 23 | "type": "object", 24 | "properties": { 25 | "name": { 26 | "type": "string" 27 | } 28 | } 29 | } 30 | }, 31 | "additionalProperties": true 32 | } 33 | }, 34 | "required": [ 35 | "title" 36 | ] 37 | } -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/validation/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema Book: 7 | r""" 8 | Book 9 | 10 | Attributes 11 | ---------- 12 | title : str, required 13 | price : float, optional 14 | quantity : int, optional 15 | """ 16 | 17 | title: str 18 | price?: float 19 | quantity?: int 20 | 21 | check: 22 | len(title) <= 20 23 | len(title) >= 2 24 | price <= 99.9 if price 25 | price >= 0 if price 26 | quantity < 100 if quantity 27 | quantity > 0 if quantity 28 | 29 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/jsonschema/validation/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://example.com/schemas/book.json", 4 | "type": "object", 5 | "properties": { 6 | "title": { 7 | "type": "string", 8 | "minLength": 2, 9 | "maxLength": 20 10 | }, 11 | "price": { 12 | "type": "number", 13 | "minimum": 0, 14 | "maximum": 99.9 15 | }, 16 | "quantity": { 17 | "type": "integer", 18 | "exclusiveMinimum": 0, 19 | "exclusiveMaximum": 100 20 | } 21 | }, 22 | "required": [ 23 | "title" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/oai/k8s/apps/deployment.k: -------------------------------------------------------------------------------- 1 | import k8s.core 2 | 3 | schema Deployment: 4 | metadata: str 5 | podSpec: core.PodSpec 6 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/oai/k8s/core/podSpec.k: -------------------------------------------------------------------------------- 1 | schema PodSpec: 2 | image: str 3 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/oai/k8s/kcl.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/gen/testdata/oai/k8s/kcl.mod -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/openapi/app/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/openapi/app/models/schema/v1/trait/opsrule.k: -------------------------------------------------------------------------------- 1 | schema OpsRule: 2 | """ OpsRule describes operation rules for various Day-2 Operations. Once declared, these 3 | operation rules will be checked before any Day-2 operations. 4 | 5 | Attributes 6 | ---------- 7 | maxUnavailable: str or int, default is Undefined, optional. 8 | The maximum percentage of the total pod instances in the component that can be 9 | simultaneously unhealthy. 10 | 11 | Examples 12 | -------- 13 | import models.schema.v1.trait as t 14 | 15 | opsRule = t.OpsRule { 16 | maxUnavailable: "30%" 17 | } 18 | """ 19 | 20 | # The maximum percentage of the total pod instances in the component that can be 21 | # simultaneously unhealthy. 22 | maxUnavailable?: int | str = "25%" -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/openapi/app/models/schema/v1/workload/common.k: -------------------------------------------------------------------------------- 1 | import models.schema.v1.workload.container as c 2 | import models.schema.v1.workload.secret as sec 3 | 4 | schema WorkloadBase: 5 | """ WorkloadBase defines set of attributes shared by different workload profile, e.g Service 6 | and Job. You can inherit this Schema to reuse these common attributes. 7 | 8 | Attributes 9 | ---------- 10 | containers: {str:c.Container}, default is Undefined, required. 11 | Containers defines the templates of containers to be ran. 12 | More info: https://kubernetes.io/docs/concepts/containers 13 | secrets: {str:sec.BasicAuthSecret|sec.TokenSecret|sec.OpaqueSecret|sec.TLSSecret|sec.ExternalSecret}, 14 | default is Undefined, optional. 15 | Secrets can be used to store small amount of sensitive data e.g. password, token 16 | replicas: int, default is 2, required. 17 | Number of container replicas based on this configuration that should be ran. 18 | labels: {str:str}, default is Undefined, optional. 19 | Labels are key/value pairs that are attached to the workload. 20 | annotations: {str:str}, default is Undefined, optional. 21 | Annotations are key/value pairs that attach arbitrary non-identifying metadata to the workload. 22 | """ 23 | 24 | # The templates of containers to be ran. 25 | containers: {str:c.Container} 26 | # Secrets store small amount of sensitive data such as a password, a token, or a key. 27 | secrets?: {str:sec.Secret} 28 | 29 | # The number of containers that should be ran. 30 | # Default is 2 to meet high availability requirements. 31 | replicas: int = 2 32 | 33 | ###### Other metadata info 34 | # Labels and annotations can be used to attach arbitrary metadata as key-value pairs to resources. 35 | labels?: {str:str} 36 | annotations?: {str:str} 37 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/openapi/app/models/schema/v1/workload/container/lifecycle/lifecycle.k: -------------------------------------------------------------------------------- 1 | import models.schema.v1.workload.container.probe as p 2 | 3 | schema Lifecycle: 4 | """ Lifecycle describes actions that the management system should take in response 5 | to container lifecycle events. 6 | 7 | Attributes 8 | ---------- 9 | preStop: p.Exec | p.Http, default is Undefined, optional. 10 | The action to be taken before a container is terminated due to an API request or 11 | management event such as liveness/startup probe failure, preemption, resource contention, etc. 12 | More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks 13 | postStart: p.Exec | p.Http, default is Undefined, optional. 14 | The action to be taken after a container is created. 15 | More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks 16 | 17 | Examples 18 | -------- 19 | import models.schema.v1.workload.container.probe as p 20 | import models.schema.v1.workload.container.lifecycle as lc 21 | 22 | lifecycleHook = lc.Lifecycle { 23 | preStop: p.Exec { 24 | command: ["preStop.sh"] 25 | } 26 | postStart: p.Http { 27 | url: "http://localhost:80" 28 | } 29 | } 30 | """ 31 | 32 | # The action to be taken before a container is terminated. 33 | preStop?: p.Exec | p.Http 34 | 35 | # The action to be taken after a container is created. 36 | postStart?: p.Exec | p.Http 37 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/openapi/app/models/schema/v1/workload/job.k: -------------------------------------------------------------------------------- 1 | schema Job(WorkloadBase): 2 | """ Job is a kind of workload profile that describes how to run your application code. This 3 | is typically used for tasks that take from a few seconds to a few days to complete. 4 | 5 | Attributes 6 | ---------- 7 | schedule: str, default is Undefined, required. 8 | The scheduling strategy in Cron format. More info: https://en.wikipedia.org/wiki/Cron. 9 | 10 | Examples 11 | -------- 12 | Instantiate a job with busybox image and runs every hour 13 | 14 | import models.schema.v1.workload as wl 15 | import models.schema.v1.workload.container as c 16 | 17 | job: wl.Job { 18 | containers: { 19 | "busybox": c.Container{ 20 | image: "busybox:1.28" 21 | command: ["/bin/sh", "-c", "echo hello"] 22 | } 23 | } 24 | schedule: "0 * * * *" 25 | } 26 | """ 27 | 28 | # The scheduling strategy in Cron format. 29 | # More info: https://en.wikipedia.org/wiki/Cron. 30 | schedule: str 31 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/openapi/app/models/schema/v1/workload/network/port.k: -------------------------------------------------------------------------------- 1 | schema Port: 2 | """ Port defines the exposed port of Service, which can be used to describe how the Service 3 | get accessed. 4 | 5 | Attributes 6 | ---------- 7 | port: int, default is 80, required. 8 | The exposed port of the Service. 9 | targetPort: int, default is Undefined, optional. 10 | The backend container port. If empty, set it the same as the port. 11 | protocol: "TCP" | "UDP", default is "TCP", required. 12 | The protocol to access the port. 13 | public: bool, default is False, required. 14 | Public defines whether the port can be accessed through Internet. 15 | 16 | Examples 17 | -------- 18 | import models.schema.v1.workload.network as n 19 | 20 | port = n.Port { 21 | port: 80 22 | targetPort: 8080 23 | protocol: "TCP" 24 | public: True 25 | } 26 | """ 27 | 28 | # The exposed port of the Service. 29 | port: int = 80 30 | 31 | # The backend container port. 32 | targetPort?: int 33 | 34 | # The protocol of port. 35 | protocol: "TCP" | "UDP" = "TCP" 36 | 37 | # Public defines whether to expose the port through Internet. 38 | public: bool = False 39 | 40 | check: 41 | 1 <= port <= 65535, "port must be between 1 and 65535, inclusive" 42 | 1 <= targetPort <= 65535 if targetPort, "targetPort must be between 1 and 65535, inclusive" -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/openapi/app/models/schema/v1/workload/secret/secret.k: -------------------------------------------------------------------------------- 1 | import regex 2 | 3 | # mapping between secret type and valid data key 4 | SECRET_TYPE_DATA_MAPPING: {str:[str]} = { 5 | "basic": ["username", "password"] 6 | "token": ["token"] 7 | "certificate": ["tls.crt", "tls.key"] 8 | } 9 | 10 | schema Secret: 11 | """ Secrets are used to provide data that is considered sensitive like passwords, API keys, 12 | TLS certificates, tokens or other credentials. 13 | 14 | Attributes 15 | ---------- 16 | $type: str, default is Undefined, required. 17 | Type of secret, used to facilitate programmatic handling of secret data. 18 | params: {str,str}, default is Undefined, optional. 19 | Collection of parameters used to facilitate programmatic handling of secret data. 20 | data: {str,str}, default is Undefined, optional. 21 | Data contains the non-binary secret data in string form. 22 | immutable: bool, default is Undefined, optional. 23 | Immutable, if set to true, ensures that data stored in the Secret cannot be updated. 24 | 25 | Examples 26 | -------- 27 | import models.schema.v1.workload.secret as sec 28 | 29 | basicAuth = sec.Secret { 30 | type: "basic" 31 | data: { 32 | "username": "" 33 | "password": "" 34 | } 35 | } 36 | """ 37 | 38 | # Types of secrets available to use. 39 | type: "basic" | "token" | "opaque" | "certificate" | "external" 40 | 41 | # Params defines extra parameters used to customize secret handling. 42 | params?: {str:str} 43 | 44 | # Data defines the keys and data that will be used by secret. 45 | data?: {str:str} 46 | 47 | # If immutable set to true, ensures that data stored in the Secret cannot be updated. 48 | immutable?: bool 49 | 50 | check: 51 | all k in data { 52 | regex.match(k, r"[A-Za-z0-9_.-]*") 53 | } if data, "a valid secret data key must consist of alphanumeric characters, '-', '_' or '.'" 54 | all k in data { 55 | k in SECRET_TYPE_DATA_MAPPING[type] if len(SECRET_TYPE_DATA_MAPPING[type]) > 0 56 | } if data, "a valid secret data key name must be one of ${SECRET_TYPE_DATA_MAPPING[type]} for ${type} type secret" 57 | 58 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/openapi/app/models/schema/v1/workload/service.k: -------------------------------------------------------------------------------- 1 | import models.schema.v1.workload.network as n 2 | 3 | schema Service(WorkloadBase): 4 | """ Service is a kind of workload profile that describes how to run your application code. This 5 | is typically used for long-running web applications that should "never" go down, and handle 6 | short-lived latency-sensitive web requests, or events. 7 | 8 | Attributes 9 | ---------- 10 | ports: [n.Port], default is Undefined, optional. 11 | The list of ports which the Service should get exposed. 12 | 13 | Examples 14 | -------- 15 | # Instantiate a long-running service and its image is "nginx:v1" 16 | 17 | import models.schema.v1.workload as wl 18 | import models.schema.v1.workload.container as c 19 | 20 | svc = wl.Service { 21 | containers: { 22 | "nginx": c.Container { 23 | image: "nginx:v1" 24 | } 25 | } 26 | ports: [ 27 | n.Port { 28 | port: 80 29 | public: True 30 | } 31 | n.Port { 32 | port: 9090 33 | } 34 | ] 35 | } 36 | """ 37 | 38 | # The list of ports get exposed. 39 | ports?: [n.Port] 40 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/proto/proto2kcl.k: -------------------------------------------------------------------------------- 1 | schema Person: 2 | name: str 3 | age: int 4 | friends: [str] 5 | movies: {str:Movie} 6 | employee?: employee 7 | company: Company 8 | star_int?: int 9 | star_map: {str:str} 10 | inter: any 11 | payload: PersonPayloadOneOf0 | PersonPayloadOneOf1 | PersonPayloadOneOf2 12 | 13 | schema Movie: 14 | desc: str 15 | size: int 16 | kind: str 17 | unknown1: any 18 | unknown2: any 19 | 20 | schema employee: 21 | name: str 22 | age: int 23 | friends: [str] 24 | movies: {str:Movie} 25 | bank_card: int 26 | nationality: str 27 | salary: float 28 | age_double: float 29 | is_married: bool 30 | gender: "unknown" | "male" | "female" 31 | gender_opt?: "unknown" | "male" | "female" 32 | 33 | schema Company: 34 | name: str 35 | employees: [employee] 36 | persons?: Person 37 | 38 | type gender = "unknown" | "male" | "female" 39 | schema PersonPayloadOneOf0: 40 | int_payload: int 41 | 42 | schema PersonPayloadOneOf1: 43 | string_payload: str 44 | 45 | schema PersonPayloadOneOf2: 46 | bool_payload: bool 47 | 48 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/proto/proto2kcl.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package test; 3 | option go_package = "test"; 4 | 5 | import "google/protobuf/any.proto"; 6 | 7 | 8 | message Person{ 9 | string name = 1; 10 | int64 age = 2; 11 | repeated string friends = 3; 12 | map movies = 4; 13 | optional employee employee = 6; 14 | Company company = 7; 15 | optional int32 star_int = 8; 16 | map star_map = 9; 17 | google.protobuf.Any inter = 10; 18 | oneof payload { 19 | int32 int_payload = 11; 20 | string string_payload = 12; 21 | bool bool_payload = 13; 22 | } 23 | } 24 | 25 | message Movie{ 26 | string desc = 1; 27 | int32 size = 2; 28 | string kind = 3; 29 | google.protobuf.Any unknown1 = 4; 30 | google.protobuf.Any unknown2 = 5; 31 | } 32 | 33 | 34 | message employee{ 35 | string name = 1; 36 | uint32 age = 2; 37 | repeated string friends = 3; 38 | map movies = 4; 39 | int32 bank_card = 5; 40 | string nationality = 6; 41 | float salary = 7; 42 | double age_double = 8; 43 | bool is_married = 9; 44 | gender gender = 10; 45 | optional gender gender_opt = 10; 46 | } 47 | 48 | 49 | message Company{ 50 | string name = 1; 51 | repeated employee employees = 2; 52 | optional Person persons = 3; 53 | } 54 | 55 | enum gender{ 56 | unknown = 0; 57 | male = 1; 58 | female = 2; 59 | } -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/proto/proto2kcl_for_num.k: -------------------------------------------------------------------------------- 1 | schema Person: 2 | name: str 3 | age: int 4 | friends: [str] 5 | movies: {str:Movie} 6 | employee?: employee 7 | company: Company 8 | star_int?: int 9 | star_map: {str:str} 10 | inter: any 11 | payload: PersonPayloadOneOf0 | PersonPayloadOneOf1 | PersonPayloadOneOf2 12 | 13 | schema Movie: 14 | desc: str 15 | size: int 16 | kind: str 17 | unknown1: any 18 | unknown2: any 19 | 20 | schema employee: 21 | name: str 22 | age: int 23 | friends: [str] 24 | movies: {str:Movie} 25 | bank_card: int 26 | nationality: str 27 | salary: float 28 | age_double: float 29 | is_married: bool 30 | gender: 0 | 1 | 2 31 | gender_opt?: 0 | 1 | 2 32 | 33 | schema Company: 34 | name: str 35 | employees: [employee] 36 | persons?: Person 37 | 38 | type gender = 0 | 1 | 2 39 | schema PersonPayloadOneOf0: 40 | int_payload: int 41 | 42 | schema PersonPayloadOneOf1: 43 | string_payload: str 44 | 45 | schema PersonPayloadOneOf2: 46 | bool_payload: bool 47 | 48 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/terraform/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | schema AlicloudConfigRule: 7 | r""" 8 | AlicloudConfigRule 9 | 10 | Attributes 11 | ---------- 12 | compliance : [ComplianceItem], optional 13 | resource_types_scope : [str], optional 14 | """ 15 | 16 | compliance?: [ComplianceItem] 17 | resource_types_scope?: [str] 18 | 19 | schema AlicloudDbInstance: 20 | r""" 21 | AlicloudDbInstance 22 | 23 | Attributes 24 | ---------- 25 | db_instance_type : str, optional 26 | engine : str, required 27 | security_group_ids : [str], optional 28 | security_ips : [str], optional 29 | tags : {str:str}, optional 30 | """ 31 | 32 | db_instance_type?: str 33 | engine: str 34 | security_group_ids?: [str] 35 | security_ips?: [str] 36 | tags?: {str:str} 37 | 38 | check: 39 | isunique(security_group_ids) if security_group_ids 40 | isunique(security_ips) if security_ips 41 | 42 | schema ComplianceItem: 43 | r""" 44 | ComplianceItem 45 | 46 | Attributes 47 | ---------- 48 | compliance_type : str, optional 49 | count : float, optional 50 | """ 51 | 52 | compliance_type?: str 53 | count?: float 54 | 55 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/textproto/data.textproto: -------------------------------------------------------------------------------- 1 | a: 1 2 | b: 2.0 3 | c: true 4 | d: "value" 5 | empty1: [] 6 | empty2: [ 7 | ] 8 | 9 | int1: [1, 2] 10 | int2: [1 2] 11 | int3: [ 12 | 1 13 | 2 14 | ] 15 | 16 | string1: [ 17 | "a", 18 | "b" 19 | ] 20 | 21 | float1: [ 1e+2 1. 0] 22 | map: { 23 | key: "foo" 24 | value: 2 25 | } 26 | map: { 27 | key: "bar" 28 | value: 3 29 | } 30 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/textproto/schema.k: -------------------------------------------------------------------------------- 1 | schema Config: 2 | a: int 3 | b: float 4 | c: bool 5 | d: str 6 | e: any 7 | empty1: [any] 8 | empty2: [] 9 | int1: [int] 10 | int2: [int] 11 | int3: [int] 12 | string1: [str] 13 | float1: [float] 14 | $map: {str:int} 15 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/toml/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | { 7 | name = "kcl" 8 | age = 1 9 | two = 2 10 | x0 = { 11 | name = "kcl" 12 | age = 1 13 | one = { 14 | two = { 15 | three = "x0_val" 16 | } 17 | } 18 | two = { 19 | three = "x0_val" 20 | } 21 | } 22 | x1 = { 23 | age = 101 24 | name = "kcl" 25 | one = { 26 | two = { 27 | three = { 28 | four = "x1_val" 29 | } 30 | } 31 | } 32 | } 33 | x2 = { 34 | one = "two" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/toml/input.toml: -------------------------------------------------------------------------------- 1 | name = "kcl" 2 | age = 1 3 | two = 2 4 | 5 | [x0] 6 | name = "kcl" 7 | age = 1 8 | 9 | [x1] 10 | age = 101 11 | name = "kcl" 12 | 13 | [x0.one.two] 14 | three = "x0_val" 15 | 16 | [x1.one.two.three] 17 | four = "x1_val" 18 | 19 | [x0.two] 20 | three = "x0_val" 21 | 22 | [x2] 23 | one = "two" 24 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/yaml/basic/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | { 7 | apiVersion = "apps/v1" 8 | kind = "Deployment" 9 | metadata = { 10 | name = "nginx-deployment" 11 | labels = { 12 | app = "nginx" 13 | } 14 | } 15 | spec = { 16 | replicas = 3 17 | selector = { 18 | matchLabels = { 19 | app = "nginx" 20 | } 21 | } 22 | template = { 23 | metadata = { 24 | labels = { 25 | app = "nginx" 26 | } 27 | } 28 | spec = { 29 | containers = [ 30 | { 31 | name = "nginx" 32 | image = "nginx:latest" 33 | ports = [ 34 | { 35 | containerPort = 80 36 | } 37 | ] 38 | } 39 | ] 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/yaml/basic/input.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx-deployment 5 | labels: 6 | app: nginx 7 | spec: 8 | replicas: 3 9 | selector: 10 | matchLabels: 11 | app: nginx 12 | template: 13 | metadata: 14 | labels: 15 | app: nginx 16 | spec: 17 | containers: 18 | - name: nginx 19 | image: 'nginx:latest' 20 | ports: 21 | - containerPort: 80 22 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/yaml/comment/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | { 7 | "cluster.name" = "my-application" 8 | } 9 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/yaml/comment/input.yaml: -------------------------------------------------------------------------------- 1 | # ---------------------------------- Cluster ----------------------------------- 2 | cluster.name: my-application 3 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/yaml/identifier/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | { 7 | identifier = { 8 | "a-b" = "identifier with hyphen" 9 | a_b = "identifier with underscore" 10 | $if = "identifier with keyword" 11 | "a/b" = "identifier with slash" 12 | "a.b" = "identifier with dot" 13 | "a#b" = "identifier with hash" 14 | "a$b" = "identifier with dollar" 15 | "a@b" = "identifier with at" 16 | "a%b" = "identifier with percent" 17 | "a*b" = "identifier with asterisk" 18 | "a&b" = "identifier with ampersand" 19 | "a(b" = "identifier with left parenthesis" 20 | "a)b" = "identifier with right parenthesis" 21 | "a,b" = "identifier with comma" 22 | "a:b" = "identifier with colon" 23 | "a;b" = "identifier with semicolon" 24 | "ab" = "identifier with greater than" 26 | "a=b" = "identifier with equal" 27 | "a+b" = "identifier with plus" 28 | "a\\b" = "identifier with backslash" 29 | "a[b" = "identifier with left bracket" 30 | "a]b" = "identifier with right bracket" 31 | "a{b" = "identifier with left brace" 32 | "a}b" = "identifier with right brace" 33 | "a'b" = "identifier with single quote" 34 | "a`b" = "identifier with back quote" 35 | "a~b" = "identifier with tilde" 36 | "a!b" = "identifier with exclamation" 37 | "a?b" = "identifier with question" 38 | "a|b" = "identifier with vertical bar" 39 | "a^b" = "identifier with caret" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/yaml/identifier/input.yaml: -------------------------------------------------------------------------------- 1 | identifier: 2 | a-b: "identifier with hyphen" 3 | a_b: "identifier with underscore" 4 | if: "identifier with keyword" 5 | a/b: "identifier with slash" 6 | a.b: "identifier with dot" 7 | a#b: "identifier with hash" 8 | a$b: "identifier with dollar" 9 | a@b: "identifier with at" 10 | a%b: "identifier with percent" 11 | a*b: "identifier with asterisk" 12 | a&b: "identifier with ampersand" 13 | a(b: "identifier with left parenthesis" 14 | a)b: "identifier with right parenthesis" 15 | a,b: "identifier with comma" 16 | a:b: "identifier with colon" 17 | a;b: "identifier with semicolon" 18 | ab: "identifier with greater than" 20 | a=b: "identifier with equal" 21 | a+b: "identifier with plus" 22 | a\\b: "identifier with backslash" 23 | a[b: "identifier with left bracket" 24 | a]b: "identifier with right bracket" 25 | a{b: "identifier with left brace" 26 | a}b: "identifier with right brace" 27 | a'b: "identifier with single quote" 28 | a`b: "identifier with back quote" 29 | a~b: "identifier with tilde" 30 | a!b: "identifier with exclamation" 31 | a?b: "identifier with question" 32 | a|b: "identifier with vertical bar" 33 | a^b: "identifier with caret" 34 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/yaml/k8s-configmap/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | { 7 | apiVersion = "v1" 8 | kind = "ConfigMap" 9 | metadata = { 10 | name = "release-name-filebeat-daemonset-config" 11 | labels = { 12 | app = "release-name-filebeat" 13 | chart = "filebeat-8.5.1" 14 | heritage = "Helm" 15 | release = "release-name" 16 | } 17 | } 18 | data = { 19 | "filebeat.yml" = r"""filebeat.inputs: 20 | - type: container 21 | paths: 22 | - /var/log/containers/*.log 23 | processors: 24 | - add_kubernetes_metadata: 25 | host: ${NODE_NAME} 26 | matchers: 27 | - logs_path: 28 | logs_path: "/var/log/containers/" 29 | 30 | output.elasticsearch: 31 | host: '${NODE_NAME}' 32 | hosts: '["https://${ELASTICSEARCH_HOSTS:elasticsearch-master:9200}"]' 33 | username: '${ELASTICSEARCH_USERNAME}' 34 | password: '${ELASTICSEARCH_PASSWORD}' 35 | protocol: https 36 | ssl.certificate_authorities: ["/usr/share/filebeat/certs/ca.crt"] 37 | """ 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/yaml/k8s-configmap/input.yaml: -------------------------------------------------------------------------------- 1 | # source: filebeat/templates/configmap.yaml 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: release-name-filebeat-daemonset-config 6 | labels: 7 | app: "release-name-filebeat" 8 | chart: "filebeat-8.5.1" 9 | heritage: "Helm" 10 | release: "release-name" 11 | data: 12 | filebeat.yml: | 13 | filebeat.inputs: 14 | - type: container 15 | paths: 16 | - /var/log/containers/*.log 17 | processors: 18 | - add_kubernetes_metadata: 19 | host: ${NODE_NAME} 20 | matchers: 21 | - logs_path: 22 | logs_path: "/var/log/containers/" 23 | 24 | output.elasticsearch: 25 | host: '${NODE_NAME}' 26 | hosts: '["https://${ELASTICSEARCH_HOSTS:elasticsearch-master:9200}"]' 27 | username: '${ELASTICSEARCH_USERNAME}' 28 | password: '${ELASTICSEARCH_PASSWORD}' 29 | protocol: https 30 | ssl.certificate_authorities: ["/usr/share/filebeat/certs/ca.crt"] 31 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/yaml/list/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | 6 | { 7 | before = { 8 | hooks = [ 9 | "go mod tidy" 10 | ] 11 | } 12 | builds = [ 13 | { 14 | id = "kcl" 15 | main = "./kcl.go" 16 | goos = [ 17 | "darwin" 18 | "linux" 19 | "windows" 20 | ] 21 | goarch = [ 22 | "amd64" 23 | "arm64" 24 | ] 25 | env = [ 26 | "CGO_ENABLED=0" 27 | ] 28 | ldflags = [ 29 | "-X kcl-lang.io/cli/pkg/version.version={{.Version}}" 30 | ] 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/yaml/list/input.yaml: -------------------------------------------------------------------------------- 1 | # This is an example .goreleaser.yml file with some sane defaults. 2 | # Make sure to check the documentation at http://goreleaser.com 3 | before: 4 | hooks: 5 | # You may remove this if you don't use go modules. 6 | - go mod tidy 7 | 8 | # .goreleaser.yml 9 | builds: 10 | - id: "kcl" 11 | main: ./kcl.go 12 | goos: 13 | - darwin 14 | - linux 15 | - windows 16 | goarch: 17 | - amd64 18 | - arm64 19 | env: 20 | - CGO_ENABLED=0 21 | ldflags: 22 | - "-X kcl-lang.io/cli/pkg/version.version={{.Version}}" 23 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/yaml2/basic/expect.k: -------------------------------------------------------------------------------- 1 | """ 2 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 3 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 4 | """ 5 | import manifests 6 | 7 | items = [ 8 | { 9 | apiVersion = "apps/v1" 10 | kind = "Deployment" 11 | metadata = { 12 | name = "nginx-deployment" 13 | labels = { 14 | app = "nginx" 15 | } 16 | } 17 | spec = { 18 | replicas = 3 19 | selector = { 20 | matchLabels = { 21 | app = "nginx" 22 | } 23 | } 24 | template = { 25 | metadata = { 26 | labels = { 27 | app = "nginx" 28 | } 29 | } 30 | spec = { 31 | containers = [ 32 | { 33 | name = "nginx" 34 | image = "nginx:latest" 35 | ports = [ 36 | { 37 | containerPort = 80 38 | } 39 | ] 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | } 46 | { 47 | apiVersion = "apps/v1" 48 | kind = "Deployment" 49 | metadata = { 50 | name = "mysql-deployment" 51 | labels = { 52 | app = "mysql" 53 | } 54 | } 55 | spec = { 56 | replicas = 3 57 | selector = { 58 | matchLabels = { 59 | app = "mysql" 60 | } 61 | } 62 | template = { 63 | metadata = { 64 | labels = { 65 | app = "mysql" 66 | } 67 | } 68 | spec = { 69 | containers = [ 70 | { 71 | name = "mysql" 72 | image = "mysql:latest" 73 | ports = [ 74 | { 75 | containerPort = 80 76 | } 77 | ] 78 | } 79 | ] 80 | } 81 | } 82 | } 83 | } 84 | ] 85 | manifests.yaml_stream(items) 86 | -------------------------------------------------------------------------------- /pkg/tools/gen/testdata/yaml2/basic/input.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx-deployment 5 | labels: 6 | app: nginx 7 | spec: 8 | replicas: 3 9 | selector: 10 | matchLabels: 11 | app: nginx 12 | template: 13 | metadata: 14 | labels: 15 | app: nginx 16 | spec: 17 | containers: 18 | - name: nginx 19 | image: 'nginx:latest' 20 | ports: 21 | - containerPort: 80 22 | --- 23 | apiVersion: apps/v1 24 | kind: Deployment 25 | metadata: 26 | name: mysql-deployment 27 | labels: 28 | app: mysql 29 | spec: 30 | replicas: 3 31 | selector: 32 | matchLabels: 33 | app: mysql 34 | template: 35 | metadata: 36 | labels: 37 | app: mysql 38 | spec: 39 | containers: 40 | - name: mysql 41 | image: 'mysql:latest' 42 | ports: 43 | - containerPort: 80 44 | -------------------------------------------------------------------------------- /pkg/tools/gen/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The KCL Authors. All rights reserved. 2 | 3 | package gen 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | "sort" 9 | ) 10 | 11 | var _ = assert 12 | var _ = assertf 13 | 14 | func assert(ok bool, a ...interface{}) { 15 | if !ok { 16 | panic(fmt.Sprint(a...)) 17 | } 18 | } 19 | 20 | func assertf(ok bool, format string, a ...interface{}) { 21 | if !ok { 22 | panic(fmt.Sprintf(format, a...)) 23 | } 24 | } 25 | 26 | func jsonString(p interface{}) string { 27 | x, err := json.MarshalIndent(p, "", " ") 28 | if err != nil { 29 | return "" 30 | } 31 | return string(x) 32 | } 33 | 34 | // getSortedKeys returns the keys sorted in alphabetical order of a map. 35 | func getSortedKeys[V any](m map[string]V) []string { 36 | keys := make([]string, 0, len(m)) 37 | for k := range m { 38 | keys = append(keys, k) 39 | } 40 | sort.Strings(keys) 41 | return keys 42 | } 43 | -------------------------------------------------------------------------------- /pkg/tools/lint/lint.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package lint 4 | 5 | import ( 6 | "kcl-lang.io/kcl-go/pkg/kcl" 7 | "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" 8 | ) 9 | 10 | func LintPath(paths []string) (results []string, err error) { 11 | svc := kcl.Service() 12 | resp, err := svc.LintPath(&gpyrpc.LintPath_Args{ 13 | Paths: paths, 14 | }) 15 | if err != nil { 16 | return nil, err 17 | } 18 | return resp.Results, nil 19 | } 20 | -------------------------------------------------------------------------------- /pkg/tools/lint/lint_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package lint 4 | 5 | import ( 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestLintPath(t *testing.T) { 11 | expect := `Module 'math' imported but unused` 12 | 13 | ss, err := LintPath([]string{"./testdata/a.k"}) 14 | if err != nil { 15 | t.Fatal(err) 16 | } 17 | if len(ss) != 1 { 18 | t.Fatalf("expect: %q, got empty", expect) 19 | } 20 | if !strings.HasSuffix(ss[0], expect) { 21 | t.Fatalf("expect: %q, got %q", expect, ss[0]) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pkg/tools/lint/testdata/a.k: -------------------------------------------------------------------------------- 1 | 2 | # kcl-lint: W0411 hello imported but unused 3 | 4 | import math 5 | 6 | a = 1 7 | 8 | b = 2 9 | -------------------------------------------------------------------------------- /pkg/tools/list/dep_parser_helper_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package list 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | func TestDepParser_parseImport(t *testing.T) { 10 | for i, tt := range t_parser_import_list { 11 | imports := parseImport(tt.code) 12 | if len(imports) != tt.imports { 13 | t.Fatalf("%d: parseImport failed, imports = %v, code = %s", i, imports, tt.code) 14 | } 15 | } 16 | } 17 | 18 | var t_parser_import_list = []struct { 19 | imports int 20 | code string 21 | }{ 22 | { 23 | imports: 1, 24 | code: ` 25 | """abc""" 26 | import base.pkg.kusion_kubernetes.apimachinery.apis 27 | `, 28 | }, 29 | { 30 | imports: 1, 31 | code: ` 32 | '''abc''' 33 | import base.pkg.kusion_kubernetes.apimachinery.apis 34 | `, 35 | }, 36 | { 37 | imports: 1, 38 | code: ` 39 | """ 40 | This is the mutating_webhook_configuration module in kusion_kubernetes.api.admissionregistration.v1beta1 package. 41 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 42 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 43 | """ 44 | import base.pkg.kusion_kubernetes.apimachinery.apis 45 | `, 46 | }, 47 | { 48 | imports: 2, 49 | code: ` 50 | """ 51 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 52 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 53 | """ 54 | import base.pkg.kusion_kubernetes.apimachinery.apis 55 | import base.pkg.kusion_kubernetes.api.core.v1 56 | `, 57 | }, 58 | { 59 | imports: 1, 60 | code: ` 61 | ''' 62 | This file was generated by the KCL auto-gen tool. DO NOT EDIT. 63 | Editing this file might prove futile when you re-run the KCL auto-gen generate command. 64 | """ 65 | import base.pkg.kusion_kubernetes.apimachinery.apis 66 | import base.pkg.kusion_kubernetes.api.core.v1 67 | ''' 68 | import abc 69 | `, 70 | }, 71 | { 72 | imports: 1, 73 | code: ` 74 | 'aaa' 75 | import kcl_plugin.hello 76 | a = 2 77 | `, 78 | }, 79 | { 80 | imports: 1, 81 | code: ` 82 | "aaa" 83 | import kcl_plugin.hello 84 | a = 2 85 | `, 86 | }, 87 | } 88 | -------------------------------------------------------------------------------- /pkg/tools/list/dep_parser_windows_test.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestInvalidFilePath(t *testing.T) { 10 | _, err := newImportDepParser(".\\testdata\\complicate\\", DepOptions{Files: []string{"appops\\projectA\\invalid.k"}, UpStreams: []string{}}) 11 | assert.EqualError(t, err, "invalid file path: stat appops\\projectA\\invalid.k: invalid argument", "err not match") 12 | } 13 | -------------------------------------------------------------------------------- /pkg/tools/list/list_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package list 4 | 5 | import ( 6 | "reflect" 7 | "sort" 8 | "strings" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestListDepFiles(t *testing.T) { 15 | files, err := ListDepFiles("../../../testdata/app0", nil) 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | 20 | expect := []string{ 21 | "main.k", 22 | "app0/before/base.k", 23 | "app0/main.k", 24 | "app0/sub/sub.k", 25 | } 26 | 27 | sort.Strings(files) 28 | sort.Strings(expect) 29 | 30 | if !reflect.DeepEqual(files, expect) { 31 | t.Fatalf("\nexpect = %v\ngot = %v", expect, files) 32 | } 33 | } 34 | 35 | func TestListDepFiles_failed(t *testing.T) { 36 | _, err := ListDepFiles("../../../testdata/app0-failed", nil) 37 | if err == nil { 38 | t.Fatal("expect error, got nil") 39 | } 40 | 41 | expectErrMsg := "package app0-failed/sub_not_found: no kcl file" 42 | if !strings.Contains(err.Error(), expectErrMsg) { 43 | t.Fatalf("expect %q, got %q", expectErrMsg, err) 44 | } 45 | } 46 | 47 | func TestNewDepParser(t *testing.T) { 48 | 49 | dp := NewDepParser("../../../testdata/import_builtin", Option{ 50 | KclYaml: "kcl.yaml", 51 | ProjectYaml: "project.yaml", 52 | }) 53 | 54 | assert.Equal(t, dp.err, nil) 55 | assert.Equal(t, len(dp.pkgFilesMap), 2) 56 | assert.Equal(t, dp.pkgFilesMap["entry"], []string{"entry/main.k"}) 57 | assert.Equal(t, dp.pkgFilesMap["sub"], []string{"sub/main.k"}) 58 | } 59 | -------------------------------------------------------------------------------- /pkg/tools/list/pkg_info.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package list 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | // FindPkgInfo find the pkg information(1. the pkg root 2. the pkg path of current workdir) 13 | func FindPkgInfo(workDir string) (pkgroot, pkgpath string, err error) { 14 | // fix ${env} 15 | if idxEnvStart := strings.Index(workDir, "${"); idxEnvStart >= 0 { 16 | if idxEnvEnd := strings.Index(workDir, "}"); idxEnvEnd > idxEnvStart { 17 | envKey := workDir[idxEnvStart+2 : idxEnvEnd-1] 18 | workDir = strings.Replace(workDir, fmt.Sprintf("${%s}", envKey), os.Getenv(envKey), 1) 19 | } 20 | } 21 | 22 | var wd = workDir 23 | if wd == "" { 24 | if x, _ := os.Getwd(); x != "" { 25 | wd = x 26 | } 27 | } 28 | if abs, _ := filepath.Abs(wd); abs != "" { 29 | wd = abs 30 | } 31 | if wd == "" { 32 | return "", "", fmt.Errorf("not found pkg root") 33 | } 34 | 35 | // try load ${pwd}/.../kcl.mod 36 | pkgroot = wd 37 | for pkgroot != "" { 38 | kModPath := filepath.Join(pkgroot, "kcl.mod") 39 | if fi, _ := os.Stat(kModPath); fi != nil { 40 | pkgpath, err = filepath.Rel(pkgroot, wd) 41 | pkgroot = filepath.ToSlash(pkgroot) 42 | pkgpath = filepath.ToSlash(pkgpath) 43 | return 44 | } 45 | pkgroot = filepath.Dir(pkgroot) 46 | if pkgroot == "" || pkgroot == "/" || pkgroot == filepath.Dir(pkgroot) { 47 | break 48 | } 49 | } 50 | 51 | // failed 52 | return "", "", fmt.Errorf("pkgroot: not found") 53 | } 54 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectA/base/base.k: -------------------------------------------------------------------------------- 1 | import base.frontend.server 2 | 3 | demo = server.Server { 4 | name: "demo" 5 | mainContainer: {} 6 | } 7 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectA/dev/main.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/complicate/appops/projectA/dev/main.k -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectB/base/base.k: -------------------------------------------------------------------------------- 1 | import base.frontend.job 2 | 3 | demo = job.Job { 4 | name: "demo" 5 | mainContainer: {} 6 | } 7 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectB/dev/main.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/complicate/appops/projectB/dev/main.k -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectC/dev/main.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/complicate/appops/projectC/dev/main.k -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectD/base/base.k: -------------------------------------------------------------------------------- 1 | import base.frontend.not_exist 2 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectD/dev/main.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/complicate/appops/projectD/dev/main.k -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectE/base/base.k: -------------------------------------------------------------------------------- 1 | import base.frontend.server 2 | import base.frontend.container 3 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectF/base/base.k: -------------------------------------------------------------------------------- 1 | import base.frontend.server 2 | import base.frontend.container 3 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectF/dev/main.k: -------------------------------------------------------------------------------- 1 | import ..base.base 2 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectG/base/base.k: -------------------------------------------------------------------------------- 1 | import base.frontend.server 2 | import base.frontend.container 3 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectG/dev/main.k: -------------------------------------------------------------------------------- 1 | import appops.projectG.base.base 2 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectH/base/base.k: -------------------------------------------------------------------------------- 1 | # this file exists for testing if a directory can take precedence over a file when they form the same import path 2 | 3 | print("this is projectH/base/base.k") 4 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectH/base/base/base.k: -------------------------------------------------------------------------------- 1 | import base.frontend.server.server 2 | 3 | print("this is projectH/base/base/base.k") 4 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectH/dev/main.k: -------------------------------------------------------------------------------- 1 | import appops.projectH.base.base 2 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/appops/projectI/dev/main.k: -------------------------------------------------------------------------------- 1 | import base.frontend.invalid_a 2 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/container/container.k: -------------------------------------------------------------------------------- 1 | # short comment 2 | """ 3 | doc string 4 | """ 5 | 6 | schema Container: 7 | name: str = "main" 8 | command?: [str] 9 | ports?: [ContainerPort] 10 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/container/container_port.k: -------------------------------------------------------------------------------- 1 | schema ContainerPort: 2 | name?: str 3 | protocol: "TCP" | "UDP" | "SCTP" = "TCP" 4 | containerPort: int 5 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/container/probe/_internal_file.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/complicate/base/frontend/container/probe/_internal_file.k -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/container/probe/exec.k: -------------------------------------------------------------------------------- 1 | schema Exec: 2 | $type: "exec" = "exec" 3 | command: [str] 4 | check: 5 | len(command) > 0, "command must be specified" 6 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/container/probe/http.k: -------------------------------------------------------------------------------- 1 | schema Http: 2 | $type: "http" = "http" 3 | path: str 4 | port: int 5 | scheme: "HTTP" | "HTTPS" = "HTTP" 6 | 7 | check: 8 | 1 <= port <= 65535, "http port must be between 1 and 65535, inclusive" 9 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/container/probe/probe.k: -------------------------------------------------------------------------------- 1 | schema Probe: 2 | handler: Exec | Http | Tcp 3 | initialDelaySeconds?: int 4 | timeoutSeconds?: int 5 | periodSeconds?: int = 10 6 | successThreshold?: int 7 | failureThreshold?: int 8 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/container/probe/probe_test.k: -------------------------------------------------------------------------------- 1 | # demo 2 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/container/probe/readme.md: -------------------------------------------------------------------------------- 1 | this is a readme file 2 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/container/probe/tcp.k: -------------------------------------------------------------------------------- 1 | schema Tcp: 2 | $type: "tcp" = "tcp" 3 | tcpSocket: int 4 | 5 | check: 6 | tcpSocket >= 1 and tcpSocket <= 65535, "tcpSocket must be between 1 and 65535, inclusive" 7 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/invalid_a/invalid_module_A.k: -------------------------------------------------------------------------------- 1 | import base.frontend.invalid_b.invalid_module_B_1 2 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/invalid_b/invalid_module_B_1.k: -------------------------------------------------------------------------------- 1 | import .invalid_module_B_2 2 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/invalid_b/invalid_module_B_2.k: -------------------------------------------------------------------------------- 1 | import base.frontend.invalid_a 2 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/job/job.k: -------------------------------------------------------------------------------- 1 | import base.frontend.container 2 | 3 | schema Job: 4 | name: str 5 | type: "Job" | "CronJob" = "Job" 6 | mainContainer: container.Container 7 | sideCars?: [container.Container] 8 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/server/server.k: -------------------------------------------------------------------------------- 1 | import base.frontend.container 2 | 3 | schema Server: 4 | name: str 5 | type: "Deployment" | "StatefulSet" | "DaemonSet" = "Deployment" 6 | mainContainer: container.Container 7 | sideCars?: [container.Container] 8 | 9 | print("this is frontend/server/server.k") 10 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/frontend/server/server/server.k: -------------------------------------------------------------------------------- 1 | # this file exists for testing if a directory can take precedence over a file when they form the same import path 2 | # this file is only referenced in the projectH/base/base/base.k 3 | 4 | print("this is frontend/server/server/server.k") 5 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/render/job/job_render.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/complicate/base/render/job/job_render.k -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/base/render/server/server_render.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/complicate/base/render/server/server_render.k -------------------------------------------------------------------------------- /pkg/tools/list/testdata/complicate/kcl.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/complicate/kcl.mod -------------------------------------------------------------------------------- /pkg/tools/list/testdata/kcl_mod_env/kcl.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/kcl_mod_env/kcl.mod -------------------------------------------------------------------------------- /pkg/tools/list/testdata/kcl_mod_env/kcl.yaml: -------------------------------------------------------------------------------- 1 | kcl_cli_configs: 2 | file: 3 | - ${KCL_MOD}/main1.k 4 | - ${KCL_MOD}/main2.k 5 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/kcl_mod_env/main1.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/kcl_mod_env/main1.k -------------------------------------------------------------------------------- /pkg/tools/list/testdata/kcl_mod_env/main2.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/kcl_mod_env/main2.k -------------------------------------------------------------------------------- /pkg/tools/list/testdata/module_with_external/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "module_with_external" 3 | version = "0.0.1" 4 | 5 | [dependencies] 6 | k8s = 1.29 7 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/module_with_external/main.k: -------------------------------------------------------------------------------- 1 | import k8s.core.v1 # external package 2 | import pkg1 3 | import pkg2 4 | import pkg3.internal.pkg 5 | import .relative_pkg1 6 | import ..relative_pkg2 7 | import ...relative_pkg3 8 | import ....relative_pkg4 9 | import math # system package 10 | 11 | The_first_kcl_program = 'Hello World!' 12 | -------------------------------------------------------------------------------- /pkg/tools/list/testdata/mymod/kcl.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/mymod/kcl.mod -------------------------------------------------------------------------------- /pkg/tools/list/testdata/mymod/sub/app/main.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/mymod/sub/app/main.k -------------------------------------------------------------------------------- /pkg/tools/list/testdata/no-kcl-mod/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/list/testdata/no-kcl-mod/.keep -------------------------------------------------------------------------------- /pkg/tools/list/utils.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | import "strings" 4 | 5 | // TODO: read from kclvm rust. 6 | var standardSystemModules = map[string]struct{}{ 7 | "collection": {}, 8 | "net": {}, 9 | "manifests": {}, 10 | "math": {}, 11 | "datetime": {}, 12 | "regex": {}, 13 | "yaml": {}, 14 | "json": {}, 15 | "crypto": {}, 16 | "base64": {}, 17 | "units": {}, 18 | "file": {}, 19 | "template": {}, 20 | "runtime": {}, 21 | } 22 | 23 | func isBuiltinPkg(pkgpath string) bool { 24 | if _, ok := standardSystemModules[pkgpath]; ok { 25 | return true 26 | } 27 | return false 28 | } 29 | 30 | func isPluginPkg(pkgpath string) bool { 31 | return strings.HasPrefix(pkgpath, "kcl_plugin/") || strings.HasPrefix(pkgpath, "kcl_plugin.") 32 | } 33 | -------------------------------------------------------------------------------- /pkg/tools/module/module.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "kcl-lang.io/kcl-go/pkg/kcl" 5 | "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" 6 | ) 7 | 8 | type UpdateDependenciesArgs = gpyrpc.UpdateDependencies_Args 9 | type UpdateDependenciesResult = gpyrpc.UpdateDependencies_Result 10 | 11 | // Download and update dependencies defined in the kcl.mod file and return the external package name and location list. 12 | func UpdateDependencies(args *UpdateDependenciesArgs) (*UpdateDependenciesResult, error) { 13 | svc := kcl.Service() 14 | return svc.UpdateDependencies(args) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/tools/override/kcl.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/tools/override/kcl.mod -------------------------------------------------------------------------------- /pkg/tools/override/override.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package override 4 | 5 | import ( 6 | "kcl-lang.io/kcl-go/pkg/kcl" 7 | "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" 8 | ) 9 | 10 | const ( 11 | DeleteAction = "Delete" 12 | CreateOrUpdateAction = "CreateOrUpdate" 13 | ) 14 | 15 | func OverrideFile(file string, specs, importPaths []string) (result bool, err error) { 16 | svc := kcl.Service() 17 | resp, err := svc.OverrideFile(&gpyrpc.OverrideFile_Args{ 18 | File: file, 19 | Specs: specs, 20 | ImportPaths: importPaths, 21 | }) 22 | if err != nil { 23 | return false, err 24 | } 25 | return resp.Result, nil 26 | } 27 | -------------------------------------------------------------------------------- /pkg/tools/override/override_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package override 4 | 5 | import ( 6 | "os" 7 | "strings" 8 | "testing" 9 | 10 | assert2 "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestOverrideFile(t *testing.T) { 14 | file := "./testdata/test.k" 15 | _, err := OverrideFile(file, []string{"config.image=\"image/image:v1\""}, []string{}) 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | _, err = OverrideFile(file, []string{"config.replicas:1"}, []string{}) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | _, err = OverrideFile(file, []string{"config.s=pkg.Service {}"}, []string{}) 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | got, err := os.ReadFile(file) 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | assert2.Equal(t, strings.ReplaceAll(string(got), "\r\n", "\n"), strings.ReplaceAll(`import pkg 32 | 33 | schema Config: 34 | image: str 35 | replicas: int 36 | 37 | if True: 38 | configOther = Config {image = "image/other:v1"} 39 | 40 | config: Config { 41 | image = "image/image:v1" 42 | replicas: 1 43 | s = pkg.Service {} 44 | } 45 | `, "\r\n", "\n")) 46 | } 47 | 48 | func TestOverrideFileWithRelativeImport(t *testing.T) { 49 | _, err := OverrideFile("./testdata/test_with_relative_import.k", []string{"config.replicas=1"}, []string{}) 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pkg/tools/override/testdata/pkg/pkg.k: -------------------------------------------------------------------------------- 1 | a = 1 2 | -------------------------------------------------------------------------------- /pkg/tools/override/testdata/test.k: -------------------------------------------------------------------------------- 1 | import pkg 2 | 3 | schema Config: 4 | image: str 5 | replicas: int 6 | 7 | if True: 8 | configOther = Config {image = "image/other:v1"} 9 | 10 | config: Config { 11 | image = "image/image:v1" 12 | replicas: 1 13 | s = pkg.Service {} 14 | } 15 | -------------------------------------------------------------------------------- /pkg/tools/override/testdata/test_with_relative_import.k: -------------------------------------------------------------------------------- 1 | import .pkg 2 | 3 | config = { 4 | a = pkg.a 5 | replicas: 1 6 | } 7 | -------------------------------------------------------------------------------- /pkg/tools/testing/indent.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import "io" 4 | 5 | type indentingWriter struct { 6 | w io.Writer 7 | } 8 | 9 | func NewIndentingWriter(w io.Writer) indentingWriter { 10 | return indentingWriter{ 11 | w: w, 12 | } 13 | } 14 | 15 | func (w indentingWriter) Write(bs []byte) (int, error) { 16 | var written int 17 | indent := true 18 | for _, b := range bs { 19 | if indent { 20 | wrote, err := w.w.Write([]byte(" ")) 21 | if err != nil { 22 | return written, err 23 | } 24 | written += wrote 25 | } 26 | wrote, err := w.w.Write([]byte{b}) 27 | if err != nil { 28 | return written, err 29 | } 30 | written += wrote 31 | indent = b == '\n' 32 | } 33 | return written, nil 34 | } 35 | -------------------------------------------------------------------------------- /pkg/tools/testing/reporter.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | func DefaultReporter(output io.Writer) Reporter { 10 | return &PrettyReporter{ 11 | Output: output, 12 | } 13 | } 14 | 15 | // Reporter defines the interface for reporting test results. 16 | type Reporter interface { 17 | // Report is called with a channel that will contain test results. 18 | Report(result *TestResult) error 19 | } 20 | 21 | // PrettyReporter reports test results in a simple human readable format. 22 | type PrettyReporter struct { 23 | Output io.Writer 24 | Verbose bool 25 | } 26 | 27 | // Report prints the test report to the reporter's output. 28 | func (r PrettyReporter) Report(result *TestResult) error { 29 | if result == nil { 30 | return nil 31 | } 32 | dirty := false 33 | var pass, fail, skip int 34 | 35 | var failures []*TestCaseInfo 36 | for _, info := range result.Info { 37 | if info.Pass() { 38 | pass++ 39 | } else if info.Skip() { 40 | skip++ 41 | } else if info.ErrMessage != "" { 42 | fail++ 43 | failures = append(failures, &info) 44 | } 45 | } 46 | 47 | for _, info := range result.Info { 48 | fmt.Fprintln(r.Output, info.Format()) 49 | } 50 | 51 | if dirty { 52 | r.hl() 53 | } 54 | 55 | total := pass + fail + skip 56 | 57 | r.hl() 58 | 59 | if pass != 0 { 60 | fmt.Fprintln(r.Output, "PASS:", fmt.Sprintf("%d/%d", pass, total)) 61 | } 62 | 63 | if fail != 0 { 64 | fmt.Fprintln(r.Output, "FAIL:", fmt.Sprintf("%d/%d", fail, total)) 65 | } 66 | 67 | if skip != 0 { 68 | fmt.Fprintln(r.Output, "SKIPPED:", fmt.Sprintf("%d/%d", skip, total)) 69 | } 70 | 71 | return nil 72 | } 73 | 74 | func (r PrettyReporter) hl() { 75 | fmt.Fprintln(r.Output, strings.Repeat("-", 80)) 76 | } 77 | -------------------------------------------------------------------------------- /pkg/tools/testing/reporter_test.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestPrettyReporter(t *testing.T) { 9 | var buf bytes.Buffer 10 | result := TestResult{ 11 | Info: []TestCaseInfo{ 12 | { 13 | Name: "test_not_log_message", 14 | Duration: 1024, 15 | }, 16 | { 17 | Name: "test_foo", 18 | Duration: 1024, 19 | LogMessage: "log message", 20 | }, 21 | { 22 | Name: "test_bar", 23 | Duration: 2048, 24 | ErrMessage: "Error: assert failed", 25 | }, 26 | }, 27 | } 28 | 29 | r := PrettyReporter{ 30 | Output: &buf, 31 | Verbose: false, 32 | } 33 | if err := r.Report(&result); err != nil { 34 | t.Fatal(err) 35 | } 36 | 37 | exp := `test_not_log_message: PASS (1ms) 38 | test_foo: PASS (1ms) 39 | log message 40 | test_bar: FAIL (2ms) 41 | Error: assert failed 42 | -------------------------------------------------------------------------------- 43 | PASS: 2/3 44 | FAIL: 1/3 45 | ` 46 | 47 | if exp != buf.String() { 48 | t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, buf.String()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pkg/tools/testing/testing.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import ( 4 | "fmt" 5 | 6 | "kcl-lang.io/kcl-go/pkg/kcl" 7 | "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" 8 | ) 9 | 10 | type TestOptions struct { 11 | PkgList []string 12 | RunRegRxp string 13 | FailFast bool 14 | // NoRun bool 15 | } 16 | 17 | type TestResult struct { 18 | Info []TestCaseInfo 19 | } 20 | 21 | type TestCaseInfo struct { 22 | Name string 23 | Duration uint64 24 | LogMessage string 25 | ErrMessage string 26 | } 27 | 28 | func (t *TestCaseInfo) Pass() bool { 29 | return t.ErrMessage == "" 30 | } 31 | 32 | func (t *TestCaseInfo) Fail() bool { 33 | return t.ErrMessage != "" 34 | } 35 | 36 | // TODO 37 | func (t *TestCaseInfo) Skip() bool { 38 | return false 39 | } 40 | 41 | func (t *TestCaseInfo) Format() string { 42 | status := fmt.Sprintf("%s: %s (%dms)", t.Name, t.StatusString(), t.Duration/1000) 43 | if t.LogMessage != "" { 44 | return fmt.Sprintf("%s\n%s", status, t.LogMessage) 45 | } 46 | if t.Fail() { 47 | return fmt.Sprintf("%s\n%s", status, t.ErrMessage) 48 | } 49 | return status 50 | } 51 | 52 | func (t *TestCaseInfo) StatusString() string { 53 | if t.Pass() { 54 | return "PASS" 55 | } else if t.Fail() { 56 | return "FAIL" 57 | } else if t.Skip() { 58 | return "SKIPPED" 59 | } 60 | return "ERROR" 61 | } 62 | 63 | func Test(testOpts *TestOptions, opts ...kcl.Option) (TestResult, error) { 64 | if testOpts == nil { 65 | testOpts = &TestOptions{} 66 | } 67 | args := kcl.NewOption().Merge(opts...) 68 | if err := args.Err; err != nil { 69 | return TestResult{}, err 70 | } 71 | 72 | svc := kcl.Service() 73 | resp, err := svc.Test(&gpyrpc.Test_Args{ 74 | ExecArgs: args.ExecProgram_Args, 75 | PkgList: testOpts.PkgList, 76 | RunRegexp: testOpts.RunRegRxp, 77 | FailFast: testOpts.FailFast, 78 | }) 79 | if err != nil { 80 | return TestResult{}, err 81 | } 82 | var info []TestCaseInfo 83 | for _, i := range resp.GetInfo() { 84 | info = append(info, TestCaseInfo{ 85 | Name: i.Name, 86 | Duration: i.Duration, 87 | LogMessage: i.LogMessage, 88 | ErrMessage: i.Error, 89 | }) 90 | } 91 | return TestResult{ 92 | Info: info, 93 | }, nil 94 | } 95 | -------------------------------------------------------------------------------- /pkg/tools/validate/test_data/data-failed.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Alice", 3 | "age": 18, 4 | "message": "This is Alice", 5 | "data": { 6 | "id": 1, 7 | "value": "value1" 8 | }, 9 | "labels": { 10 | "key": "value" 11 | }, 12 | "hc": [1, 2, 3, ""] 13 | } 14 | -------------------------------------------------------------------------------- /pkg/tools/validate/test_data/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Alice", 3 | "age": 18, 4 | "message": "This is Alice", 5 | "data": { 6 | "id": 1, 7 | "value": "value1" 8 | }, 9 | "labels": { 10 | "key": "value" 11 | }, 12 | "hc": [1, 2, 3] 13 | } 14 | -------------------------------------------------------------------------------- /pkg/tools/validate/test_data/schema.k: -------------------------------------------------------------------------------- 1 | schema User: 2 | name: str 3 | age: int 4 | message?: str 5 | data: Data 6 | labels: {str:} 7 | hc: [int] 8 | 9 | check: 10 | age > 10 11 | 12 | schema Data: 13 | id: int 14 | value: str 15 | -------------------------------------------------------------------------------- /pkg/tools/validate/validate.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package validate 4 | 5 | import ( 6 | "errors" 7 | "os" 8 | 9 | "kcl-lang.io/kcl-go/pkg/kcl" 10 | "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" 11 | ) 12 | 13 | // ValidateOptions represents the options for the Validate function. 14 | type ValidateOptions struct { 15 | Schema string // The schema to validate against. 16 | AttributeName string // The attribute name to validate. 17 | Format string // The format of the data. 18 | } 19 | 20 | // Validate validates the given data file against the specified 21 | // schema file with the provided options. 22 | func Validate(dataFile, schemaFile string, opts *ValidateOptions) (ok bool, err error) { 23 | data, err := os.ReadFile(dataFile) 24 | if err != nil { 25 | return false, err 26 | } 27 | if opts == nil { 28 | opts = &ValidateOptions{} 29 | } 30 | svc := kcl.Service() 31 | resp, err := svc.ValidateCode(&gpyrpc.ValidateCode_Args{ 32 | File: schemaFile, 33 | Data: string(data), 34 | Schema: opts.Schema, 35 | AttributeName: opts.AttributeName, 36 | Format: opts.Format, 37 | }) 38 | if err != nil { 39 | return false, err 40 | } 41 | var e error = nil 42 | if resp.ErrMessage != "" { 43 | e = errors.New(resp.ErrMessage) 44 | } 45 | return resp.Success, e 46 | } 47 | 48 | func ValidateCode(data, code string, opts *ValidateOptions) (ok bool, err error) { 49 | if opts == nil { 50 | opts = &ValidateOptions{} 51 | } 52 | svc := kcl.Service() 53 | resp, err := svc.ValidateCode(&gpyrpc.ValidateCode_Args{ 54 | Data: data, 55 | Code: code, 56 | Schema: opts.Schema, 57 | AttributeName: opts.AttributeName, 58 | Format: opts.Format, 59 | }) 60 | if err != nil { 61 | return false, err 62 | } 63 | var e error = nil 64 | if resp.ErrMessage != "" { 65 | e = errors.New(resp.ErrMessage) 66 | } 67 | return resp.Success, e 68 | } 69 | 70 | func ValidateCodeFile(dataFile, data, code string, opts *ValidateOptions) (ok bool, err error) { 71 | if opts == nil { 72 | opts = &ValidateOptions{} 73 | } 74 | svc := kcl.Service() 75 | resp, err := svc.ValidateCode(&gpyrpc.ValidateCode_Args{ 76 | Datafile: dataFile, 77 | Data: data, 78 | Code: code, 79 | Schema: opts.Schema, 80 | AttributeName: opts.AttributeName, 81 | Format: opts.Format, 82 | }) 83 | if err != nil { 84 | return false, err 85 | } 86 | var e error = nil 87 | if resp.ErrMessage != "" { 88 | e = errors.New(resp.ErrMessage) 89 | } 90 | return resp.Success, e 91 | } 92 | -------------------------------------------------------------------------------- /pkg/tools/validate/validate_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package validate 4 | 5 | import ( 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestValidate(t *testing.T) { 11 | ok, err := Validate("./test_data/data.json", "./test_data/schema.k", nil) 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | if !ok { 16 | t.Fatalf("expect: %q, got False", "True") 17 | } 18 | } 19 | 20 | func TestValidateFailed(t *testing.T) { 21 | ok, err := Validate("./test_data/data-failed.json", "./test_data/schema.k", nil) 22 | if ok == false && err != nil && strings.Contains(err.Error(), "expected [int], got [int(1) | int(2) | int(3) | str()]") { 23 | // Test Pass 24 | } else { 25 | t.Fatalf("expect: error, got (%v, %v)", ok, err) 26 | } 27 | } 28 | 29 | func TestValidateCode(t *testing.T) { 30 | data := `{"key": "value"}` 31 | code := ` 32 | schema Person: 33 | key: str 34 | 35 | check: 36 | "value" in key # 'key' is required and 'key' must contain "value" 37 | ` 38 | 39 | ok, err := ValidateCode(data, code, nil) 40 | if err != nil { 41 | t.Fatal(err) 42 | } 43 | if !ok { 44 | t.Fatalf("expect: %q, got False", "True") 45 | } 46 | } 47 | 48 | func TestValidateCodeFail(t *testing.T) { 49 | data := `{"k": "value"}` 50 | code := ` 51 | schema Person: 52 | key: str 53 | 54 | check: 55 | "value" in key # 'key' is required and 'key' must contain "value" 56 | ` 57 | 58 | _, err := ValidateCode(data, code, nil) 59 | if err == nil { 60 | t.Fatalf("expect validation error") 61 | } else if !strings.Contains(err.Error(), "error") { 62 | t.Fatalf("expect validation error, got %s", err.Error()) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pkg/utils/assert.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package utils 4 | 5 | import ( 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | func Assert(condition bool, args ...interface{}) { 11 | if !condition { 12 | if msg := fmt.Sprint(args...); msg != "" { 13 | panic("Assert failed, " + msg) 14 | } else { 15 | panic("Assert failed") 16 | } 17 | } 18 | } 19 | 20 | func TAssert(tb testing.TB, condition bool, args ...interface{}) { 21 | tb.Helper() 22 | if !condition { 23 | if msg := fmt.Sprint(args...); msg != "" { 24 | tb.Fatalf("Assert failed, %s", msg) 25 | } else { 26 | tb.Fatalf("Assert failed") 27 | } 28 | } 29 | } 30 | 31 | func TAssertf(tb testing.TB, condition bool, format string, a ...interface{}) { 32 | tb.Helper() 33 | if !condition { 34 | if msg := fmt.Sprintf(format, a...); msg != "" { 35 | tb.Fatalf("tAssert failed, %s", msg) 36 | } else { 37 | tb.Fatalf("tAssert failed") 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pkg/utils/pkg_path_test.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package utils 4 | 5 | import "testing" 6 | 7 | func TestFinePkgPath(t *testing.T) { 8 | pkgPath, err := GoodPkgPath("./testdata/sub/main.k") 9 | TAssert(t, err == nil, err) 10 | TAssert(t, pkgPath == "sub", pkgPath) 11 | 12 | pkgPath, err = GoodPkgPath("./testdata/a/b/x.k") 13 | TAssert(t, err == nil, err) 14 | TAssert(t, pkgPath == "a/b", pkgPath) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/utils/pkg_root.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The KCL Authors. All rights reserved. 2 | 3 | package utils 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | func GoodPkgPath(path string) (string, error) { 13 | if x, _ := filepath.Abs(path); x != "" { 14 | path = x 15 | } 16 | if strings.HasSuffix(path, ".k") { 17 | path = filepath.Dir(path) 18 | } 19 | pkgRoot, err := FindPkgRoot(path) 20 | if err != nil { 21 | return "", err 22 | } 23 | 24 | pkgPath, err := filepath.Rel(pkgRoot, path) 25 | if err != nil { 26 | return "", err 27 | } 28 | 29 | return filepath.ToSlash(pkgPath), nil 30 | } 31 | 32 | func FindPkgRoot(workDir string) (string, error) { 33 | wd := workDir 34 | if wd == "" { 35 | if x, _ := os.Getwd(); x != "" { 36 | wd = x 37 | } 38 | } 39 | if abs, _ := filepath.Abs(wd); abs != "" { 40 | wd = abs 41 | } 42 | 43 | if wd == "" { 44 | return "", fmt.Errorf("not found pkgroot") 45 | } 46 | 47 | // try load ${pwd}/.../kcl.mod 48 | pkgroot := wd 49 | for pkgroot != "" { 50 | kModPath := filepath.Join(pkgroot, "kcl.mod") 51 | if fi, _ := os.Stat(kModPath); fi != nil { 52 | return pkgroot, nil 53 | } 54 | parentDir := filepath.Dir(pkgroot) 55 | if parentDir == pkgroot { 56 | break 57 | } 58 | pkgroot = parentDir 59 | } 60 | 61 | // failed 62 | return "", fmt.Errorf("not found pkgroot") 63 | } 64 | -------------------------------------------------------------------------------- /pkg/utils/testdata/a/b/x.k: -------------------------------------------------------------------------------- 1 | package b 2 | -------------------------------------------------------------------------------- /pkg/utils/testdata/kcl.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/utils/testdata/kcl.mod -------------------------------------------------------------------------------- /pkg/utils/testdata/sub/main.k: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/pkg/utils/testdata/sub/main.k -------------------------------------------------------------------------------- /pkg/utils/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package utils 4 | 5 | import "os" 6 | 7 | func FileExists(path string) bool { 8 | fi, err := os.Lstat(path) 9 | if err != nil || fi.IsDir() { 10 | return false 11 | } 12 | return true 13 | } 14 | 15 | func DirExists(path string) bool { 16 | fi, err := os.Lstat(path) 17 | if err != nil || !fi.IsDir() { 18 | return false 19 | } 20 | return true 21 | } 22 | -------------------------------------------------------------------------------- /scripts/kclvm.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package scripts 4 | 5 | type ( 6 | KclvmTripleType string 7 | KclvmVersionType string 8 | ) 9 | 10 | const ( 11 | KclvmTripleType_unknown KclvmTripleType = "" 12 | KclvmTripleType_linux_amd64 KclvmTripleType = "linux-amd64" 13 | KclvmTripleType_linux_arm64 KclvmTripleType = "linux-arm64" 14 | KclvmTripleType_darwin_amd64 KclvmTripleType = "darwin-amd64" 15 | KclvmTripleType_darwin_arm64 KclvmTripleType = "darwin-arm64" 16 | KclvmTripleType_windows KclvmTripleType = "windows" 17 | ) 18 | 19 | const ( 20 | KclvmAbiVersion KclvmVersionType = KclvmVersionType_v0_11_2 21 | KclvmVersionType_latest = KclvmVersionType_v0_11_2 22 | 23 | KclvmVersionType_v0_11_2 KclvmVersionType = "v0.11.2" 24 | KclvmVersionType_v0_11_1 KclvmVersionType = "v0.11.1" 25 | KclvmVersionType_v0_11_0 KclvmVersionType = "v0.11.0" 26 | KclvmVersionType_v0_11_0_alpha1 KclvmVersionType = "v0.11.0-alpha.1" 27 | KclvmVersionType_v0_10_0 KclvmVersionType = "v0.10.0" 28 | KclvmVersionType_v0_9_0 KclvmVersionType = "v0.9.0" 29 | KclvmVersionType_v0_8_0 KclvmVersionType = "v0.8.0" 30 | KclvmVersionType_v0_7_5 KclvmVersionType = "v0.7.5" 31 | KclvmVersionType_v0_7_4 KclvmVersionType = "v0.7.4" 32 | KclvmVersionType_v0_7_3 KclvmVersionType = "v0.7.3" 33 | KclvmVersionType_v0_7_2 KclvmVersionType = "v0.7.2" 34 | KclvmVersionType_v0_7_1 KclvmVersionType = "v0.7.1" 35 | KclvmVersionType_v0_7_0 KclvmVersionType = "v0.7.0" 36 | KclvmVersionType_v0_6_0 KclvmVersionType = "v0.6.0" 37 | KclvmVersionType_v0_5_6 KclvmVersionType = "v0.5.6" 38 | KclvmVersionType_v0_5_5 KclvmVersionType = "v0.5.5" 39 | KclvmVersionType_v0_5_4 KclvmVersionType = "v0.5.4" 40 | KclvmVersionType_v0_5_3 KclvmVersionType = "v0.5.3" 41 | KclvmVersionType_v0_5_2 KclvmVersionType = "v0.5.2" 42 | KclvmVersionType_v0_5_1 KclvmVersionType = "v0.5.1" 43 | KclvmVersionType_v0_5_0 KclvmVersionType = "v0.5.0" 44 | KclvmVersionType_v0_4_6 KclvmVersionType = "v0.4.6" 45 | KclvmVersionType_v0_4_5 KclvmVersionType = "v0.4.5" 46 | KclvmVersionType_v0_4_4 KclvmVersionType = "v0.4.4" 47 | KclvmVersionType_v0_4_3 KclvmVersionType = "v0.4.3" 48 | ) 49 | -------------------------------------------------------------------------------- /scripts/util_fs.go: -------------------------------------------------------------------------------- 1 | package scripts 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func FileExists(path string) bool { 8 | fi, err := os.Lstat(path) 9 | if err != nil || fi.IsDir() { 10 | return false 11 | } 12 | return true 13 | } 14 | 15 | func DirExists(path string) bool { 16 | fi, err := os.Lstat(path) 17 | if err != nil || !fi.IsDir() { 18 | return false 19 | } 20 | return true 21 | } 22 | -------------------------------------------------------------------------------- /scripts/util_http_get.go: -------------------------------------------------------------------------------- 1 | package scripts 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "crypto/tls" 7 | "fmt" 8 | "io" 9 | "net/http" 10 | "os" 11 | ) 12 | 13 | func HttpGetData(ctx context.Context, url string, insecureSkipVerify bool) (data []byte, err error) { 14 | if ctx == nil { 15 | ctx = context.Background() 16 | } 17 | tr := &http.Transport{ 18 | TLSClientConfig: &tls.Config{InsecureSkipVerify: insecureSkipVerify}, 19 | } 20 | client := &http.Client{Transport: tr} 21 | 22 | req, err := http.NewRequestWithContext(ctx, "GET", url, nil) 23 | if err != nil { 24 | return nil, fmt.Errorf("failed to download %s: %v", url, err) 25 | } 26 | 27 | resp, err := client.Do(req) 28 | if err != nil { 29 | return nil, fmt.Errorf("failed to download %s: %v", url, err) 30 | } 31 | defer resp.Body.Close() 32 | 33 | var buf bytes.Buffer 34 | _, err = io.Copy(&buf, resp.Body) 35 | if err != nil { 36 | return nil, err 37 | } 38 | return buf.Bytes(), nil 39 | } 40 | 41 | func HttpGetFile(ctx context.Context, url, localFilename string, insecureSkipVerify bool) error { 42 | if ctx == nil { 43 | ctx = context.Background() 44 | } 45 | tr := &http.Transport{ 46 | TLSClientConfig: &tls.Config{InsecureSkipVerify: insecureSkipVerify}, 47 | } 48 | client := &http.Client{Transport: tr} 49 | 50 | req, err := http.NewRequestWithContext(ctx, "GET", url, nil) 51 | if err != nil { 52 | return fmt.Errorf("failed to download %s: %v", url, err) 53 | } 54 | 55 | resp, err := client.Do(req) 56 | if err != nil { 57 | return fmt.Errorf("failed to download %s: %v", url, err) 58 | } 59 | defer resp.Body.Close() 60 | 61 | f, err := os.Create(localFilename) 62 | if err != nil { 63 | return fmt.Errorf("failed to download %s: %v", url, err) 64 | } 65 | 66 | _, err = io.Copy(f, resp.Body) 67 | if err != nil { 68 | f.Close() 69 | os.Remove(localFilename) 70 | return fmt.Errorf("failed to download %s: %v", url, err) 71 | } 72 | f.Close() 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /scripts/util_md5.go: -------------------------------------------------------------------------------- 1 | package scripts 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | "io" 7 | "os" 8 | "regexp" 9 | "strings" 10 | ) 11 | 12 | func IsMd5Text(s string) bool { 13 | s = strings.TrimSpace(s) 14 | matched, err := regexp.MatchString(`^[a-f0-9]{32}$`, s) 15 | if err != nil { 16 | panic(err) 17 | } 18 | return matched 19 | } 20 | 21 | func MD5File(filename string) string { 22 | f, err := os.Open(filename) 23 | if err != nil { 24 | return "" 25 | } 26 | defer f.Close() 27 | 28 | h := md5.New() 29 | if _, err := io.Copy(h, f); err != nil { 30 | return "" 31 | } 32 | 33 | return fmt.Sprintf("%x", h.Sum(nil)) 34 | } 35 | -------------------------------------------------------------------------------- /scripts/util_untargz.go: -------------------------------------------------------------------------------- 1 | package scripts 2 | 3 | import ( 4 | "archive/tar" 5 | "compress/gzip" 6 | "fmt" 7 | "io" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | ) 12 | 13 | func UnTarGz(tarGzFile, trimPrefix, outputDir string) error { 14 | os.MkdirAll(outputDir, 0777) 15 | 16 | gzipStream, err := os.Open(tarGzFile) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | uncompressedStream, err := gzip.NewReader(gzipStream) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | tarReader := tar.NewReader(uncompressedStream) 27 | for { 28 | header, err := tarReader.Next() 29 | if err == io.EOF { 30 | break 31 | } 32 | if err != nil { 33 | return fmt.Errorf("UnTarGz: Next() failed: %w", err) 34 | } 35 | 36 | switch header.Typeflag { 37 | case tar.TypeDir: 38 | path := filepath.Join(outputDir, strings.TrimPrefix(header.Name, trimPrefix)) 39 | if strings.Contains(path, "..") { 40 | return fmt.Errorf("UnTarGz: MkdirAll() failed: %s", `path contains ".."`) 41 | } 42 | if err := os.MkdirAll(path, 0777); err != nil { 43 | return fmt.Errorf("UnTarGz: MkdirAll() failed: %w", err) 44 | } 45 | case tar.TypeReg: 46 | path := filepath.Join(outputDir, strings.TrimPrefix(header.Name, trimPrefix)) 47 | if strings.Contains(path, "..") { 48 | return fmt.Errorf("UnTarGz: MkdirAll() failed: %s", `path contains ".."`) 49 | } 50 | outFile, err := os.Create(path) 51 | if err != nil { 52 | return fmt.Errorf("UnTarGz: Create() failed: %w", err) 53 | } 54 | if _, err := io.Copy(outFile, tarReader); err != nil { 55 | return fmt.Errorf("UnTarGz: Copy() failed: %w", err) 56 | } 57 | outFile.Close() 58 | 59 | default: 60 | return fmt.Errorf( 61 | "UnTarGz: unknown type: %v in %v", 62 | header.Typeflag, 63 | header.Name, 64 | ) 65 | } 66 | 67 | } 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /scripts/util_unzip.go: -------------------------------------------------------------------------------- 1 | // Copyright The KCL Authors. All rights reserved. 2 | 3 | package scripts 4 | 5 | import ( 6 | "archive/zip" 7 | "fmt" 8 | "io" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | ) 13 | 14 | func Unzip(zipFile, outputDir string) error { 15 | dst := outputDir 16 | archive, err := zip.OpenReader(zipFile) 17 | if err != nil { 18 | return err 19 | } 20 | defer archive.Close() 21 | 22 | for _, f := range archive.File { 23 | filePath := filepath.Join(dst, f.Name) 24 | 25 | if !strings.HasPrefix(filePath, filepath.Clean(dst)+string(os.PathSeparator)) { 26 | return fmt.Errorf("invalid file path") 27 | } 28 | if f.FileInfo().IsDir() { 29 | os.MkdirAll(filePath, os.ModePerm) 30 | continue 31 | } 32 | 33 | if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { 34 | return err 35 | } 36 | 37 | dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | fileInArchive, err := f.Open() 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | if _, err := io.Copy(dstFile, fileInArchive); err != nil { 48 | return err 49 | } 50 | 51 | dstFile.Close() 52 | fileInArchive.Close() 53 | } 54 | 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /scripts/util_zipfs.go: -------------------------------------------------------------------------------- 1 | package scripts 2 | 3 | import ( 4 | "archive/zip" 5 | "io" 6 | "io/fs" 7 | ) 8 | 9 | func ZipFs(w io.Writer, vfs fs.FS) (err error) { 10 | zw := zip.NewWriter(w) 11 | defer zw.Close() 12 | 13 | return fs.WalkDir(vfs, ".", func(path string, dir fs.DirEntry, errx error) error { 14 | if dir.IsDir() { 15 | return nil 16 | } 17 | dst, err := zw.Create(path) 18 | if err != nil { 19 | return errx 20 | } 21 | 22 | src, err := vfs.Open(path) 23 | if err != nil { 24 | return errx 25 | } 26 | defer src.Close() 27 | 28 | if _, err := io.Copy(dst, src); err != nil { 29 | return err 30 | } 31 | 32 | return nil 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /testdata/app0-failed/kcl.yaml: -------------------------------------------------------------------------------- 1 | kcl_cli_configs: 2 | file: 3 | - main.k 4 | - ${KCL_MOD}/app0/sub/sub.k 5 | 6 | disable_none: false 7 | strict_range_check: false 8 | debug: false 9 | 10 | kcl_options: 11 | - key: app-name 12 | value: xappinfo 13 | - key: image 14 | value: image-url 15 | - key: deploy-topology 16 | value: [ 17 | { "idc": "x10", "zone": "R000C", "replicas": 4 }, 18 | { "idc": "x10", "zone": "R000A", "replicas": 4 }, 19 | { "idc": "x10", "zone": "R000B", "replicas": 4 }, 20 | ] 21 | -------------------------------------------------------------------------------- /testdata/app0-failed/main.k: -------------------------------------------------------------------------------- 1 | import .sub as sub 2 | import net # standard system module 3 | import .sub_not_found 4 | 5 | main1=3 6 | 7 | app_name = option("app-name") 8 | image = option("image") 9 | deploy_topology = option("deploy-topology") 10 | 11 | x = deploy_topology[1] 12 | -------------------------------------------------------------------------------- /testdata/app0-failed/sub/sub.k: -------------------------------------------------------------------------------- 1 | sub1 = 2 2 | -------------------------------------------------------------------------------- /testdata/app0/before/base.k: -------------------------------------------------------------------------------- 1 | base1 = 1 2 | -------------------------------------------------------------------------------- /testdata/app0/kcl.yaml: -------------------------------------------------------------------------------- 1 | kcl_cli_configs: 2 | file: 3 | - ../main.k 4 | - ./before/base.k 5 | - main.k 6 | - ${KCL_MOD}/app0/sub/sub.k 7 | 8 | disable_none: false 9 | strict_range_check: false 10 | debug: false 11 | 12 | kcl_options: 13 | - key: app-name 14 | value: xappinfo 15 | - key: image 16 | value: image-url 17 | - key: deploy-topology 18 | value: [ 19 | { "idc": "x10", "zone": "R000C", "replicas": 4 }, 20 | { "idc": "x10", "zone": "R000A", "replicas": 4 }, 21 | { "idc": "x10", "zone": "R000B", "replicas": 4 }, 22 | ] 23 | -------------------------------------------------------------------------------- /testdata/app0/main.k: -------------------------------------------------------------------------------- 1 | import app0.sub as sub 2 | 3 | main1=3 4 | 5 | app_name = option("app-name") 6 | image = option("image") 7 | deploy_topology = option("deploy-topology") 8 | 9 | x = deploy_topology[1] 10 | -------------------------------------------------------------------------------- /testdata/app0/sub/sub.k: -------------------------------------------------------------------------------- 1 | sub1 = 2 2 | -------------------------------------------------------------------------------- /testdata/external/external_1/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "external_1" 3 | edition = "0.0.1" 4 | version = "0.0.1" 5 | 6 | [dependencies] -------------------------------------------------------------------------------- /testdata/external/external_1/main.k: -------------------------------------------------------------------------------- 1 | e1 = 'Hello External_1 World!' -------------------------------------------------------------------------------- /testdata/external/external_2/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "external_2" 3 | edition = "0.0.1" 4 | version = "0.0.1" 5 | 6 | [dependencies] -------------------------------------------------------------------------------- /testdata/external/external_2/main.k: -------------------------------------------------------------------------------- 1 | e2 = 'Hello External_2 World!' -------------------------------------------------------------------------------- /testdata/get_schema_type/base.k: -------------------------------------------------------------------------------- 1 | schema Base: 2 | name: str 3 | -------------------------------------------------------------------------------- /testdata/get_schema_type/schema.k: -------------------------------------------------------------------------------- 1 | schema Schema(Base): 2 | info?: str 3 | -------------------------------------------------------------------------------- /testdata/hello/hello.k: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The KCL Authors. All rights reserved. 2 | 3 | schema Person: 4 | name: str = "kcl" 5 | age: int = 1 6 | 7 | hello = Person { 8 | name: "hello kcl" 9 | age: 102 10 | } 11 | -------------------------------------------------------------------------------- /testdata/hello/hello_test.k: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The KCL Authors. All rights reserved. 2 | 3 | import testing 4 | 5 | schema TestPerson: 6 | a = Person{} 7 | assert a.name == 'kcl' 8 | 9 | schema TestPerson_age: 10 | a = Person{} 11 | assert a.age == 1 12 | 13 | schema TestPerson_ok: 14 | a = Person{} 15 | assert a.name == "kcl" 16 | assert a.age == 1 17 | 18 | schema TestOptions: 19 | testing.arguments("name", "ktest") 20 | testing.arguments("age", "123") 21 | 22 | testing.arguments("int0", 10) 23 | testing.arguments("float0", 0.0) 24 | testing.arguments("bool-true", True) 25 | testing.arguments("bool-false", False) 26 | 27 | name = option("name") 28 | assert name == "ktest" 29 | 30 | age = option("age") 31 | assert age == 123 32 | 33 | assert option("int0") == 10 34 | assert option("float0") == 0.0 35 | #assert option("bool-true") == True 36 | #assert option("bool-false") == False 37 | 38 | schema TestOptions_setting: 39 | testing.setting_file("./xappinfo/settings.yaml") 40 | testing.arguments("file", "settings.yaml") 41 | 42 | assert option("global-tenant") == "MAIN_SITE" 43 | assert option("app-name") == "xappinfo" 44 | assert option("file") == "settings.yaml" 45 | -------------------------------------------------------------------------------- /testdata/import-external/main.k: -------------------------------------------------------------------------------- 1 | import external_1 as e1 2 | import external_2 as e2 3 | 4 | a = e1.e1 5 | b = e2.e2 -------------------------------------------------------------------------------- /testdata/import_builtin/entry/main.k: -------------------------------------------------------------------------------- 1 | import collection 2 | import net 3 | import math 4 | import datetime 5 | import regex 6 | import yaml 7 | import json 8 | import crypto 9 | import base64 10 | import units 11 | import manifests 12 | import sub 13 | 14 | manifests.yaml_stream([{k1 = [1, 2], k2 = [3, 4]}, {k3 = [5, 6], k4 = [7, 8]}, {k5 = [9, 10]}]) 15 | a = sub.a -------------------------------------------------------------------------------- /testdata/import_builtin/kcl.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/testdata/import_builtin/kcl.mod -------------------------------------------------------------------------------- /testdata/import_builtin/sub/main.k: -------------------------------------------------------------------------------- 1 | a = 10 -------------------------------------------------------------------------------- /testdata/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | -------------------------------------------------------------------------------- /testdata/lint/a.k: -------------------------------------------------------------------------------- 1 | _a = 1 2 | schema Person: 3 | name: str 4 | age: int -------------------------------------------------------------------------------- /testdata/lint/import.k: -------------------------------------------------------------------------------- 1 | import a 2 | import a # reimport 3 | -------------------------------------------------------------------------------- /testdata/lint/kcl.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kcl-lang/kcl-go/eda21261f659b7afc8110bc0a3559e5a88eeba73/testdata/lint/kcl.mod -------------------------------------------------------------------------------- /testdata/main.k: -------------------------------------------------------------------------------- 1 | name = "kcl-go" 2 | age = 2 3 | 4 | four = 4 5 | 6 | schema Person: 7 | name: str = "kcl" 8 | age: int = 1 9 | 10 | x0 = Person {} 11 | x1 = Person { 12 | age = 101 13 | } 14 | -------------------------------------------------------------------------------- /testdata/option/main.k: -------------------------------------------------------------------------------- 1 | a = option("key1") 2 | b = option("key2", required=True) 3 | c = { 4 | metadata.key = option("metadata-key") 5 | } 6 | -------------------------------------------------------------------------------- /testdata/stream/one_stream.k: -------------------------------------------------------------------------------- 1 | import manifests 2 | 3 | manifests.yaml_stream([ 4 | {a = 1} 5 | ]) 6 | -------------------------------------------------------------------------------- /testdata/stream/two_stream.k: -------------------------------------------------------------------------------- 1 | import manifests 2 | 3 | manifests.yaml_stream([ 4 | {a = 1} 5 | {b = 2} 6 | ]) 7 | -------------------------------------------------------------------------------- /testdata/sub/sub.k: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The KCL Authors. All rights reserved. 2 | 3 | schema Person: 4 | name?: any 5 | 6 | version = "dev" 7 | -------------------------------------------------------------------------------- /testdata/sub/sub_test.k: -------------------------------------------------------------------------------- 1 | # Copyright 2021 The KCL Authors. All rights reserved. 2 | 3 | schema TestFoo: 4 | assert version == "dev" 5 | 6 | schema TestSub_failed: 7 | assert version == "dev-??" 8 | 9 | schema TestSub_unit_failed: 10 | assert 1Ki == 1000, "${1Ki}" 11 | 12 | -------------------------------------------------------------------------------- /testdata/test_module/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test_data" 3 | 4 | -------------------------------------------------------------------------------- /testdata/test_module/pkg/func.k: -------------------------------------------------------------------------------- 1 | func = lambda x { 2 | x 3 | } 4 | -------------------------------------------------------------------------------- /testdata/test_module/pkg/func_test.k: -------------------------------------------------------------------------------- 1 | test_func_0 = lambda { 2 | assert func("a") == "a" 3 | } 4 | 5 | test_func_1 = lambda { 6 | assert func("a") == "d" 7 | } 8 | -------------------------------------------------------------------------------- /testdata/test_plan/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test_plan" 3 | 4 | -------------------------------------------------------------------------------- /testdata/test_plan/main.k: -------------------------------------------------------------------------------- 1 | b = 1 2 | a = 2 3 | _c = 3 4 | -------------------------------------------------------------------------------- /testdata/test_print/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test_print" 3 | 4 | -------------------------------------------------------------------------------- /testdata/test_print/main.k: -------------------------------------------------------------------------------- 1 | hello = "world" 2 | print("Hello world") 3 | -------------------------------------------------------------------------------- /testdata/update_dependencies/kcl.mod: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mod_update" 3 | edition = "0.0.1" 4 | version = "0.0.1" 5 | 6 | [dependencies] 7 | helloworld = { oci = "oci://ghcr.io/kcl-lang/helloworld", tag = "0.1.0" } 8 | flask = { git = "https://github.com/kcl-lang/flask-demo-kcl-manifests", commit = "ade147b" } 9 | -------------------------------------------------------------------------------- /testdata/update_dependencies/main.k: -------------------------------------------------------------------------------- 1 | import helloworld 2 | import flask 3 | 4 | a = helloworld.The_first_kcl_program 5 | -------------------------------------------------------------------------------- /testdata/vet/hello.k: -------------------------------------------------------------------------------- 1 | 2 | schema Person: 3 | key: str 4 | 5 | check: 6 | "value" in key # 'key' is required and 'key' must contain "value" 7 | -------------------------------------------------------------------------------- /testdata/vet/hello.k.json: -------------------------------------------------------------------------------- 1 | {"key": "value"} -------------------------------------------------------------------------------- /testdata/vet/sample.k: -------------------------------------------------------------------------------- 1 | import regex 2 | schema Sample: 3 | foo: str # Required, 不能为None, 且类型必须为str 4 | bar: int # Required, 不能为None, 且类型必须为int 5 | fooList: [int] # Required, 不能为None, 且类型必须为str列表 6 | color: "Red" | "Yellow" | "Blue" # Required, 字面值联合类型,且必须为"Red", "Yellow", "Blue"中的一个,枚举作用 7 | id?: int # Optional,可以留空,类型必须为int 8 | customSample: Sample1 9 | check: 10 | bar >= 0 # bar必须大于等于0 11 | bar < 100 # bar必须小于100 12 | len(fooList) > 0 # fooList不能为None,并且长度必须大于0 13 | len(fooList) < 100 # fooList不能为None,并且长度必须小于100 14 | regex.match(foo, "^The.*Foo$") # regex 正则表达式匹配 15 | bar in range(100) # range, bar范围只能为1到99 16 | bar in [2, 4, 6, 8] # enum, bar只能取2, 4, 6, 8 17 | bar % 2 == 0 # bar必须为2的倍数 18 | all foo in fooList { 19 | foo > 1 20 | } # fooList中的所有元素必须大于1 21 | any foo in fooList { 22 | foo > 10 23 | } # fooList中至少有一个元素必须大于10 24 | abs(id) > 10 if id is not None # check if 表达式,当 id 不为空时,id的绝对值必须大于10 25 | schema Sample1: 26 | foo: str # Required, 不能为None, 且类型必须为str 27 | bar: int # Required, 不能为None, 且类型必须为int 28 | fooList: [int] # Required, 不能为None, 且类型必须为str列表 29 | color: "Red" | "Yellow" | "Blue" # Required, 字面值联合类型,且必须为"Red", "Yellow", "Blue"中的一个,枚举作用 30 | id?: int # Optional,可以留空,类型必须为int 31 | check: 32 | bar >= 0 # bar必须大于等于0 33 | bar < 100 # bar必须小于100 34 | len(fooList) > 0 # fooList不能为None,并且长度必须大于0 35 | len(fooList) < 100 # fooList不能为None,并且长度必须小于100 36 | regex.match(foo, "^The.*Foo$") # regex 正则表达式匹配 37 | bar in range(100) # range, bar范围只能为1到99 38 | bar in [2, 4, 6, 8] # enum, bar只能取2, 4, 6, 8 39 | bar % 2 == 0 # bar必须为2的倍数 40 | all foo in fooList { 41 | foo > 1 42 | } # fooList中的所有元素必须大于1 43 | any foo in fooList { 44 | foo > 10 45 | } # fooList中至少有一个元素必须大于10 46 | abs(id) > 10 if id is not None # check if 表达式,当 id 不为空时,id的绝对值必须大于10 47 | -------------------------------------------------------------------------------- /testdata/vet/sample.k.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "TheaaaaFoo", 3 | "bar": 8, 4 | "fooList": [ 5 | 2, 6 | 11 7 | ], 8 | "color": "Red", 9 | "id": 100, 10 | "customSample": { 11 | "foo": "TheaaaaFoo", 12 | "bar": 8, 13 | "fooList": [ 14 | 2, 15 | 10 16 | ], 17 | "color": "Red", 18 | "id": 100 19 | } 20 | } -------------------------------------------------------------------------------- /testdata/xappinfo/settings.yaml: -------------------------------------------------------------------------------- 1 | kcl_cli_configs: 2 | file: 3 | - ../../../../../base/pkg/kusion_models/app_configuration/xapp/xapp_app_configuration.k 4 | - ../../../../../base/pkg/kusion_models/app_configuration/xapp/xapp_app_configuration_render.k 5 | - ../../../../../base/pkg/kusion_models/app_configuration/deploy_topology_render.k 6 | - ../main.k 7 | - ../../../../../base/pkg/kusion_models/app_configuration/xapp/xapp_app_configuration_backend.k 8 | disable_none: true 9 | kcl_options: 10 | - key: global-tenant 11 | value: MAIN_SITE 12 | - key: app-name 13 | value: xappinfo 14 | - key: global-app-name 15 | value: xappinfo 16 | - key: env-type 17 | value: prod 18 | - key: xappinfo-env-type 19 | value: prod 20 | - key: image 21 | value: image-url 22 | - key: deploy-topology 23 | value: [ 24 | { "idc": "x10", "zone": "R000C", "replicas": 4 }, 25 | { "idc": "x10", "zone": "R000A", "replicas": 4 }, 26 | { "idc": "x10", "zone": "R000B", "replicas": 4 }, 27 | ] 28 | 29 | --------------------------------------------------------------------------------