Skip to content

Commit

Permalink
Fix the issues of strip and merge desc for code block
Browse files Browse the repository at this point in the history
  • Loading branch information
chenyukang committed Oct 16, 2023
1 parent 9415fcb commit 53b7712
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 17 deletions.
70 changes: 70 additions & 0 deletions schemars/tests/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,59 @@ enum MyEnum {
},
}

#[allow(dead_code)]
#[derive(JsonSchema)]
/**
*
* # This is the struct's title
*
* This is the struct's description.
*
* This is example:
* ```json
* {
* "value": 0,
* "type": "msg"
* }
* ```
*/
struct MyStructWithInlineCode {
/// # An integer
my_int: i32,
}

#[allow(dead_code)]
#[derive(JsonSchema)]

/// This is example:
/// ```json
/// {
/// "value": 0,
/// "type": "msg"
/// }
/// ```
struct MyStructWithInlineCodeNormal {
/// # An integer
my_int: i32,
}

#[allow(dead_code)]
#[derive(JsonSchema)]

/// This is example:
///
/// | A | B |
/// |---|---|
/// | 1 | 2 |
/// | 3 | 4 |
/// | 5 | 6 |
/// this is last line
///
struct MyStructWithInlineCodeTable {
/// # An integer
my_int: i32,
}

#[test]
fn doc_comments_struct() -> TestResult {
test_default_generated_schema::<MyStruct>("doc_comments_struct")
Expand All @@ -70,6 +123,23 @@ fn doc_comments_enum() -> TestResult {
test_default_generated_schema::<MyEnum>("doc_comments_enum")
}

#[test]
fn doc_comments_with_inline_code() -> TestResult {
test_default_generated_schema::<MyStructWithInlineCode>("doc_comments_with_inline_code")
}

#[test]
fn doc_comments_with_inline_code_normal() -> TestResult {
test_default_generated_schema::<MyStructWithInlineCodeNormal>(
"doc_comments_with_inline_code_normal",
)
}

#[test]
fn doc_comments_with_inline_table() -> TestResult {
test_default_generated_schema::<MyStructWithInlineCodeTable>("doc_comments_with_inline_table")
}

/// # OverrideDocs struct
/// This description should be overridden
#[allow(dead_code)]
Expand Down
16 changes: 16 additions & 0 deletions schemars/tests/expected/doc_comments_with_inline_code.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "This is the struct's title",
"description": "This is the struct's description.\n\nThis is example:\n ```json\n {\n \"value\": 0,\n \"type\": \"msg\"\n }\n```",
"type": "object",
"required": [
"my_int"
],
"properties": {
"my_int": {
"title": "An integer",
"type": "integer",
"format": "int32"
}
}
}
16 changes: 16 additions & 0 deletions schemars/tests/expected/doc_comments_with_inline_code_normal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "MyStructWithInlineCodeNormal",
"description": "This is example:\n ```json\n {\n \"value\": 0,\n \"type\": \"msg\"\n }\n```",
"type": "object",
"required": [
"my_int"
],
"properties": {
"my_int": {
"title": "An integer",
"type": "integer",
"format": "int32"
}
}
}
16 changes: 16 additions & 0 deletions schemars/tests/expected/doc_comments_with_inline_table.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "MyStructWithInlineCodeTable",
"description": "This is example:\n\n| A | B |\n|---|---|\n| 1 | 2 |\n| 3 | 4 |\n| 5 | 6 | this is last line",
"type": "object",
"required": [
"my_int"
],
"properties": {
"my_int": {
"title": "An integer",
"type": "integer",
"format": "int32"
}
}
}
2 changes: 1 addition & 1 deletion schemars/tests/validate_inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub struct Struct<'a> {
#[schemars(inner(length(min = 5, max = 100)))]
array_str_length: [&'a str; 2],
#[schemars(inner(contains(pattern = "substring...")))]
slice_str_contains: &'a[&'a str],
slice_str_contains: &'a [&'a str],
#[schemars(inner(regex = "STARTS_WITH_HELLO"))]
vec_str_regex: Vec<String>,
#[schemars(inner(length(min = 1, max = 100)))]
Expand Down
76 changes: 60 additions & 16 deletions schemars_derive/src/attr/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ fn merge_description_lines(doc: &str) -> Option<String> {
let desc = doc
.trim()
.split("\n\n")
.filter_map(|line| none_if_empty(line.trim().replace('\n', " ")))
.map(|s| s.to_string())
.collect::<Vec<_>>();

let desc = desc
.iter()
.map(|paragrah| merge_without_codeblock(paragrah))
.filter_map(|line| none_if_empty(line.to_string()))
.collect::<Vec<_>>()
.join("\n\n");
none_if_empty(desc)
Expand All @@ -47,30 +53,22 @@ fn get_doc(attrs: &[Attribute]) -> Option<String> {
None
})
.collect::<Vec<_>>();

let mut lines = attrs
let lines = attrs
.iter()
.flat_map(|a| a.split('\n'))
.map(str::trim)
.skip_while(|s| s.is_empty())
.map(|l| l.to_string())
.collect::<Vec<_>>();

if let Some(&"") = lines.last() {
lines.pop();
}

let mut res = strip_without_codeblock(&lines, |l| l.trim().to_string());
// Added for backward-compatibility, but perhaps we shouldn't do this
// https://github.com/rust-lang/rust/issues/32088
if lines.iter().all(|l| l.starts_with('*')) {
for line in lines.iter_mut() {
*line = line[1..].trim()
}
while let Some(&"") = lines.first() {
lines.remove(0);
}
if res.iter().all(|l| l.trim().starts_with('*')) {
res = res.iter().map(|l| l[1..].to_string()).collect::<Vec<_>>();
res = strip_without_codeblock(&res, |l| l.trim().to_string());
};

none_if_empty(lines.join("\n"))
none_if_empty(res.join("\n"))
}

fn none_if_empty(s: String) -> Option<String> {
Expand All @@ -80,3 +78,49 @@ fn none_if_empty(s: String) -> Option<String> {
Some(s)
}
}

fn strip_without_codeblock(lines: &Vec<String>, f: fn(&str) -> String) -> Vec<String> {
let mut res = vec![];
let mut in_codeblock = false;
for line in lines {
if line.trim().starts_with("```") {
in_codeblock = !in_codeblock;
}
if in_codeblock {
res.push(line.to_string());
} else {
res.push(f(line));
}
}
while let Some("") = res.first().map(|s| s.as_str()) {
res.remove(0);
}
while let Some("") = res.last().map(|s| s.as_str()) {
res.pop();
}
res
}

fn merge_without_codeblock(content: &str) -> String {
let lines = content.lines();
let mut res = String::new();
let mut in_codeblock = false;
for line in lines {
let flag = line.trim().starts_with("```");
if flag {
in_codeblock = !in_codeblock;
}
// other possible Markdown prefix characters
let maybe_markdown = ["#", "-", ">", "|", "*", "["]
.iter()
.any(|p| line.trim().starts_with(p))
|| line.trim().chars().next().map(char::is_numeric) == Some(true);
let prefix = if in_codeblock || flag || maybe_markdown {
"\n"
} else {
" "
};
res += &(format!("{}{}", prefix, line));
}
res.trim().to_string()
}

0 comments on commit 53b7712

Please sign in to comment.