Repository: kcl-lang/kcl-go (go)

Issue:
support Go API for GetSchemaTypeMapping
## Feature Request

**Is your feature request related to a problem? Please describe:**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->

We have rest API `rpc GetSchemaTypeMapping(GetSchemaTypeMapping_Args) returns(GetSchemaTypeMapping_Result);` on kcl-py but the corresponding Go API is missing.

The `GetSchemaTypeMapping((file: string, code:string)` is required to export HIR of KCL schema, which could be used to integrate other tools, like kcl-doc, vscode-kcl low code create from schema, etc.

**Describe the feature you'd like:**
<!-- A clear and concise description of what you want to happen. -->
add `GetSchemaTypeMapping((file: string, code:string)` Go API

**Describe alternatives you've considered:**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->

**Teachability, Documentation, Adoption, Migration Strategy:**
<!-- If you can, explain some scenarios how users might use this, situations it would be helpful in. Any API designs, mockups, or diagrams are also helpful. -->


Pull request description:
fix: fix fmt toml. Only allow table after table in root scope
<!-- Thank you for contributing to KCL!

Note: 

1. With pull requests:

    - Open your pull request against "main"
    - Your pull request should have no more than two commits, if not you should squash them.
    - It should pass all tests in the available continuous integration systems such as GitHub Actions.
    - You should add/modify tests to cover your proposed code changes.
    - If your pull request contains a new feature, please document it on the README.

2. Please create an issue first to describe the problem.

    We recommend that link the issue with the PR in the following question.
    For more info, check https://kcl-lang.io/docs/community/contribute/
-->

#### 1. Does this PR affect any open issues?(Y/N) and add issue references (e.g. "fix #123", "re #123".):

- [ ] N
- [x] Y fix https://github.com/kcl-lang/kcl/issues/1792 

<!-- You can add issue references here. 
    e.g. 
    fix #123, re #123, 
    fix https://github.com/XXX/issues/44
-->

#### 2. What is the scope of this PR (e.g. component or file name):

<!-- You can add the scope of this change here. 
    e.g. 
    /src/server/core.rs,
    kcl-lang/kcl/src/parser 
-->

#### 3. Provide a description of the PR(e.g. more details, effects, motivations or doc link):

<!-- You can choose a brief description here -->
- [ ] Affects user behaviors
- [ ] Contains syntax changes
- [ ] Contains variable changes
- [ ] Contains experimental features
- [ ] Performance regression: Consumes more CPU
- [ ] Performance regression: Consumes more Memory
- [ ] Other

<!-- You can add more details here.
    e.g. 
    Call method "XXXX" to ..... in order to ....,
    More details: https://XXXX.com/doc......
-->

#### 4. Are there any breaking changes?(Y/N) and describe the breaking changes(e.g. more details, motivations or doc link):

- [ ] N
- [ ] Y 

<!-- You can add more details here.
    e.g. 
    Calling method "XXXX" will cause the "XXXX", "XXXX" modules to be affected.
    More details: https://XXXX.com/doc......
-->

#### 5. Are there test cases for these changes?(Y/N) select and add more details, references or doc links:

<!-- You can choose a brief description here -->
- [ ] Unit test
- [ ] Integration test
- [ ] Benchmark (add benchmark stats below)
- [ ] Manual test (add detailed scripts or steps below)
- [ ] Other

<!-- You can add more details here.
e.g. 
The test case in XXXX is used to .....
test cases in /src/tests/XXXXX
test cases https://github.com/XXX/pull/44
benchmark stats: time XXX ms
-->


Patch:
diff --git a/pkg/3rdparty/toml/encode.go b/pkg/3rdparty/toml/encode.go
index c550a80..65945fa 100644
--- a/pkg/3rdparty/toml/encode.go
+++ b/pkg/3rdparty/toml/encode.go
@@ -127,9 +127,11 @@ func Marshal(v any) ([]byte, error) {
 // NOTE: only exported keys are encoded due to the use of reflection. Unexported
 // keys are silently discarded.
 type Encoder struct {
-	Indent     string // string for a single indentation level; default is two spaces.
-	hasWritten bool   // written any output to w yet?
-	w          *bufio.Writer
+	Indent          string // string for a single indentation level; default is two spaces.
+	hasWritten      bool   // written any output to w yet?
+	hasWrittenTable bool   // table must at end of file
+	inTable         bool
+	w               *bufio.Writer
 }
 
 // NewEncoder create a new Encoder.
@@ -168,6 +170,7 @@ func (enc *Encoder) encode(key Key, rv reflect.Value) {
 	// If we can marshal the type to text, then we use that. This prevents the
 	// encoder for handling these types as generic structs (or whatever the
 	// underlying type of a TextMarshaler is).
+
 	switch {
 	case isMarshaler(rv):
 		enc.writeKeyValue(key, rv, false)
@@ -183,6 +186,18 @@ func (enc *Encoder) encode(key Key, rv reflect.Value) {
 	}
 
 	k := rv.Kind()
+
+	// https://toml.io/en/v1.0.0#table
+	// Under a table,  and until the next header or EOF, are the key/values of that table.
+	// Therefore, if a table has been defined and is not within the scope of this table, only other tables are allowed to appear
+	if !enc.inTable && enc.hasWrittenTable {
+		switch k {
+		case reflect.Map, reflect.Struct:
+		default:
+			encPanic(fmt.Errorf("unsupported to define '%s' after a table, ref: https://toml.io/en/v1.0.0#table", key))
+		}
+	}
+
 	switch k {
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
 		reflect.Int64,
@@ -379,16 +394,19 @@ func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
 }
 
 func (enc *Encoder) eTable(key Key, rv reflect.Value) {
+	enc.inTable = true
 	if len(key) == 1 {
 		// Output an extra newline between top-level tables.
 		// (The newline isn't written if nothing else has been written though.)
 		enc.newline()
 	}
 	if len(key) > 0 {
+		enc.hasWrittenTable = true
 		enc.wf("%s[%s]", enc.indentStr(key), key)
 		enc.newline()
 	}
 	enc.eMapOrStruct(key, rv, false)
+	enc.inTable = false
 }
 
 func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value, inline bool) {


Test patch:
diff --git a/pkg/tools/gen/gentoml_test.go b/pkg/tools/gen/gentoml_test.go
index fb20d80..2b07d35 100644
--- a/pkg/tools/gen/gentoml_test.go
+++ b/pkg/tools/gen/gentoml_test.go
@@ -1,6 +1,7 @@
 package gen
 
 import (
+	"fmt"
 	"testing"
 
 	"github.com/goccy/go-yaml"
@@ -30,22 +31,24 @@ c_key = "value3"
 		{
 			name: "Nested MapSlice",
 			data: &yaml.MapSlice{
+				{Key: "outer_key2", Value: "outer_value2"},
 				{Key: "outer_key1", Value: yaml.MapSlice{
 					{Key: "inner_key1", Value: "inner_value1"},
 					{Key: "inner_key2", Value: "inner_value2"},
 				}},
-				{Key: "outer_key2", Value: "outer_value2"},
 			},
-			expectedTOML: `[outer_key1]
+			expectedTOML: `outer_key2 = "outer_value2"
+
+[outer_key1]
   inner_key1 = "inner_value1"
   inner_key2 = "inner_value2"
-outer_key2 = "outer_value2"
 `,
 			expectErr: nil,
 		},
 		{
 			name: "Nested MapSlice with Slice",
 			data: &yaml.MapSlice{
+				{Key: "simple_key", Value: "simple_value"},
 				{Key: "key_with_slices", Value: []yaml.MapSlice{
 					{
 						{Key: "inner_key1", Value: "value1"},
@@ -54,10 +57,9 @@ outer_key2 = "outer_value2"
 						{Key: "inner_key2", Value: "value2"},
 					},
 				}},
-				{Key: "simple_key", Value: "simple_value"},
 			},
-			expectedTOML: `key_with_slices = [{inner_key1 = "value1"}, {inner_key2 = "value2"}]
-simple_key = "simple_value"
+			expectedTOML: `simple_key = "simple_value"
+key_with_slices = [{inner_key1 = "value1"}, {inner_key2 = "value2"}]
 `,
 			expectErr: nil,
 		},
@@ -86,31 +88,50 @@ simple_key = "simple_value"
 			name: "Simple MapSlice",
 			data: &yaml.MapSlice{
 				{Key: "b_key", Value: "value1"},
+				{Key: "c_key", Value: "value3"},
 				{Key: "a_key", Value: map[string]string{
 					"a_a_key": "value2",
 				}},
-				{Key: "c_key", Value: "value3"},
 			},
 			expectedTOML: `b_key = "value1"
+c_key = "value3"
 
 [a_key]
   a_a_key = "value2"
-c_key = "value3"
 `,
 			expectErr: nil,
 		},
+
+		{
+			name: "Table Test",
+			data: &yaml.MapSlice{
+				{Key: "a_key", Value: map[string]string{
+					"a_a_key": "value2",
+				}},
+				{Key: "b_key", Value: "value1"},
+			},
+			expectedTOML: "",
+			expectErr:    fmt.Errorf("unsupported to define 'b_key' after a table, ref: https://toml.io/en/v1.0.0#table"),
+		},
 	}
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			tomlData, err := toml.Marshal(tt.data)
-			if err != tt.expectErr {
+			if err != nil && tt.expectErr != nil {
+				if err.Error() != tt.expectErr.Error() {
+					t.Fatalf("expected error: %v, got: %v", tt.expectErr, err)
+				} else {
+					return
+				}
+			} else if err == nil && tt.expectErr == nil {
+				if got := string(tomlData); got != tt.expectedTOML {
+					t.Errorf("expected:\n%s\ngot:\n%s", tt.expectedTOML, got)
+				}
+			} else {
 				t.Fatalf("expected error: %v, got: %v", tt.expectErr, err)
 			}
 
-			if got := string(tomlData); got != tt.expectedTOML {
-				t.Errorf("expected:\n%s\ngot:\n%s", tt.expectedTOML, got)
-			}
 		})
 	}
 }