Skip to content

Commit

Permalink
enhance: doc parser support schema example (#703)
Browse files Browse the repository at this point in the history
* enhance: doc parser support schema example

* chore: code style and format code
  • Loading branch information
amyXia1994 authored Sep 11, 2023
1 parent d3a1111 commit be09eef
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 15 deletions.
16 changes: 15 additions & 1 deletion kclvm/api/src/service/ty.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::gpyrpc::{Decorator, KclType};
use crate::gpyrpc::{Decorator, Example, KclType};
use indexmap::IndexSet;
use kclvm_runtime::SCHEMA_SETTINGS_ATTR_NAME;
use kclvm_sema::ty::{DictType, SchemaType, Type};
Expand Down Expand Up @@ -37,6 +37,7 @@ pub(crate) fn kcl_schema_ty_to_pb_ty(schema_ty: &SchemaType) -> KclType {
r#type: "schema".to_string(),
schema_name: schema_ty.name.clone(),
schema_doc: schema_ty.doc.clone(),
examples: get_schema_ty_examples(schema_ty),
properties: get_schema_ty_attributes(schema_ty, &mut 1),
required: get_schema_ty_required_attributes(schema_ty),
decorators: schema_ty
Expand All @@ -55,6 +56,19 @@ pub(crate) fn kcl_schema_ty_to_pb_ty(schema_ty: &SchemaType) -> KclType {
}
}

fn get_schema_ty_examples(schema_ty: &SchemaType) -> HashMap<String, Example> {
let mut examples = HashMap::new();
for (key, example) in &schema_ty.examples {
let exa = Example {
summary: example.summary.clone(),
description: example.description.clone(),
value: example.value.clone(),
};
examples.insert(key.clone(), exa);
}
examples
}

fn get_schema_ty_attributes(schema_ty: &SchemaType, line: &mut i32) -> HashMap<String, KclType> {
let mut base_type_mapping = if let Some(base) = &schema_ty.base {
get_schema_ty_attributes(base, line)
Expand Down
2 changes: 1 addition & 1 deletion kclvm/api/src/testdata/get-schema-type-mapping.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"file": "schema.k",
"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"
"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"
}
8 changes: 7 additions & 1 deletion kclvm/api/src/testdata/get-schema-type-mapping.response.json
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,13 @@
"filename": "schema.k",
"pkg_path": "__main__",
"description": "Server is the common user interface for long-running services adopting the best practice of Kubernetes.",
"examples": {}
"examples": {
"Default example": {
"summary": "",
"description": "",
"value": "myCustomApp = AppConfiguration {\n name = \"componentName\"\n}"
}
}
},
"Container": {
"type": "schema",
Expand Down
59 changes: 53 additions & 6 deletions kclvm/sema/src/resolver/doc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use pcre2::bytes::Regex;
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::iter::Iterator;
use std::str;

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

let attrs = parse_attr_list(attr_content);

Doc::new(summary, attrs)
let mut examples = HashMap::new();
let example_section = read_to_next_section(&mut doc);
if !example_section.is_empty() {
let default_example_content = match example_section.len() {
0 | 1 | 2 => "".to_string(),
_ => example_section[2..].join("\n"),
};
examples.insert(
"Default example".to_string(),
Example::new("".to_string(), "".to_string(), default_example_content),
);
}
Doc::new(summary, attrs, examples)
}

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

impl Doc {
fn new(summary: String, attrs: Vec<Attribute>) -> Self {
Self { summary, attrs }
fn new(summary: String, attrs: Vec<Attribute>, examples: HashMap<String, Example>) -> Self {
Self {
summary,
attrs,
examples,
}
}
}

Expand All @@ -303,10 +320,28 @@ impl Attribute {
}
}

/// The Example struct contains the example summary and the literal content
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Example {
pub summary: String,
pub description: String,
pub value: String,
}

impl Example {
fn new(summary: String, description: String, value: String) -> Self {
Self {
summary,
description,
value,
}
}
}

#[cfg(test)]
mod tests {
use super::{clean_doc, is_at_section, read_to_next_section, strip_quotes, Reader};
use crate::resolver::doc::parse_doc_string;
use crate::resolver::doc::{parse_doc_string, Example};
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
Expand Down Expand Up @@ -586,5 +621,17 @@ unindented line
"See also: kusion_models/core/v1/metadata.k.".to_string(),
]
);
assert!(doc.examples.contains_key("Default example"));
assert_eq!(
doc.examples.get("Default example"),
Some(&Example::new(
"".to_string(),
"".to_string(),
"myCustomApp = AppConfiguration {
name = \"componentName\"
}"
.to_string()
))
);
}
}
7 changes: 6 additions & 1 deletion kclvm/sema/src/resolver/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ impl<'ctx> Resolver<'ctx> {
pkgpath: self.ctx.pkgpath.clone(),
filename: self.ctx.filename.clone(),
doc: parsed_doc.summary.clone(),
examples: parsed_doc.examples,
is_instance: false,
is_mixin,
is_protocol,
Expand Down Expand Up @@ -814,6 +815,7 @@ impl<'ctx> Resolver<'ctx> {
pkgpath: self.ctx.pkgpath.clone(),
filename: self.ctx.filename.clone(),
doc: parsed_doc.summary.clone(),
examples: parsed_doc.examples,
is_instance: false,
is_mixin: schema_stmt.is_mixin,
is_protocol: schema_stmt.is_protocol,
Expand Down Expand Up @@ -918,11 +920,14 @@ impl<'ctx> Resolver<'ctx> {
DecoratorTarget::Schema,
&rule_stmt.name.node,
);

let parsed_doc = parse_doc_string(&rule_stmt.doc);
SchemaType {
name: rule_stmt.name.node.clone(),
pkgpath: self.ctx.pkgpath.clone(),
filename: self.ctx.filename.clone(),
doc: rule_stmt.doc.clone(),
doc: parsed_doc.summary.clone(),
examples: parsed_doc.examples,
is_instance: false,
is_mixin: false,
is_protocol: false,
Expand Down
5 changes: 2 additions & 3 deletions kclvm/sema/src/resolver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod arg;
mod attr;
mod calculation;
mod config;
mod doc;
pub mod doc;
mod format;
pub mod global;
mod import;
Expand All @@ -26,12 +26,11 @@ use crate::lint::{CombinedLintPass, Linter};
use crate::pre_process::pre_process_program;
use crate::resolver::scope::ScopeObject;
use crate::resolver::ty_alias::process_program_type_alias;
use crate::ty::TypeContext;
use crate::{resolver::scope::Scope, ty::SchemaType};
use kclvm_ast::ast::Program;
use kclvm_error::*;

use crate::ty::TypeContext;

use self::scope::{builtin_scope, ProgramScope};

/// Resolver is responsible for program semantic checking, mainly
Expand Down
2 changes: 1 addition & 1 deletion kclvm/sema/src/resolver/test_data/doc.k
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ schema Server:
See also: kusion_models/core/v1/metadata.k.

Examples
----------------------
--------
myCustomApp = AppConfiguration {
name = "componentName"
}
Expand Down
4 changes: 4 additions & 0 deletions kclvm/sema/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ use kclvm_error::Position;
pub use unify::*;
pub use walker::walk_type;

use super::resolver::doc::Example;

#[cfg(test)]
mod tests;

Expand Down Expand Up @@ -175,6 +177,8 @@ pub struct SchemaType {
pub filename: String,
/// The schema definition document string.
pub doc: String,
/// The code snippets of the schema usage examples
pub examples: HashMap<String, Example>,
/// Indicates whether the schema is a type of a instance or
/// a type (value). Besides, it is necessary to distinguish
/// between a type instance and a type value, such as the following code:
Expand Down
2 changes: 1 addition & 1 deletion kclvm/spec/gpyrpc/gpyrpc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ message Decorator {
message Example {
string summary = 1; // Short description for the example.
string description = 2; // Long description for the example.
google.protobuf.Any value = 3; // Embedded literal example.
string value = 3; // Embedded literal example.
}

// ----------------------------------------------------------------------------
Expand Down

0 comments on commit be09eef

Please sign in to comment.