diff --git a/kclvm/api/src/testdata/get-schema-type-mapping.json b/kclvm/api/src/testdata/get-schema-type-mapping.json index bdd4a8ed2..b4ccfbeea 100644 --- a/kclvm/api/src/testdata/get-schema-type-mapping.json +++ b/kclvm/api/src/testdata/get-schema-type-mapping.json @@ -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" } diff --git a/kclvm/sema/src/resolver/doc.rs b/kclvm/sema/src/resolver/doc.rs index 7b4c88b0a..93fa4f6fd 100644 --- a/kclvm/sema/src/resolver/doc.rs +++ b/kclvm/sema/src/resolver/doc.rs @@ -1,5 +1,5 @@ use pcre2::bytes::Regex; -use std::collections::HashSet; +use std::collections::{HashSet, HashMap}; use std::iter::Iterator; use std::str; @@ -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); @@ -274,7 +274,16 @@ pub(crate) fn parse_doc_string(ori: &String) -> Doc { let attrs = parse_attr_list(attr_content); - Doc::new(summary, attrs) + let example_section = read_to_next_section(&mut doc); + let default_example_content = match example_section.len() { + 0 | 1 | 2 => "".to_string(), + _ => example_section[2..].join("\n"), + }; + let mut examples = HashMap::new(); + 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. @@ -282,11 +291,12 @@ pub(crate) fn parse_doc_string(ori: &String) -> Doc { pub(crate) struct Doc { pub summary: String, pub attrs: Vec, + pub examples: HashMap, } impl Doc { - fn new(summary: String, attrs: Vec) -> Self { - Self { summary, attrs } + fn new(summary: String, attrs: Vec, examples: HashMap) -> Self { + Self { summary, attrs, examples: examples } } } @@ -303,10 +313,24 @@ 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; @@ -586,5 +610,9 @@ 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()))); } } diff --git a/kclvm/sema/src/resolver/global.rs b/kclvm/sema/src/resolver/global.rs index 34527a564..eb9d5d374 100644 --- a/kclvm/sema/src/resolver/global.rs +++ b/kclvm/sema/src/resolver/global.rs @@ -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, @@ -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, @@ -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, diff --git a/kclvm/sema/src/resolver/mod.rs b/kclvm/sema/src/resolver/mod.rs index fdae6e513..3394d0a82 100644 --- a/kclvm/sema/src/resolver/mod.rs +++ b/kclvm/sema/src/resolver/mod.rs @@ -2,7 +2,7 @@ mod arg; mod attr; mod calculation; mod config; -mod doc; +pub mod doc; mod format; pub mod global; mod import; @@ -29,7 +29,6 @@ use crate::resolver::ty_alias::process_program_type_alias; 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}; diff --git a/kclvm/sema/src/resolver/test_data/doc.k b/kclvm/sema/src/resolver/test_data/doc.k index 397bf963c..5ac8e6154 100644 --- a/kclvm/sema/src/resolver/test_data/doc.k +++ b/kclvm/sema/src/resolver/test_data/doc.k @@ -19,7 +19,7 @@ schema Server: See also: kusion_models/core/v1/metadata.k. Examples - ---------------------- + -------- myCustomApp = AppConfiguration { name = "componentName" } diff --git a/kclvm/sema/src/ty/mod.rs b/kclvm/sema/src/ty/mod.rs index b193e387a..463bbc36d 100644 --- a/kclvm/sema/src/ty/mod.rs +++ b/kclvm/sema/src/ty/mod.rs @@ -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; @@ -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, /// 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: