Skip to content

Commit be09eef

Browse files
authored
enhance: doc parser support schema example (#703)
* enhance: doc parser support schema example * chore: code style and format code
1 parent d3a1111 commit be09eef

File tree

9 files changed

+90
-15
lines changed

9 files changed

+90
-15
lines changed

kclvm/api/src/service/ty.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::gpyrpc::{Decorator, KclType};
1+
use crate::gpyrpc::{Decorator, Example, KclType};
22
use indexmap::IndexSet;
33
use kclvm_runtime::SCHEMA_SETTINGS_ATTR_NAME;
44
use kclvm_sema::ty::{DictType, SchemaType, Type};
@@ -37,6 +37,7 @@ pub(crate) fn kcl_schema_ty_to_pb_ty(schema_ty: &SchemaType) -> KclType {
3737
r#type: "schema".to_string(),
3838
schema_name: schema_ty.name.clone(),
3939
schema_doc: schema_ty.doc.clone(),
40+
examples: get_schema_ty_examples(schema_ty),
4041
properties: get_schema_ty_attributes(schema_ty, &mut 1),
4142
required: get_schema_ty_required_attributes(schema_ty),
4243
decorators: schema_ty
@@ -55,6 +56,19 @@ pub(crate) fn kcl_schema_ty_to_pb_ty(schema_ty: &SchemaType) -> KclType {
5556
}
5657
}
5758

59+
fn get_schema_ty_examples(schema_ty: &SchemaType) -> HashMap<String, Example> {
60+
let mut examples = HashMap::new();
61+
for (key, example) in &schema_ty.examples {
62+
let exa = Example {
63+
summary: example.summary.clone(),
64+
description: example.description.clone(),
65+
value: example.value.clone(),
66+
};
67+
examples.insert(key.clone(), exa);
68+
}
69+
examples
70+
}
71+
5872
fn get_schema_ty_attributes(schema_ty: &SchemaType, line: &mut i32) -> HashMap<String, KclType> {
5973
let mut base_type_mapping = if let Some(base) = &schema_ty.base {
6074
get_schema_ty_attributes(base, line)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"file": "schema.k",
3-
"code": "@info(name=\"ServerSchema\")\nschema Server:\n \"\"\"Server is the common user interface for long-running\n services adopting the best practice of Kubernetes.\n\n Attributes\n ----------\n workloadType: str, default is \"Deployment\", required\n Use this attribute to specify which kind of long-running service you want.\n Valid values: Deployment, CafeDeployment.\n See also: kusion_models/core/v1/workload_metadata.k.\n name: str, required\n A Server-level attribute.\n The name of the long-running service.\n See also: kusion_models/core/v1/metadata.k.\n labels: {str:str}, optional\n A Server-level attribute.\n The labels of the long-running service.\n See also: kusion_models/core/v1/metadata.k.\n\n Examples\n ----------------------\n myCustomApp = AppConfiguration {\n name = \"componentName\"\n }\n \"\"\"\n workloadType: str = \"Deployment\"\n @info(\"name\", key=\"value\")\n name: str\n labels?: {str:str}\n useCustomizeLables: True | False\n containers: [Container]\n\nschema Container:\n \"\"\"Container is the common user interface for long-running services.\n\n Attributes\n ----------\n name: str, required\n The name of the long-running container.\n \"\"\"\n name: str\n"
3+
"code": "@info(name=\"ServerSchema\")\nschema Server:\n \"\"\"Server is the common user interface for long-running\n services adopting the best practice of Kubernetes.\n\n Attributes\n ----------\n workloadType: str, default is \"Deployment\", required\n Use this attribute to specify which kind of long-running service you want.\n Valid values: Deployment, CafeDeployment.\n See also: kusion_models/core/v1/workload_metadata.k.\n name: str, required\n A Server-level attribute.\n The name of the long-running service.\n See also: kusion_models/core/v1/metadata.k.\n labels: {str:str}, optional\n A Server-level attribute.\n The labels of the long-running service.\n See also: kusion_models/core/v1/metadata.k.\n\n Examples\n --------\n myCustomApp = AppConfiguration {\n name = \"componentName\"\n }\n \"\"\"\n workloadType: str = \"Deployment\"\n @info(\"name\", key=\"value\")\n name: str\n labels?: {str:str}\n useCustomizeLables: True | False\n containers: [Container]\n\nschema Container:\n \"\"\"Container is the common user interface for long-running services.\n\n Attributes\n ----------\n name: str, required\n The name of the long-running container.\n \"\"\"\n name: str\n"
44
}

kclvm/api/src/testdata/get-schema-type-mapping.response.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,13 @@
206206
"filename": "schema.k",
207207
"pkg_path": "__main__",
208208
"description": "Server is the common user interface for long-running services adopting the best practice of Kubernetes.",
209-
"examples": {}
209+
"examples": {
210+
"Default example": {
211+
"summary": "",
212+
"description": "",
213+
"value": "myCustomApp = AppConfiguration {\n name = \"componentName\"\n}"
214+
}
215+
}
210216
},
211217
"Container": {
212218
"type": "schema",

kclvm/sema/src/resolver/doc.rs

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use pcre2::bytes::Regex;
2-
use std::collections::HashSet;
2+
use std::collections::{HashMap, HashSet};
33
use std::iter::Iterator;
44
use std::str;
55

@@ -259,7 +259,7 @@ fn parse_summary(doc: &mut Reader) -> String {
259259
/// The description of each attribute will be returned as separate lines.
260260
pub(crate) fn parse_doc_string(ori: &String) -> Doc {
261261
if ori.is_empty() {
262-
return Doc::new("".to_string(), vec![]);
262+
return Doc::new("".to_string(), vec![], HashMap::new());
263263
}
264264
let mut ori = ori.clone();
265265
strip_quotes(&mut ori);
@@ -274,19 +274,36 @@ pub(crate) fn parse_doc_string(ori: &String) -> Doc {
274274

275275
let attrs = parse_attr_list(attr_content);
276276

277-
Doc::new(summary, attrs)
277+
let mut examples = HashMap::new();
278+
let example_section = read_to_next_section(&mut doc);
279+
if !example_section.is_empty() {
280+
let default_example_content = match example_section.len() {
281+
0 | 1 | 2 => "".to_string(),
282+
_ => example_section[2..].join("\n"),
283+
};
284+
examples.insert(
285+
"Default example".to_string(),
286+
Example::new("".to_string(), "".to_string(), default_example_content),
287+
);
288+
}
289+
Doc::new(summary, attrs, examples)
278290
}
279291

280292
/// The Doc struct contains a summary of schema and all the attributes described in the the docstring.
281293
#[derive(Debug, PartialEq, Eq, Clone)]
282294
pub(crate) struct Doc {
283295
pub summary: String,
284296
pub attrs: Vec<Attribute>,
297+
pub examples: HashMap<String, Example>,
285298
}
286299

287300
impl Doc {
288-
fn new(summary: String, attrs: Vec<Attribute>) -> Self {
289-
Self { summary, attrs }
301+
fn new(summary: String, attrs: Vec<Attribute>, examples: HashMap<String, Example>) -> Self {
302+
Self {
303+
summary,
304+
attrs,
305+
examples,
306+
}
290307
}
291308
}
292309

@@ -303,10 +320,28 @@ impl Attribute {
303320
}
304321
}
305322

323+
/// The Example struct contains the example summary and the literal content
324+
#[derive(Debug, PartialEq, Eq, Clone)]
325+
pub struct Example {
326+
pub summary: String,
327+
pub description: String,
328+
pub value: String,
329+
}
330+
331+
impl Example {
332+
fn new(summary: String, description: String, value: String) -> Self {
333+
Self {
334+
summary,
335+
description,
336+
value,
337+
}
338+
}
339+
}
340+
306341
#[cfg(test)]
307342
mod tests {
308343
use super::{clean_doc, is_at_section, read_to_next_section, strip_quotes, Reader};
309-
use crate::resolver::doc::parse_doc_string;
344+
use crate::resolver::doc::{parse_doc_string, Example};
310345
use std::fs::File;
311346
use std::io::prelude::*;
312347
use std::path::PathBuf;
@@ -586,5 +621,17 @@ unindented line
586621
"See also: kusion_models/core/v1/metadata.k.".to_string(),
587622
]
588623
);
624+
assert!(doc.examples.contains_key("Default example"));
625+
assert_eq!(
626+
doc.examples.get("Default example"),
627+
Some(&Example::new(
628+
"".to_string(),
629+
"".to_string(),
630+
"myCustomApp = AppConfiguration {
631+
name = \"componentName\"
632+
}"
633+
.to_string()
634+
))
635+
);
589636
}
590637
}

kclvm/sema/src/resolver/global.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ impl<'ctx> Resolver<'ctx> {
6868
pkgpath: self.ctx.pkgpath.clone(),
6969
filename: self.ctx.filename.clone(),
7070
doc: parsed_doc.summary.clone(),
71+
examples: parsed_doc.examples,
7172
is_instance: false,
7273
is_mixin,
7374
is_protocol,
@@ -814,6 +815,7 @@ impl<'ctx> Resolver<'ctx> {
814815
pkgpath: self.ctx.pkgpath.clone(),
815816
filename: self.ctx.filename.clone(),
816817
doc: parsed_doc.summary.clone(),
818+
examples: parsed_doc.examples,
817819
is_instance: false,
818820
is_mixin: schema_stmt.is_mixin,
819821
is_protocol: schema_stmt.is_protocol,
@@ -918,11 +920,14 @@ impl<'ctx> Resolver<'ctx> {
918920
DecoratorTarget::Schema,
919921
&rule_stmt.name.node,
920922
);
923+
924+
let parsed_doc = parse_doc_string(&rule_stmt.doc);
921925
SchemaType {
922926
name: rule_stmt.name.node.clone(),
923927
pkgpath: self.ctx.pkgpath.clone(),
924928
filename: self.ctx.filename.clone(),
925-
doc: rule_stmt.doc.clone(),
929+
doc: parsed_doc.summary.clone(),
930+
examples: parsed_doc.examples,
926931
is_instance: false,
927932
is_mixin: false,
928933
is_protocol: false,

kclvm/sema/src/resolver/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ mod arg;
22
mod attr;
33
mod calculation;
44
mod config;
5-
mod doc;
5+
pub mod doc;
66
mod format;
77
pub mod global;
88
mod import;
@@ -26,12 +26,11 @@ use crate::lint::{CombinedLintPass, Linter};
2626
use crate::pre_process::pre_process_program;
2727
use crate::resolver::scope::ScopeObject;
2828
use crate::resolver::ty_alias::process_program_type_alias;
29+
use crate::ty::TypeContext;
2930
use crate::{resolver::scope::Scope, ty::SchemaType};
3031
use kclvm_ast::ast::Program;
3132
use kclvm_error::*;
3233

33-
use crate::ty::TypeContext;
34-
3534
use self::scope::{builtin_scope, ProgramScope};
3635

3736
/// Resolver is responsible for program semantic checking, mainly

kclvm/sema/src/resolver/test_data/doc.k

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ schema Server:
1919
See also: kusion_models/core/v1/metadata.k.
2020

2121
Examples
22-
----------------------
22+
--------
2323
myCustomApp = AppConfiguration {
2424
name = "componentName"
2525
}

kclvm/sema/src/ty/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ use kclvm_error::Position;
1919
pub use unify::*;
2020
pub use walker::walk_type;
2121

22+
use super::resolver::doc::Example;
23+
2224
#[cfg(test)]
2325
mod tests;
2426

@@ -175,6 +177,8 @@ pub struct SchemaType {
175177
pub filename: String,
176178
/// The schema definition document string.
177179
pub doc: String,
180+
/// The code snippets of the schema usage examples
181+
pub examples: HashMap<String, Example>,
178182
/// Indicates whether the schema is a type of a instance or
179183
/// a type (value). Besides, it is necessary to distinguish
180184
/// between a type instance and a type value, such as the following code:

kclvm/spec/gpyrpc/gpyrpc.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ message Decorator {
318318
message Example {
319319
string summary = 1; // Short description for the example.
320320
string description = 2; // Long description for the example.
321-
google.protobuf.Any value = 3; // Embedded literal example.
321+
string value = 3; // Embedded literal example.
322322
}
323323

324324
// ----------------------------------------------------------------------------

0 commit comments

Comments
 (0)