diff --git a/crates/rover-client/src/operations/graph/introspect/schema.rs b/crates/rover-client/src/operations/graph/introspect/schema.rs index 796cb93e7..a48dbbac8 100644 --- a/crates/rover-client/src/operations/graph/introspect/schema.rs +++ b/crates/rover-client/src/operations/graph/introspect/schema.rs @@ -531,9 +531,13 @@ mod tests { type Species implements Node { """The name of this species.""" name: String - """The classification of this species, such as "mammal" or "reptile".""" + """ + The classification of this species, such as "mammal" or "reptile". + """ classification: String - """The designation of this species, such as "sentient".""" + """ + The designation of this species, such as "sentient". + """ designation: String """The average height of this species in centimeters.""" averageHeight: Float @@ -750,7 +754,9 @@ mod tests { } """A single transport craft that has hyperdrive capability.""" type Starship implements Node { - """The name of this starship. The common name, such as "Death Star".""" + """ + The name of this starship. The common name, such as "Death Star". + """ name: String """ The model or official name of this starship. Such as "T-65 X-wing" or "DS-1 @@ -905,7 +911,9 @@ mod tests { Transport". """ model: String - """The class of this vehicle, such as "Wheeled" or "Repulsorcraft".""" + """ + The class of this vehicle, such as "Wheeled" or "Repulsorcraft". + """ vehicleClass: String """The manufacturers of this vehicle.""" manufacturers: [String] diff --git a/crates/sdl-encoder/src/description.rs b/crates/sdl-encoder/src/description.rs new file mode 100644 index 000000000..f0fe59ec7 --- /dev/null +++ b/crates/sdl-encoder/src/description.rs @@ -0,0 +1,135 @@ +use std::fmt::{self, Display}; + +#[derive(Debug, PartialEq, Clone)] +/// Convenience enum to create a Description. Can be a `Top` level, a `Field` +/// level or an `Input` level. The variants are distinguished by the way they +/// get displayed, e.g. number of leading spaces. +pub enum Description { + /// Top-level description. + Top { + /// Description. + source: Option, + }, + /// Field-level description. + /// This description gets additional leading spaces. + Field { + /// Description. + source: Option, + }, + /// Input-level description. + /// This description get an additional space at the end. + Input { + /// Description. + source: Option, + }, +} + +impl Display for Description { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Description::Top { source } => { + if let Some(description) = source { + if is_block_string_character(description) { + writeln!(f, "\"\"\"\n{}\n\"\"\"", description)? + } else { + writeln!(f, "\"\"\"{}\"\"\"", description)? + } + } + } + Description::Field { source } => { + if let Some(description) = source { + if is_block_string_character(description) { + writeln!(f, " \"\"\"\n {}\n \"\"\"", description)? + } else { + writeln!(f, " \"\"\"{}\"\"\"", description)? + } + } + } + Description::Input { source } => { + if let Some(description) = source { + if is_block_string_character(description) { + write!(f, "\"\"\"\n{}\n\"\"\" ", description)? + } else { + write!(f, "\"\"\"{}\"\"\" ", description)? + } + } + } + } + write!(f, "") + } +} + +fn is_block_string_character(s: &str) -> bool { + s.contains('\n') || s.contains('"') || s.contains('\r') +} +#[cfg(test)] +mod test { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn it_encodes_description_without_block_string_character() { + let desc = Description::Top { + source: Some( + "Favourite cat nap spots include: plant corner, pile of clothes.".to_string(), + ), + }; + + assert_eq!( + desc.to_string(), + r#""""Favourite cat nap spots include: plant corner, pile of clothes.""" +"# + ); + } + + #[test] + fn it_encodes_description_with_quotations() { + let desc = Description::Top { + source: Some( + "Favourite \"cat\" nap spots include: plant corner, pile of clothes.".to_string(), + ), + }; + + assert_eq!( + desc.to_string(), + r#"""" +Favourite "cat" nap spots include: plant corner, pile of clothes. +""" +"# + ); + } + + #[test] + fn it_encodes_description_with_new_line() { + let desc = Description::Top { + source: Some( + "Favourite cat nap spots include:\nplant corner, pile of clothes.".to_string(), + ), + }; + + assert_eq!( + desc.to_string(), + r#"""" +Favourite cat nap spots include: +plant corner, pile of clothes. +""" +"# + ); + } + + #[test] + fn it_encodes_description_with_carriage_return() { + let desc = Description::Top { + source: Some( + "Favourite cat nap spots include:\rplant corner,\rpile of clothes.".to_string(), + ), + }; + + assert_eq!( + desc.to_string(), + String::from( + "\"\"\"\nFavourite cat nap spots include:\rplant corner,\rpile of clothes.\n\"\"\"\n" + ) + ); + } +} diff --git a/crates/sdl-encoder/src/directive_def.rs b/crates/sdl-encoder/src/directive_def.rs index 6ef9bf951..7d4418824 100644 --- a/crates/sdl-encoder/src/directive_def.rs +++ b/crates/sdl-encoder/src/directive_def.rs @@ -1,4 +1,4 @@ -use crate::InputValue; +use crate::{Description, InputValue}; use std::fmt::{self, Display}; /// The `__Directive` type represents a Directive that a service supports. @@ -34,7 +34,7 @@ pub struct Directive { // Name must return a String. name: String, // Description may return a String or null. - description: Option, + description: Description, // Args returns a Vector of __InputValue representing the arguments this // directive accepts. args: Vec, @@ -48,7 +48,7 @@ impl Directive { pub fn new(name: String) -> Self { Self { name, - description: None, + description: Description::Top { source: None }, args: Vec::new(), locations: Vec::new(), } @@ -56,7 +56,9 @@ impl Directive { /// Set the Directive's description. pub fn description(&mut self, description: Option) { - self.description = description; + self.description = Description::Top { + source: description, + }; } /// Set the Directive's location. @@ -72,16 +74,7 @@ impl Directive { impl Display for Directive { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(description) = &self.description { - // We are determing on whether to have description formatted as - // a multiline comment based on whether or not it already includes a - // \n. - match description.contains('\n') { - true => writeln!(f, "\"\"\"\n{}\n\"\"\"", description)?, - false => writeln!(f, "\"\"\"{}\"\"\"", description)?, - } - } - + write!(f, "{}", self.description)?; write!(f, "directive @{}", self.name)?; if !self.args.is_empty() { diff --git a/crates/sdl-encoder/src/enum_def.rs b/crates/sdl-encoder/src/enum_def.rs index 59bdff003..07ba3787a 100644 --- a/crates/sdl-encoder/src/enum_def.rs +++ b/crates/sdl-encoder/src/enum_def.rs @@ -1,4 +1,4 @@ -use crate::EnumValue; +use crate::{Description, EnumValue}; use std::fmt::{self, Display}; /// Enums are special scalars that can only have a defined set of values. @@ -41,7 +41,7 @@ pub struct EnumDef { // Name must return a String. name: String, // Description may return a String or null. - description: Option, + description: Description, // A vector of EnumValue. There must be at least one and they must have // unique names. values: Vec, @@ -52,14 +52,16 @@ impl EnumDef { pub fn new(name: String) -> Self { Self { name, - description: None, + description: Description::Top { source: None }, values: Vec::new(), } } /// Set the Enum Definition's description. pub fn description(&mut self, description: Option) { - self.description = description; + self.description = Description::Top { + source: description, + }; } /// Set the Enum Definitions's values. @@ -70,16 +72,7 @@ impl EnumDef { impl Display for EnumDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(description) = &self.description { - // We are determing on whether to have description formatted as - // a multiline comment based on whether or not it already includes a - // \n. - match description.contains('\n') { - true => writeln!(f, "\"\"\"\n{}\n\"\"\"", description)?, - false => writeln!(f, "\"\"\"{}\"\"\"", description)?, - } - } - + write!(f, "{}", self.description)?; write!(f, "enum {} {{", self.name)?; for value in &self.values { write!(f, "\n{}", value)?; diff --git a/crates/sdl-encoder/src/enum_value.rs b/crates/sdl-encoder/src/enum_value.rs index 2c542a6a2..322e0d89d 100644 --- a/crates/sdl-encoder/src/enum_value.rs +++ b/crates/sdl-encoder/src/enum_value.rs @@ -1,5 +1,7 @@ use std::fmt::{self, Display}; +use crate::Description; + /// The __EnumValue type represents one of possible values of an enum. /// /// *EnumValueDefinition*: @@ -26,7 +28,7 @@ pub struct EnumValue { // Name must return a String. name: String, // Description may return a String or null. - description: Option, + description: Description, // Deprecated returns true if this enum value should no longer be used, otherwise false. is_deprecated: bool, // Deprecation reason optionally provides a reason why this enum value is deprecated. @@ -39,14 +41,16 @@ impl EnumValue { Self { name, is_deprecated: false, - description: None, + description: Description::Field { source: None }, deprecation_reason: None, } } /// Set the Enum Value's description. pub fn description(&mut self, description: Option) { - self.description = description; + self.description = Description::Field { + source: description, + }; } /// Set the Enum Value's deprecation properties. @@ -58,16 +62,7 @@ impl EnumValue { impl Display for EnumValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(description) = &self.description { - // We are determing on whether to have description formatted as - // a multiline comment based on whether or not it already includes a - // \n. - match description.contains('\n') { - true => writeln!(f, " \"\"\"\n {}\n \"\"\"", description)?, - false => writeln!(f, " \"\"\"{}\"\"\"", description)?, - } - } - + write!(f, "{}", self.description)?; write!(f, " {}", self.name)?; if self.is_deprecated { diff --git a/crates/sdl-encoder/src/field.rs b/crates/sdl-encoder/src/field.rs index a4cff95e9..a9421c56d 100644 --- a/crates/sdl-encoder/src/field.rs +++ b/crates/sdl-encoder/src/field.rs @@ -1,4 +1,4 @@ -use crate::{InputValue, Type_}; +use crate::{Description, InputValue, Type_}; use std::fmt::{self, Display}; /// The __Field type represents each field in an Object or Interface type. /// @@ -35,7 +35,7 @@ pub struct Field { // Name must return a String. name: String, // Description may return a String. - description: Option, + description: Description, // Args returns a List of __InputValue representing the arguments this field accepts. args: Vec, // Type must return a __Type that represents the type of value returned by this field. @@ -50,7 +50,7 @@ impl Field { /// Create a new instance of Field. pub fn new(name: String, type_: Type_) -> Self { Self { - description: None, + description: Description::Field { source: None }, name, type_, args: Vec::new(), @@ -61,7 +61,9 @@ impl Field { /// Set the Field's description. pub fn description(&mut self, description: Option) { - self.description = description; + self.description = Description::Field { + source: description, + }; } /// Set the Field's deprecation properties. @@ -78,19 +80,7 @@ impl Field { impl Display for Field { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(description) = &self.description { - // Let's indent description on a field level for now, as all fields - // are always on the same level and are indented by 2 spaces. - // - // We are also determing on whether to have description formatted as - // a multiline comment based on whether or not it already includes a - // \n. - match description.contains('\n') { - true => writeln!(f, " \"\"\"\n {}\n \"\"\"", description)?, - false => writeln!(f, " \"\"\"{}\"\"\"", description)?, - } - } - + write!(f, "{}", self.description)?; write!(f, " {}", self.name)?; if !self.args.is_empty() { diff --git a/crates/sdl-encoder/src/input_field.rs b/crates/sdl-encoder/src/input_field.rs index c034b3a42..bf45ad7dc 100644 --- a/crates/sdl-encoder/src/input_field.rs +++ b/crates/sdl-encoder/src/input_field.rs @@ -1,4 +1,4 @@ -use crate::Type_; +use crate::{Description, Type_}; use std::fmt::{self, Display}; #[derive(Debug, PartialEq, Clone)] @@ -24,7 +24,7 @@ pub struct InputField { // Name must return a String. name: String, // Description may return a String. - description: Option, + description: Description, // Type must return a __Type that represents the type of value returned by this field. type_: Type_, // Default value for this input field. @@ -35,7 +35,7 @@ impl InputField { /// Create a new instance of InputField. pub fn new(name: String, type_: Type_) -> Self { Self { - description: None, + description: Description::Field { source: None }, name, type_, default_value: None, @@ -44,7 +44,9 @@ impl InputField { /// Set the InputField's description. pub fn description(&mut self, description: Option) { - self.description = description; + self.description = Description::Field { + source: description, + }; } /// Set the InputField's default value. @@ -55,18 +57,7 @@ impl InputField { impl Display for InputField { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(description) = &self.description { - // Let's indent description on a field level for now, as all fields - // are always on the same level and are indented by 2 spaces. - // - // We are also determing on whether to have description formatted as - // a multiline comment based on whether or not it already includes a - // \n. - match description.contains('\n') { - true => writeln!(f, " \"\"\"\n {}\n \"\"\"", description)?, - false => writeln!(f, " \"\"\"{}\"\"\"", description)?, - } - } + write!(f, "{}", self.description)?; write!(f, " {}: {}", self.name, self.type_)?; if let Some(default) = &self.default_value { diff --git a/crates/sdl-encoder/src/input_object_def.rs b/crates/sdl-encoder/src/input_object_def.rs index 5edb2de06..1c5c968ed 100644 --- a/crates/sdl-encoder/src/input_object_def.rs +++ b/crates/sdl-encoder/src/input_object_def.rs @@ -1,4 +1,4 @@ -use crate::InputField; +use crate::{Description, InputField}; use std::fmt::{self, Display}; /// Input objects are composite types used as inputs into queries defined as a list of named input values.. @@ -52,7 +52,7 @@ pub struct InputObjectDef { // Name must return a String. name: String, // Description may return a String or null. - description: Option, + description: Description, // A vector of fields fields: Vec, } @@ -62,14 +62,16 @@ impl InputObjectDef { pub fn new(name: String) -> Self { Self { name, - description: None, + description: Description::Top { source: None }, fields: Vec::new(), } } /// Set the InputObjectDef's description field. pub fn description(&mut self, description: Option) { - self.description = description + self.description = Description::Top { + source: description, + }; } /// Push a Field to InputObjectDef's fields vector. @@ -80,15 +82,7 @@ impl InputObjectDef { impl Display for InputObjectDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(description) = &self.description { - // We are determing on whether to have description formatted as - // a multiline comment based on whether or not it already includes a - // \n. - match description.contains('\n') { - true => writeln!(f, "\"\"\"\n{}\n\"\"\"", description)?, - false => writeln!(f, "\"\"\"{}\"\"\"", description)?, - } - } + write!(f, "{}", self.description)?; write!(f, "input {} {{", &self.name)?; diff --git a/crates/sdl-encoder/src/input_value.rs b/crates/sdl-encoder/src/input_value.rs index 9bf20efca..652a9e001 100644 --- a/crates/sdl-encoder/src/input_value.rs +++ b/crates/sdl-encoder/src/input_value.rs @@ -1,4 +1,4 @@ -use crate::Type_; +use crate::{Description, Type_}; use std::fmt::{self, Display}; // NOTE(@lrlna): __InputValue is also meant to be used for InputFields on an @@ -37,7 +37,7 @@ pub struct InputValue { // Name must return a String. name: String, // Description may return a String. - description: Option, + description: Description, // Type must return a __Type that represents the type this input value expects. type_: Type_, // Default may return a String encoding (using the GraphQL language) of @@ -55,7 +55,7 @@ impl InputValue { /// Create a new instance of InputValue. pub fn new(name: String, type_: Type_) -> Self { Self { - description: None, + description: Description::Input { source: None }, name, type_, is_deprecated: false, @@ -66,7 +66,9 @@ impl InputValue { /// Set the InputValue's description. pub fn description(&mut self, description: Option) { - self.description = description; + self.description = Description::Input { + source: description, + }; } /// Set the InputValue's default value. @@ -83,15 +85,7 @@ impl InputValue { impl Display for InputValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(description) = &self.description { - // We are determing on whether to have description formatted as - // a multiline comment based on whether or not it already includes a - // \n. - match description.contains('\n') { - true => write!(f, "\"\"\"\n{}\n\"\"\" ", description)?, - false => write!(f, "\"\"\"{}\"\"\" ", description)?, - } - } + write!(f, "{}", self.description)?; write!(f, "{}: {}", self.name, self.type_)?; diff --git a/crates/sdl-encoder/src/interface_def.rs b/crates/sdl-encoder/src/interface_def.rs index 4ddc774ed..37d523d4c 100644 --- a/crates/sdl-encoder/src/interface_def.rs +++ b/crates/sdl-encoder/src/interface_def.rs @@ -1,4 +1,4 @@ -use crate::Field; +use crate::{Description, Field}; use std::fmt::{self, Display}; /// InterfaceDefs are an abstract type where there are common fields declared. @@ -71,7 +71,7 @@ pub struct InterfaceDef { // Name must return a String. name: String, // Description may return a String or null. - description: Option, + description: Description, // The vector of interfaces that this interface implements. interfaces: Vec, // The vector of fields required by this interface. @@ -83,7 +83,7 @@ impl InterfaceDef { pub fn new(name: String) -> Self { Self { name, - description: None, + description: Description::Top { source: None }, fields: Vec::new(), interfaces: Vec::new(), } @@ -91,7 +91,9 @@ impl InterfaceDef { /// Set the schema def's description. pub fn description(&mut self, description: Option) { - self.description = description + self.description = Description::Top { + source: description, + }; } /// Set the interfaces ObjectDef implements. @@ -107,15 +109,7 @@ impl InterfaceDef { impl Display for InterfaceDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(description) = &self.description { - // We are determing on whether to have description formatted as - // a multiline comment based on whether or not it already includes a - // \n. - match description.contains('\n') { - true => writeln!(f, "\"\"\"\n{}\n\"\"\"", description)?, - false => writeln!(f, "\"\"\"{}\"\"\"", description)?, - } - } + write!(f, "{}", self.description)?; write!(f, "interface {}", &self.name)?; for (i, interface) in self.interfaces.iter().enumerate() { diff --git a/crates/sdl-encoder/src/lib.rs b/crates/sdl-encoder/src/lib.rs index 8a885c47b..d7c6a8436 100644 --- a/crates/sdl-encoder/src/lib.rs +++ b/crates/sdl-encoder/src/lib.rs @@ -65,6 +65,9 @@ mod schema; pub use schema::Schema; +mod description; +pub use description::Description; + mod field; pub use field::Field; diff --git a/crates/sdl-encoder/src/object_def.rs b/crates/sdl-encoder/src/object_def.rs index 0b90b6f6a..27eaa51bc 100644 --- a/crates/sdl-encoder/src/object_def.rs +++ b/crates/sdl-encoder/src/object_def.rs @@ -1,4 +1,4 @@ -use crate::Field; +use crate::{Description, Field}; use std::fmt::{self, Display}; /// Object types represent concrete instantiations of sets of fields. /// @@ -56,7 +56,7 @@ pub struct ObjectDef { // Name must return a String. name: String, // Description may return a String or null. - description: Option, + description: Description, // The vector of interfaces that an object implements. interfaces: Vec, // The vector of fields query‐able on this type. @@ -68,7 +68,7 @@ impl ObjectDef { pub fn new(name: String) -> Self { Self { name, - description: None, + description: Description::Top { source: None }, interfaces: Vec::new(), fields: Vec::new(), } @@ -76,7 +76,9 @@ impl ObjectDef { /// Set the ObjectDef's description field. pub fn description(&mut self, description: Option) { - self.description = description + self.description = Description::Top { + source: description, + }; } /// Set the interfaces ObjectDef implements. @@ -92,15 +94,7 @@ impl ObjectDef { impl Display for ObjectDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(description) = &self.description { - // We are determing on whether to have description formatted as - // a multiline comment based on whether or not it already includes a - // \n. - match description.contains('\n') { - true => writeln!(f, "\"\"\"\n{}\n\"\"\"", description)?, - false => writeln!(f, "\"\"\"{}\"\"\"", description)?, - } - } + write!(f, "{}", self.description)?; write!(f, "type {}", &self.name)?; for (i, interface) in self.interfaces.iter().enumerate() { diff --git a/crates/sdl-encoder/src/scalar_def.rs b/crates/sdl-encoder/src/scalar_def.rs index d78b10700..2c040e975 100644 --- a/crates/sdl-encoder/src/scalar_def.rs +++ b/crates/sdl-encoder/src/scalar_def.rs @@ -1,5 +1,7 @@ use std::fmt::{self, Display}; +use crate::Description; + /// Represents scalar types such as Int, String, and Boolean. /// Scalars cannot have fields. /// @@ -28,7 +30,7 @@ pub struct ScalarDef { // Name must return a String. name: String, // Description may return a String or null. - description: Option, + description: Description, } impl ScalarDef { @@ -36,28 +38,21 @@ impl ScalarDef { pub fn new(name: String) -> Self { Self { name, - description: None, + description: Description::Top { source: None }, } } /// Set the ScalarDef's description. pub fn description(&mut self, description: Option) { - self.description = description; + self.description = Description::Top { + source: description, + }; } } impl Display for ScalarDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(description) = &self.description { - // We are determing on whether to have description formatted as - // a multiline comment based on whether or not it already includes a - // \n. - match description.contains('\n') { - true => writeln!(f, "\"\"\"\n{}\n\"\"\"", description)?, - false => writeln!(f, "\"\"\"{}\"\"\"", description)?, - } - } - + write!(f, "{}", self.description)?; writeln!(f, "scalar {}", self.name) } } diff --git a/crates/sdl-encoder/src/schema_def.rs b/crates/sdl-encoder/src/schema_def.rs index 2d74b7c45..b8ae46eb7 100644 --- a/crates/sdl-encoder/src/schema_def.rs +++ b/crates/sdl-encoder/src/schema_def.rs @@ -1,5 +1,7 @@ use std::fmt::{self, Display}; +use crate::Description; + /// A GraphQL service’s collective type system capabilities are referred to as that service’s “schema”. /// /// *SchemaDefinition*: @@ -32,7 +34,7 @@ use std::fmt::{self, Display}; #[derive(Debug, Clone)] pub struct SchemaDef { // Description may be a String. - description: Option, + description: Description, // The vector of fields in a schema to represent root operation type // definition. query: Option, @@ -44,7 +46,7 @@ impl SchemaDef { /// Create a new instance of SchemaDef. pub fn new() -> Self { Self { - description: None, + description: Description::Top { source: None }, query: None, mutation: None, subscription: None, @@ -53,7 +55,9 @@ impl SchemaDef { /// Set the SchemaDef's description. pub fn description(&mut self, description: Option) { - self.description = description + self.description = Description::Top { + source: description, + }; } /// Set the schema def's query type. @@ -80,15 +84,8 @@ impl Default for SchemaDef { impl Display for SchemaDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(description) = &self.description { - // We determine whether to have description formatted as - // a multiline comment based on whether or not it already includes a - // \n. - match description.contains('\n') { - true => writeln!(f, "\"\"\"\n{}\n\"\"\"", description)?, - false => writeln!(f, "\"\"\"{}\"\"\"", description)?, - } - } + write!(f, "{}", self.description)?; + writeln!(f, "schema {{")?; if let Some(query) = &self.query { writeln!(f, " query: {}", query)?; diff --git a/crates/sdl-encoder/src/union_def.rs b/crates/sdl-encoder/src/union_def.rs index 7a4e98247..ed7036a72 100644 --- a/crates/sdl-encoder/src/union_def.rs +++ b/crates/sdl-encoder/src/union_def.rs @@ -1,5 +1,7 @@ use std::fmt::{self, Display}; +use crate::Description; + /// UnionDefs are an abstract type where no common fields are declared. /// /// *UnionDefTypeDefinition*: @@ -26,7 +28,7 @@ pub struct UnionDef { // Name must return a String. name: String, // Description may return a String. - description: Option, + description: Description, // The vector of members that can be represented within this union. members: Vec, } @@ -36,14 +38,16 @@ impl UnionDef { pub fn new(name: String) -> Self { Self { name, - description: None, + description: Description::Top { source: None }, members: Vec::new(), } } /// Set the UnionDefs description. pub fn description(&mut self, description: Option) { - self.description = description; + self.description = Description::Top { + source: description, + }; } /// Set a UnionDef member. @@ -54,15 +58,7 @@ impl UnionDef { impl Display for UnionDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(description) = &self.description { - // We are determing on whether to have description formatted as - // a multiline comment based on whether or not it already includes a - // \n. - match description.contains('\n') { - true => writeln!(f, "\"\"\"\n{}\n\"\"\"", description)?, - false => writeln!(f, "\"\"\"{}\"\"\"", description)?, - } - } + write!(f, "{}", self.description)?; write!(f, "union {} = ", self.name)?;