1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use weedle::dictionary::DictionaryMember;
use weedle::interface::*;
use weedle::namespace::{AttributeNamespaceMember, OperationNamespaceMember};
use weedle::*;

/// A WebIDL symbol that may have a documentation string
pub trait SymbolWithDocstring {
	/// Returns a string literal with the starting whitespace removed.
	/// If the symbol has no docstring, it will return an empty string literal.
	///
	/// ## Examples
	/// The following examples compare the behavior and APIs of `weedle2` and `webidl-utils`.
	/// To summarize:
	///  - weedle2:
	///    - Symbols that can contain a docstring will have type `Option<Docstring>`.
	///    - If there is no docstring, it will be `None`.
	///    - A parsed `Docstring` will have a non-normalized string. For example, if
	///      a docstring is written as `/// This is an enum`, the resulting string will be
	///      `" This is an enum"`.
	///  - webidl-utils:
	///    - `SymbolWithDocString` will return a `&str`.
	///    - If there is no docstring, it will be `""` (empty string literal).
	///    - It will normalize strings by removing the beginning whitespace.
	///
	/// ```
	/// use webidl_utils::symbol::SymbolWithDocstring;
	/// use weedle::{EnumDefinition, Parse};
	///
	/// let (_, enum_def) = EnumDefinition::parse(
	///     r#"
	///         /// This is an enum
	///         enum Color { "red", "green", "blue" };
	///     "#)
	///     .expect("EnumDefinition parsed with an error");
	///
	/// // weedle2 behavior
	/// assert_eq!(enum_def.docstring.clone().unwrap().0.as_str(), " This is an enum");
	///
	/// // webidl-utils behavior
	/// assert_eq!(enum_def.docstring(), "This is an enum");
	/// ```
	fn docstring(&self) -> &str;
	fn has_docstring(&self) -> bool;
}

macro_rules! impl_symbol_with_docstring {
	($($sym:ident),+ $(,)?) => {
		$(
			impl<'a> SymbolWithDocstring for $sym<'a> {
				fn docstring(&self) -> &str {
					self.docstring.as_ref()
						.map(|docstring| &docstring.0[..])
						.unwrap_or("")
						.trim_start()
				}

				fn has_docstring(&self) -> bool {
					self.docstring.is_some()
				}
			}
		)+
	};
}

impl_symbol_with_docstring!(
	CallbackInterfaceDefinition,
	InterfaceDefinition,
	NamespaceDefinition,
	DictionaryDefinition,
	EnumDefinition,
	EnumVariant,
	DictionaryMember,
	ConstructorInterfaceMember,
	OperationInterfaceMember,
	OperationNamespaceMember,
	AttributeNamespaceMember,
);

#[cfg(test)]
mod tests {
	use crate::symbol::SymbolWithDocstring;
	use weedle::{EnumDefinition, Parse};

	#[test]
	fn test() {
		let (_, enum_def) = EnumDefinition::parse(
			r#"
				/// This is an enum
				enum Color { "red", "green", "blue" };
			"#,
		)
		.expect("EnumDefinition parsed with an error");

		assert_eq!(enum_def.docstring(), "This is an enum");
		assert_eq!(enum_def.has_docstring(), true);
	}
}