Skip to content

Commit 1fd5c10

Browse files
LegNeatotheduke
authored and
theduke
committed
Add support for using doc comments as descriptions
Fixes #194.
1 parent e841672 commit 1fd5c10

File tree

10 files changed

+337
-6
lines changed

10 files changed

+337
-6
lines changed

changelog/master.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
# [master] yyyy-mm-dd
22

33
## Changes
4+
45
* Changed serialization of `NaiveDate` when using the optional `chronos` support.
56

67
**Note:** while this is not a Rust breaking change, if you relied on the serialization format (perhaps by storing serialized data in a database or making asumptions in your client code written in another language) it could be a breaking change for your application.
78

89
[#151](https://github.com/graphql-rust/juniper/pull/151)
910

1011
* The `GraphQLObject`, `GraphQLInputObject`, and `GraphQLEnum` custom derives will reject
11-
invalid [names](http://facebook.github.io/graphql/October2016/#Name) at compile time.
12+
invalid [names](http://facebook.github.io/graphql/October2016/#Name) at compile time.
1213

1314
[#170](https://github.com/graphql-rust/juniper/pull/170)
1415

@@ -19,4 +20,11 @@
1920
fractional part could not be decoded (because they are represented without
2021
a decimal part `.0`).
2122

22-
[#179](https://github.com/graphql-rust/juniper/pull/179)
23+
[#179](https://github.com/graphql-rust/juniper/pull/179)
24+
25+
* The `GraphQLObject`, `GraphQLInputObject`, and `GraphQLEnum` custom derives
26+
now parse doc strings and use them as descriptions. This behavior can be
27+
overridden by using an explicit GraphQL `description` annotation such as
28+
`#[graphql(description = "my description")]`.
29+
30+
[#194](https://github.com/graphql-rust/juniper/issues/194)

juniper_codegen/src/derive_enum.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ impl EnumAttrs {
2828
internal: false,
2929
};
3030

31+
// Check doc comments for description.
32+
res.description = get_doc_comment(&input.attrs);
33+
3134
// Check attributes for name and description.
3235
if let Some(items) = get_graphl_attr(&input.attrs) {
3336
for item in items {
@@ -74,6 +77,9 @@ impl EnumVariantAttrs {
7477
fn from_input(variant: &Variant) -> EnumVariantAttrs {
7578
let mut res = EnumVariantAttrs::default();
7679

80+
// Check doc comments for description.
81+
res.description = get_doc_comment(&variant.attrs);
82+
7783
// Check attributes for name and description.
7884
if let Some(items) = get_graphl_attr(&variant.attrs) {
7985
for item in items {

juniper_codegen/src/derive_input_object.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ impl ObjAttrs {
2525
fn from_input(input: &DeriveInput) -> ObjAttrs {
2626
let mut res = ObjAttrs::default();
2727

28+
// Check doc comments for description.
29+
res.description = get_doc_comment(&input.attrs);
30+
2831
// Check attributes for name and description.
2932
if let Some(items) = get_graphl_attr(&input.attrs) {
3033
for item in items {
@@ -72,6 +75,9 @@ impl ObjFieldAttrs {
7275
fn from_input(variant: &Field) -> ObjFieldAttrs {
7376
let mut res = ObjFieldAttrs::default();
7477

78+
// Check doc comments for description.
79+
res.description = get_doc_comment(&variant.attrs);
80+
7581
// Check attributes for name and description.
7682
if let Some(items) = get_graphl_attr(&variant.attrs) {
7783
for item in items {

juniper_codegen/src/derive_object.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ impl ObjAttrs {
1919
fn from_input(input: &DeriveInput) -> ObjAttrs {
2020
let mut res = ObjAttrs::default();
2121

22+
// Check doc comments for description.
23+
res.description = get_doc_comment(&input.attrs);
24+
2225
// Check attributes for name and description.
2326
if let Some(items) = get_graphl_attr(&input.attrs) {
2427
for item in items {
@@ -56,6 +59,9 @@ impl ObjFieldAttrs {
5659
fn from_input(variant: &Field) -> ObjFieldAttrs {
5760
let mut res = ObjFieldAttrs::default();
5861

62+
// Check doc comments for description.
63+
res.description = get_doc_comment(&variant.attrs);
64+
5965
// Check attributes for name and description.
6066
if let Some(items) = get_graphl_attr(&variant.attrs) {
6167
for item in items {

juniper_codegen/src/util.rs

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,67 @@
11
use syn::{
22
Attribute,
33
Meta,
4+
MetaNameValue,
45
NestedMeta,
56
Lit,
67
};
78
use regex::Regex;
89

10+
// Gets doc comment.
11+
pub fn get_doc_comment(attrs: &Vec<Attribute>) -> Option<String> {
12+
if let Some(items) = get_doc_attr(attrs) {
13+
if let Some(doc_strings) = get_doc_strings(&items) {
14+
return Some(join_doc_strings(&doc_strings));
15+
}
16+
}
17+
None
18+
}
19+
20+
// Concatenates doc strings into one string.
21+
fn join_doc_strings(docs: &Vec<String>) -> String {
22+
let s: String = docs.iter()
23+
// Convert empty comments to newlines.
24+
.map(|x| if x == "" { "\n".to_string() } else { x.clone() })
25+
.collect::<Vec<String>>()
26+
.join(" ");
27+
// Clean up spacing on empty lines.
28+
s.replace(" \n ", "\n")
29+
}
30+
31+
// Gets doc strings from doc comment attributes.
32+
fn get_doc_strings(items: &Vec<MetaNameValue>) -> Option<Vec<String>> {
33+
let mut docs = Vec::new();
34+
for item in items {
35+
match item.lit {
36+
Lit::Str(ref strlit) => {
37+
docs.push(strlit.value().trim().to_string());
38+
},
39+
_ => panic!("doc attributes only have string literal"),
40+
}
41+
}
42+
if !docs.is_empty() {
43+
return Some(docs);
44+
}
45+
None
46+
}
47+
48+
// Gets doc comment attributes.
49+
fn get_doc_attr(attrs: &Vec<Attribute>) -> Option<Vec<MetaNameValue>> {
50+
let mut docs = Vec::new();
51+
for attr in attrs {
52+
match attr.interpret_meta() {
53+
Some(Meta::NameValue(ref nv)) if nv.ident == "doc" => {
54+
docs.push(nv.clone())
55+
}
56+
_ => {}
57+
}
58+
}
59+
if !docs.is_empty() {
60+
return Some(docs);
61+
}
62+
None
63+
}
64+
965
// Get the nested items of a a #[graphql(...)] attribute.
1066
pub fn get_graphl_attr(attrs: &Vec<Attribute>) -> Option<Vec<NestedMeta>> {
1167
for attr in attrs {
@@ -124,12 +180,12 @@ pub fn is_valid_name(field_name: &str) -> bool {
124180
#[test]
125181
fn test_is_valid_name(){
126182
assert_eq!(is_valid_name("yesItIs"), true);
127-
assert_eq!(is_valid_name("NoitIsnt"), true);
128-
assert_eq!(is_valid_name("iso6301"), true);
129-
assert_eq!(is_valid_name("thisIsATest"), true);
183+
assert_eq!(is_valid_name("NoitIsnt"), true);
184+
assert_eq!(is_valid_name("iso6301"), true);
185+
assert_eq!(is_valid_name("thisIsATest"), true);
130186
assert_eq!(is_valid_name("i6Op"), true);
131187
assert_eq!(is_valid_name("i!"), false);
132-
assert_eq!(is_valid_name(""), false);
188+
assert_eq!(is_valid_name(""), false);
133189
assert_eq!(is_valid_name("aTest"), true);
134190
assert_eq!(is_valid_name("__Atest90"), true);
135191
}

juniper_tests/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ serde_json = { version = "1" }
88

99
[dev-dependencies]
1010
fnv = "1.0.3"
11+
indexmap = "1.0"
1112

1213
[[test]]
1314
name = "integration_tests"

juniper_tests/src/codegen/derive_enum.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,33 @@ enum SomeEnum {
1111
#[graphql(name = "FULL", description = "field descr", deprecated = "depr")] Full,
1212
}
1313

14+
/// Enum doc.
15+
#[derive(GraphQLEnum)]
16+
enum DocEnum {
17+
/// Variant doc.
18+
Foo,
19+
}
20+
21+
/// Doc 1.
22+
/// Doc 2.
23+
///
24+
/// Doc 4.
25+
#[derive(GraphQLEnum, Debug, PartialEq)]
26+
enum MultiDocEnum {
27+
/// Variant 1.
28+
/// Variant 2.
29+
Foo,
30+
}
31+
32+
/// This is not used as the description.
33+
#[derive(GraphQLEnum, Debug, PartialEq)]
34+
#[graphql(description = "enum override")]
35+
enum OverrideDocEnum {
36+
/// This is not used as the description.
37+
#[graphql(description = "variant override")]
38+
Foo,
39+
}
40+
1441
#[test]
1542
fn test_derived_enum() {
1643
// Ensure that rename works.
@@ -43,3 +70,25 @@ fn test_derived_enum() {
4370
Some(SomeEnum::Full)
4471
);
4572
}
73+
74+
#[test]
75+
fn test_doc_comment() {
76+
let mut registry = juniper::Registry::new(FnvHashMap::default());
77+
let meta = DocEnum::meta(&(), &mut registry);
78+
assert_eq!(meta.description(), Some(&"Enum doc.".to_string()));
79+
}
80+
81+
#[test]
82+
fn test_multi_doc_comment() {
83+
let mut registry = juniper::Registry::new(FnvHashMap::default());
84+
let meta = MultiDocEnum::meta(&(), &mut registry);
85+
assert_eq!(meta.description(), Some(&"Doc 1. Doc 2.\nDoc 4.".to_string()));
86+
}
87+
88+
#[test]
89+
fn test_doc_comment_override() {
90+
let mut registry = juniper::Registry::new(FnvHashMap::default());
91+
let meta = OverrideDocEnum::meta(&(), &mut registry);
92+
assert_eq!(meta.description(), Some(&"enum override".to_string()));
93+
94+
}

juniper_tests/src/codegen/derive_input_object.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,33 @@ struct Input {
1313
#[graphql(default)] other: Option<bool>,
1414
}
1515

16+
/// Object comment.
17+
#[derive(GraphQLInputObject, Debug, PartialEq)]
18+
struct DocComment {
19+
/// Field comment.
20+
regular_field: bool,
21+
}
22+
23+
/// Doc 1.
24+
/// Doc 2.
25+
///
26+
/// Doc 4.
27+
#[derive(GraphQLInputObject, Debug, PartialEq)]
28+
struct MultiDocComment {
29+
/// Field 1.
30+
/// Field 2.
31+
regular_field: bool,
32+
}
33+
34+
/// This is not used as the description.
35+
#[derive(GraphQLInputObject, Debug, PartialEq)]
36+
#[graphql(description = "obj override")]
37+
struct OverrideDocComment {
38+
/// This is not used as the description.
39+
#[graphql(description = "field override")]
40+
regular_field: bool,
41+
}
42+
1643
#[test]
1744
fn test_derived_input_object() {
1845
assert_eq!(Input::name(&()), Some("MyInput"));
@@ -57,3 +84,24 @@ fn test_derived_input_object() {
5784
}
5885
);
5986
}
87+
88+
#[test]
89+
fn test_doc_comment() {
90+
let mut registry = juniper::Registry::new(FnvHashMap::default());
91+
let meta = DocComment::meta(&(), &mut registry);
92+
assert_eq!(meta.description(), Some(&"Object comment.".to_string()));
93+
}
94+
95+
#[test]
96+
fn test_multi_doc_comment() {
97+
let mut registry = juniper::Registry::new(FnvHashMap::default());
98+
let meta = MultiDocComment::meta(&(), &mut registry);
99+
assert_eq!(meta.description(), Some(&"Doc 1. Doc 2.\nDoc 4.".to_string()));
100+
}
101+
102+
#[test]
103+
fn test_doc_comment_override() {
104+
let mut registry = juniper::Registry::new(FnvHashMap::default());
105+
let meta = OverrideDocComment::meta(&(), &mut registry);
106+
assert_eq!(meta.description(), Some(&"obj override".to_string()));
107+
}

0 commit comments

Comments
 (0)