Skip to content

Commit

Permalink
Escape comments (#510)
Browse files Browse the repository at this point in the history
  • Loading branch information
nbaztec authored Sep 20, 2021
1 parent 87cea1d commit d7324c4
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 20 deletions.
2 changes: 2 additions & 0 deletions prost-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ petgraph = { version = "0.6", default-features = false }
prost = { version = "0.8.0", path = "..", default-features = false }
prost-types = { version = "0.8.0", path = "../prost-types", default-features = false }
tempfile = "3"
lazy_static = "1.4.0"
regex = "1.5.4"

[build-dependencies]
which = { version = "4", default-features = false }
Expand Down
115 changes: 112 additions & 3 deletions prost-build/src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use lazy_static::lazy_static;
use prost_types::source_code_info::Location;
use regex::Regex;

/// Comments on a Protobuf item.
#[derive(Debug)]
Expand Down Expand Up @@ -53,7 +55,7 @@ impl Comments {
buf.push_str(" ");
}
buf.push_str("//");
buf.push_str(line);
buf.push_str(&Self::sanitize_line(line));
buf.push('\n');
}
buf.push('\n');
Expand All @@ -65,7 +67,7 @@ impl Comments {
buf.push_str(" ");
}
buf.push_str("///");
buf.push_str(line);
buf.push_str(&Self::sanitize_line(line));
buf.push('\n');
}

Expand All @@ -83,10 +85,24 @@ impl Comments {
buf.push_str(" ");
}
buf.push_str("///");
buf.push_str(line);
buf.push_str(&Self::sanitize_line(line));
buf.push('\n');
}
}

/// Sanitizes the line for rustdoc by performing the following operations:
/// - escape urls as <http://foo.com>
/// - escape `[` & `]`
fn sanitize_line(line: &str) -> String {
lazy_static! {
static ref RULE_URL: Regex = Regex::new(r"https?://[^\s)]+").unwrap();
static ref RULE_BRACKETS: Regex = Regex::new(r"(\[)(\S+)(])").unwrap();
}

let mut s = RULE_URL.replace_all(line, r"<$0>").to_string();
s = RULE_BRACKETS.replace_all(&s, r"\$1$2\$3").to_string();
s
}
}

/// A service descriptor.
Expand Down Expand Up @@ -130,3 +146,96 @@ pub struct Method {
/// Identifies if server streams multiple server messages.
pub server_streaming: bool,
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_comment_append_with_indent_sanitizes_comment_doc_url() {
struct TestCases {
name: &'static str,
input: String,
expected: String,
}

let tests = vec![
TestCases {
name: "valid_http",
input: "See https://www.rust-lang.org/".to_string(),
expected: "///See <https://www.rust-lang.org/>\n".to_string(),
},
TestCases {
name: "valid_https",
input: "See https://www.rust-lang.org/".to_string(),
expected: "///See <https://www.rust-lang.org/>\n".to_string(),
},
TestCases {
name: "valid_https_parenthesis",
input: "See (https://www.rust-lang.org/)".to_string(),
expected: "///See (<https://www.rust-lang.org/>)\n".to_string(),
},
TestCases {
name: "invalid",
input: "See note://abc".to_string(),
expected: "///See note://abc\n".to_string(),
},
];
for t in tests {
let input = Comments {
leading_detached: vec![],
leading: vec![],
trailing: vec![t.input],
};

let mut actual = "".to_string();
input.append_with_indent(0, &mut actual);

assert_eq!(t.expected, actual, "failed {}", t.name);
}
}

#[test]
fn test_comment_append_with_indent_sanitizes_square_brackets() {
struct TestCases {
name: &'static str,
input: String,
expected: String,
}

let tests = vec![
TestCases {
name: "valid_brackets",
input: "foo [bar] baz".to_string(),
expected: "///foo \\[bar\\] baz\n".to_string(),
},
TestCases {
name: "invalid_start_bracket",
input: "foo [= baz".to_string(),
expected: "///foo [= baz\n".to_string(),
},
TestCases {
name: "invalid_end_bracket",
input: "foo =] baz".to_string(),
expected: "///foo =] baz\n".to_string(),
},
TestCases {
name: "invalid_bracket_combination",
input: "[0, 9)".to_string(),
expected: "///[0, 9)\n".to_string(),
},
];
for t in tests {
let input = Comments {
leading_detached: vec![],
leading: vec![],
trailing: vec![t.input],
};

let mut actual = "".to_string();
input.append_with_indent(0, &mut actual);

assert_eq!(t.expected, actual, "failed {}", t.name);
}
}
}
34 changes: 17 additions & 17 deletions prost-types/src/protobuf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ pub struct MethodDescriptorProto {
// extension number. You can declare multiple options with only one extension
// number by putting them in a sub-message. See the Custom Options section of
// the docs for examples:
// https://developers.google.com/protocol-buffers/docs/proto#options
// <https://developers.google.com/protocol-buffers/docs/proto#options>
// If this turns out to be popular, a web service will be set up
// to automatically assign option numbers.

Expand Down Expand Up @@ -989,7 +989,7 @@ pub mod generated_code_info {
/// If the embedded message type is well-known and has a custom JSON
/// representation, that representation will be embedded adding a field
/// `value` which holds the custom JSON in addition to the `@type`
/// field. Example (for message [google.protobuf.Duration][]):
/// field. Example (for message \[google.protobuf.Duration][\]):
///
/// {
/// "@type": "type.googleapis.com/google.protobuf.Duration",
Expand All @@ -1011,7 +1011,7 @@ pub struct Any {
/// server that maps type URLs to message definitions as follows:
///
/// * If no scheme is provided, `https` is assumed.
/// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
/// * An HTTP GET on the URL must yield a \[google.protobuf.Type][\]
/// value in binary format, or produce an error.
/// * Applications are allowed to cache lookup results based on the
/// URL, or have them precompiled into a binary to avoid any
Expand Down Expand Up @@ -1223,7 +1223,7 @@ pub enum Syntax {
/// from API Services, which represent a concrete implementation of an interface
/// as opposed to simply a description of methods and bindings. They are also
/// sometimes simply referred to as "APIs" in other contexts, such as the name of
/// this message itself. See https://cloud.google.com/apis/design/glossary for
/// this message itself. See <https://cloud.google.com/apis/design/glossary> for
/// detailed terminology.
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Api {
Expand All @@ -1245,7 +1245,7 @@ pub struct Api {
/// consistent with what is provided here.
///
/// The versioning schema uses [semantic
/// versioning](http://semver.org) where the major version number
/// versioning](<http://semver.org>) where the major version number
/// indicates a breaking change and the minor version an additive,
/// non-breaking change. Both version numbers are signals to users
/// what to expect from different versions, and should be carefully
Expand All @@ -1264,7 +1264,7 @@ pub struct Api {
/// message.
#[prost(message, optional, tag="5")]
pub source_context: ::core::option::Option<SourceContext>,
/// Included interfaces. See [Mixin][].
/// Included interfaces. See \[Mixin][\].
#[prost(message, repeated, tag="6")]
pub mixins: ::prost::alloc::vec::Vec<Mixin>,
/// The source syntax of the service.
Expand Down Expand Up @@ -1310,7 +1310,7 @@ pub struct Method {
///
/// - If an http annotation is inherited, the path pattern will be
/// modified as follows. Any version prefix will be replaced by the
/// version of the including interface plus the [root][] path if
/// version of the including interface plus the \[root][\] path if
/// specified.
///
/// Example of a simple mixin:
Expand Down Expand Up @@ -1543,7 +1543,7 @@ pub struct Duration {
/// d: 1
/// x: 2
/// }
/// c: [1]
/// c: \[1\]
/// }
///
/// And an update message:
Expand All @@ -1552,7 +1552,7 @@ pub struct Duration {
/// b {
/// d: 10
/// }
/// c: [2]
/// c: \[2\]
/// }
///
/// then if the field mask is:
Expand Down Expand Up @@ -1743,11 +1743,11 @@ pub enum NullValue {
///
/// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
/// second table is needed for interpretation, using a [24-hour linear
/// smear](https://developers.google.com/time/smear).
/// smear](<https://developers.google.com/time/smear>).
///
/// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
/// restricting to that range, we ensure that we can convert to and from [RFC
/// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
/// 3339](<https://www.ietf.org/rfc/rfc3339.txt>) date strings.
///
/// # Examples
///
Expand Down Expand Up @@ -1803,8 +1803,8 @@ pub enum NullValue {
/// # JSON Mapping
///
/// In JSON format, the Timestamp type is encoded as a string in the
/// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
/// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
/// [RFC 3339](<https://www.ietf.org/rfc/rfc3339.txt>) format. That is, the
/// format is "{year}-{month}-{day}T{hour}:{min}:{sec}\[.{frac_sec}\]Z"
/// where {year} is always expressed using four digits while {month}, {day},
/// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
/// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
Expand All @@ -1818,13 +1818,13 @@ pub enum NullValue {
///
/// In JavaScript, one can convert a Date object to this format using the
/// standard
/// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
/// \[toISOString()\](<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString>)
/// method. In Python, a standard `datetime.datetime` object can be converted
/// to this format using
/// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
/// \[`strftime`\](<https://docs.python.org/2/library/time.html#time.strftime>) with
/// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
/// the Joda Time's [`ISODateTimeFormat.dateTime()`](
/// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
/// the Joda Time's \[`ISODateTimeFormat.dateTime()`\](
/// <http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D>
/// ) to obtain a formatter capable of generating timestamps in this format.
///
///
Expand Down

0 comments on commit d7324c4

Please sign in to comment.