Repository: kcl-lang/kcl (language: rust)

Issue:
[WIP] Feat : Support Call Kclvm Cli by C API
<!-- Thank you for contributing to KusionStack!

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://kusionstack.io/docs/governance/contribute/
-->

#### 1. Does this PR affect any open issues?(Y/N) and add issue references (e.g. "fix #123", "re #123".):

- [ ] N
- [x] Y 
re #125 

#### 2. What is the scope of this PR (e.g. component or file name):

<!-- You can add the scope of this change here. 
-->
        KCLVM/kclvm/spec
        KCLVM/kclvm/src/lib.rs

#### 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
- [x] Contains experimental features
- [ ] Performance regression: Consumes more CPU
- [ ] Performance regression: Consumes more Memory
- [ ] Other

<!-- You can add more details here.
-->
    Support user to call kclvm_cli by c_api


#### 4. Are there any breaking changes?(Y/N) and describe the breaking changes(e.g. more details, motivations or doc link):

- [x] N
- [ ] Y 

<!-- You can add more details here.

-->

#### 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 -->
- [x] Unit test
- [ ] Integration test
- [ ] Benchmark (add benchmark stats below)
- [ ] Manual test (add detailed scripts or steps below)
- [ ] Other

<!-- You can add more details here.
None
-->
KCLVM/kclvm/src/api_test.rs

#### 6. Release note

<!-- compatibility change, improvement, bugfix, and new feature need a release note -->

Please refer to [Release Notes Language Style Guide](https://kusionstack.io/docs/governance/release-policy/) to write a quality release note.

```release-note
None
```



Discussion / hints:
****CLA Assistant Lite bot**** All contributors have signed the CLA  ✍️ ✅
I have read the CLA Document and I hereby sign the CLA
## Pull Request Test Coverage Report for [Build 2744948016](https://coveralls.io/builds/51191024)

* **2** of **5523**   **(0.04%)**  changed or added relevant lines in **6** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage decreased (**-7.6%**) to **51.731%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [kclvm/spec/src/main.rs](https://coveralls.io/builds/51191024/source?filename=kclvm%2Fspec%2Fsrc%2Fmain.rs#L2) | 1 | 3 | 33.33%
| [kclvm/spec/src/service/util.rs](https://coveralls.io/builds/51191024/source?filename=kclvm%2Fspec%2Fsrc%2Fservice%2Futil.rs#L2) | 0 | 25 | 0.0%
| [kclvm/spec/src/service/service.rs](https://coveralls.io/builds/51191024/source?filename=kclvm%2Fspec%2Fsrc%2Fservice%2Fservice.rs#L10) | 0 | 109 | 0.0%
| [kclvm/spec/src/service/api.rs](https://coveralls.io/builds/51191024/source?filename=kclvm%2Fspec%2Fsrc%2Fservice%2Fapi.rs#L10) | 0 | 153 | 0.0%
| [kclvm/spec/src/model/gpyrpc.rs](https://coveralls.io/builds/51191024/source?filename=kclvm%2Fspec%2Fsrc%2Fmodel%2Fgpyrpc.rs#L28) | 0 | 5232 | 0.0%
<!-- | **Total:** | **2** | **5523** | **0.04%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/51191024/badge)](https://coveralls.io/builds/51191024) |
| :-- | --: |
| Change from base [Build 2743999583](https://coveralls.io/builds/51186839): |  -7.6% |
| Covered Lines: | 22244 |
| Relevant Lines: | 42999 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Fix patch:
diff --git a/kclvm/evaluator/src/schema.rs b/kclvm/evaluator/src/schema.rs
index 93d832e75..a67d4f368 100644
--- a/kclvm/evaluator/src/schema.rs
+++ b/kclvm/evaluator/src/schema.rs
@@ -205,6 +205,29 @@ impl SchemaEvalContext {
         false
     }
 
+    /// Get all attribute from schema
+    pub fn get_attrs(s: &Evaluator, ctx: &SchemaEvalContextRef) -> Vec<(String, bool)> {
+        let mut attrs = vec![];
+        for stmt in &ctx.borrow().node.body {
+            if let ast::Stmt::SchemaAttr(attr) = &stmt.node {
+                attrs.push((attr.name.node.clone(), attr.is_optional));
+            }
+        }
+        if let Some(index) = ctx.borrow().parent {
+            let frame = {
+                let frames = s.frames.borrow();
+                frames
+                    .get(index)
+                    .expect(kcl_error::INTERNAL_ERROR_MSG)
+                    .clone()
+            };
+            if let Proxy::Schema(schema) = &frame.proxy {
+                attrs.extend(SchemaEvalContext::get_attrs(s, &schema.ctx));
+            }
+        }
+        attrs
+    }
+
     /// Whether the index signature is the schema context.
     pub fn has_index_signature(s: &Evaluator, ctx: &SchemaEvalContextRef) -> bool {
         if ctx.borrow().node.index_signature.is_some() {
diff --git a/kclvm/evaluator/src/ty.rs b/kclvm/evaluator/src/ty.rs
index 6b7b694d3..e66063f9b 100644
--- a/kclvm/evaluator/src/ty.rs
+++ b/kclvm/evaluator/src/ty.rs
@@ -1,7 +1,7 @@
 use kclvm_runtime::{
-    check_type, dereference_type, is_dict_type, is_list_type, is_type_union, schema_config_meta,
-    schema_runtime_type, separate_kv, split_type_union, val_plan, ConfigEntryOperationKind,
-    ValueRef, BUILTIN_TYPES, KCL_TYPE_ANY, PKG_PATH_PREFIX,
+    check_type, dereference_type, is_dict_type, is_list_type, is_schema_type, is_type_union,
+    schema_config_meta, schema_runtime_type, separate_kv, split_type_union, val_plan,
+    ConfigEntryOperationKind, ValueRef, BUILTIN_TYPES, KCL_TYPE_ANY, PKG_PATH_PREFIX,
 };
 use scopeguard::defer;
 
@@ -72,7 +72,7 @@ pub fn type_pack_and_check(
     let mut checked = false;
     let mut converted_value = value.clone();
     let expected_type = &expected_types.join(" | ").replace('@', "");
-    for tpe in expected_types {
+    for tpe in &expected_types {
         if !is_schema {
             converted_value = convert_collection_value(s, value, tpe);
         }
@@ -88,9 +88,66 @@ pub fn type_pack_and_check(
         }
     }
     if !checked {
+        let mut error_msgs = vec![];
+        for tpe in &expected_types {
+            if is_schema_type(tpe) {
+                let schema_type_name = if tpe.contains('.') {
+                    if tpe.starts_with(PKG_PATH_PREFIX) {
+                        tpe.to_string()
+                    } else {
+                        format!("{PKG_PATH_PREFIX}{tpe}")
+                    }
+                } else {
+                    format!("{}.{}", s.current_pkgpath(), tpe)
+                };
+
+                if let Some(index) = s.schemas.borrow().get(&schema_type_name) {
+                    let frame = {
+                        let frames = s.frames.borrow();
+                        frames
+                            .get(*index)
+                            .expect(kcl_error::INTERNAL_ERROR_MSG)
+                            .clone()
+                    };
+                    if let Proxy::Schema(caller) = &frame.proxy {
+                        if value.is_config() {
+                            let config = value.as_dict_ref();
+                            for (key, _) in &config.values {
+                                let no_such_attr =
+                                    !SchemaEvalContext::has_attr(s, &caller.ctx, key)
+                                        && !key.starts_with('_');
+                                let has_index_signature =
+                                    SchemaEvalContext::has_index_signature(s, &caller.ctx);
+                                if !has_index_signature && no_such_attr {
+                                    error_msgs.push(format!(
+                                        "Schema {} does not contain attribute {}",
+                                        tpe, key
+                                    ));
+                                }
+                            }
+
+                            for (attr, is_optional) in SchemaEvalContext::get_attrs(s, &caller.ctx)
+                            {
+                                if !config.values.contains_key(&attr) && !is_optional {
+                                    error_msgs.push(format!(
+                                        "Schema {}'s attribute {} is missing",
+                                        tpe, attr
+                                    ));
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
         panic!(
-            "expect {expected_type}, got {}",
-            val_plan::type_of(value, true)
+            "expect {expected_type}, got {}{}",
+            val_plan::type_of(value, true),
+            if error_msgs.is_empty() {
+                "".to_string()
+            } else {
+                format!(". For details:\n{}", error_msgs.join("\n"))
+            }
         );
     }
     converted_value


Test patch:
diff --git a/test/grammar/types/runtime_ty/runtime_ty_err_0/stderr.golden b/test/grammar/types/runtime_ty/runtime_ty_err_0/stderr.golden
index 95f352159..aa232308f 100644
--- a/test/grammar/types/runtime_ty/runtime_ty_err_0/stderr.golden
+++ b/test/grammar/types/runtime_ty/runtime_ty_err_0/stderr.golden
@@ -2,5 +2,7 @@ error[E3M38]: EvaluationError
  --> ${CWD}/main.k:7:1
   |
 7 | person: Person = json.decode('{"err_name": "Alice", "age": 18}')
-  |  expect Person, got dict
+  |  expect Person, got dict. For details:
+Schema Person does not contain attribute err_name
+Schema Person's attribute name is missing
   |
\ No newline at end of file
diff --git a/test/grammar/types/type_as/type_as_err_2/stderr.golden b/test/grammar/types/type_as/type_as_err_2/stderr.golden
index 9d5e5183e..35e4fac94 100644
--- a/test/grammar/types/type_as/type_as_err_2/stderr.golden
+++ b/test/grammar/types/type_as/type_as_err_2/stderr.golden
@@ -2,5 +2,7 @@ error[E3M38]: EvaluationError
  --> ${CWD}/main.k:8:1
   |
 8 | bar = foo as Bar
-  |  expect Bar, got Foo
+  |  expect Bar, got Foo. For details:
+Schema Bar does not contain attribute foo
+Schema Bar's attribute bar is missing
   |
diff --git a/test/grammar/types/type_as/type_as_err_3/stderr.golden b/test/grammar/types/type_as/type_as_err_3/stderr.golden
index 8dc529e3c..d67a20374 100644
--- a/test/grammar/types/type_as/type_as_err_3/stderr.golden
+++ b/test/grammar/types/type_as/type_as_err_3/stderr.golden
@@ -2,5 +2,7 @@ error[E3M38]: EvaluationError
   --> ${CWD}/child/child.k:15:1
    |
 15 | base_a: A = _base_a as A
-   |  expect A, got base.A
+   |  expect A, got base.A. For details:
+Schema A does not contain attribute bar
+Schema A's attribute foo is missing
    |
diff --git a/test/grammar/types/type_as/type_as_err_4/stderr.golden b/test/grammar/types/type_as/type_as_err_4/stderr.golden
index 857ba7d21..d363e7192 100644
--- a/test/grammar/types/type_as/type_as_err_4/stderr.golden
+++ b/test/grammar/types/type_as/type_as_err_4/stderr.golden
@@ -2,5 +2,7 @@ error[E3M38]: EvaluationError
   --> ${CWD}/main.k:18:1
    |
 18 | child_a = _child_a as A
-   |  expect A, got child.A
+   |  expect A, got child.A. For details:
+Schema A does not contain attribute foo
+Schema A's attribute main is missing
    |