mirror of
https://github.com/rust-lang/cargo
synced 2024-09-13 21:11:44 +00:00
Auto merge of #13241 - epage:syntax, r=Muscraft
feat(embedded): Add multiple experimental manifest syntaxes ### What does this PR try to resolve? As syntax discussions for "cargo script" are on-going, this allows us to experiment with a couple of them so we can see how they work in practice. This is missing the line-prefix syntax as we decide how we want to separate blocks for it. While doing this, I removed the previous doc-comment syntax. This was left in for transition purposes. With where discussions are going, its unlikely we'll go back to that syntax. ### How should we test and review this PR? ### Additional information rust-lang/rfcs#3503 https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Syntax.20for.20embedded.20tooling.20metadata
This commit is contained in:
commit
f9946d15df
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -289,7 +289,6 @@ dependencies = [
|
||||||
"os_info",
|
"os_info",
|
||||||
"pasetors",
|
"pasetors",
|
||||||
"pathdiff",
|
"pathdiff",
|
||||||
"pulldown-cmark",
|
|
||||||
"rand",
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
|
@ -304,7 +303,6 @@ dependencies = [
|
||||||
"shell-escape",
|
"shell-escape",
|
||||||
"snapbox",
|
"snapbox",
|
||||||
"supports-hyperlinks",
|
"supports-hyperlinks",
|
||||||
"syn 2.0.46",
|
|
||||||
"tar",
|
"tar",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"time",
|
"time",
|
||||||
|
|
|
@ -91,7 +91,6 @@ sha2 = "0.10.8"
|
||||||
shell-escape = "0.1.5"
|
shell-escape = "0.1.5"
|
||||||
supports-hyperlinks = "2.1.0"
|
supports-hyperlinks = "2.1.0"
|
||||||
snapbox = { version = "0.4.15", features = ["diff", "path"] }
|
snapbox = { version = "0.4.15", features = ["diff", "path"] }
|
||||||
syn = { version = "2.0.46", features = ["extra-traits", "full"] }
|
|
||||||
tar = { version = "0.4.40", default-features = false }
|
tar = { version = "0.4.40", default-features = false }
|
||||||
tempfile = "3.9.0"
|
tempfile = "3.9.0"
|
||||||
thiserror = "1.0.56"
|
thiserror = "1.0.56"
|
||||||
|
@ -179,7 +178,6 @@ opener.workspace = true
|
||||||
os_info.workspace = true
|
os_info.workspace = true
|
||||||
pasetors.workspace = true
|
pasetors.workspace = true
|
||||||
pathdiff.workspace = true
|
pathdiff.workspace = true
|
||||||
pulldown-cmark.workspace = true
|
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
rusqlite.workspace = true
|
rusqlite.workspace = true
|
||||||
|
@ -192,7 +190,6 @@ serde_json = { workspace = true, features = ["raw_value"] }
|
||||||
sha1.workspace = true
|
sha1.workspace = true
|
||||||
shell-escape.workspace = true
|
shell-escape.workspace = true
|
||||||
supports-hyperlinks.workspace = true
|
supports-hyperlinks.workspace = true
|
||||||
syn.workspace = true
|
|
||||||
tar.workspace = true
|
tar.workspace = true
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
time.workspace = true
|
time.workspace = true
|
||||||
|
|
|
@ -18,10 +18,7 @@ pub(super) fn expand_manifest(
|
||||||
let source = split_source(content)?;
|
let source = split_source(content)?;
|
||||||
if let Some(frontmatter) = source.frontmatter {
|
if let Some(frontmatter) = source.frontmatter {
|
||||||
match source.info {
|
match source.info {
|
||||||
Some("cargo") => {}
|
Some("cargo") | None => {}
|
||||||
None => {
|
|
||||||
anyhow::bail!("frontmatter is missing an infostring; specify `cargo` for embedding a manifest");
|
|
||||||
}
|
|
||||||
Some(other) => {
|
Some(other) => {
|
||||||
if let Some(remainder) = other.strip_prefix("cargo,") {
|
if let Some(remainder) = other.strip_prefix("cargo,") {
|
||||||
anyhow::bail!("cargo does not support frontmatter infostring attributes like `{remainder}` at this time")
|
anyhow::bail!("cargo does not support frontmatter infostring attributes like `{remainder}` at this time")
|
||||||
|
@ -66,17 +63,8 @@ pub(super) fn expand_manifest(
|
||||||
let manifest = toml::to_string_pretty(&manifest)?;
|
let manifest = toml::to_string_pretty(&manifest)?;
|
||||||
Ok(manifest)
|
Ok(manifest)
|
||||||
} else {
|
} else {
|
||||||
// Legacy doc-comment support; here only for transitional purposes
|
let frontmatter = "";
|
||||||
let comment = extract_comment(content)?.unwrap_or_default();
|
let manifest = expand_manifest_(frontmatter, path, config)
|
||||||
let manifest = match extract_manifest(&comment)? {
|
|
||||||
Some(manifest) => Some(manifest),
|
|
||||||
None => {
|
|
||||||
tracing::trace!("failed to extract manifest");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.unwrap_or_default();
|
|
||||||
let manifest = expand_manifest_(&manifest, path, config)
|
|
||||||
.with_context(|| format!("failed to parse manifest at {}", path.display()))?;
|
.with_context(|| format!("failed to parse manifest at {}", path.display()))?;
|
||||||
let manifest = toml::to_string_pretty(&manifest)?;
|
let manifest = toml::to_string_pretty(&manifest)?;
|
||||||
Ok(manifest)
|
Ok(manifest)
|
||||||
|
@ -229,17 +217,29 @@ fn split_source(input: &str) -> CargoResult<Source<'_>> {
|
||||||
source.content = content;
|
source.content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Experiment: let us try which char works better
|
||||||
|
let tick_char = source
|
||||||
|
.content
|
||||||
|
.chars()
|
||||||
|
.filter(|c| ['`', '#', '-'].contains(c))
|
||||||
|
.next()
|
||||||
|
.unwrap_or('`');
|
||||||
|
|
||||||
let tick_end = source
|
let tick_end = source
|
||||||
.content
|
.content
|
||||||
.char_indices()
|
.char_indices()
|
||||||
.find_map(|(i, c)| (c != '`').then_some(i))
|
.find_map(|(i, c)| (c != tick_char).then_some(i))
|
||||||
.unwrap_or(source.content.len());
|
.unwrap_or(source.content.len());
|
||||||
let (fence_pattern, rest) = match tick_end {
|
let (fence_pattern, rest) = match tick_end {
|
||||||
0 => {
|
0 => {
|
||||||
return Ok(source);
|
return Ok(source);
|
||||||
}
|
}
|
||||||
1 | 2 => {
|
1 | 2 => {
|
||||||
anyhow::bail!("found {tick_end} backticks in rust frontmatter, expected at least 3")
|
if tick_char == '#' {
|
||||||
|
// Attribute
|
||||||
|
return Ok(source);
|
||||||
|
}
|
||||||
|
anyhow::bail!("found {tick_end} `{tick_char}` in rust frontmatter, expected at least 3")
|
||||||
}
|
}
|
||||||
_ => source.content.split_at(tick_end),
|
_ => source.content.split_at(tick_end),
|
||||||
};
|
};
|
||||||
|
@ -268,323 +268,6 @@ fn split_source(input: &str) -> CargoResult<Source<'_>> {
|
||||||
Ok(source)
|
Ok(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Locates a "code block manifest" in Rust source.
|
|
||||||
fn extract_comment(input: &str) -> CargoResult<Option<String>> {
|
|
||||||
let mut doc_fragments = Vec::new();
|
|
||||||
let file = syn::parse_file(input)?;
|
|
||||||
// HACK: `syn` doesn't tell us what kind of comment was used, so infer it from how many
|
|
||||||
// attributes were used
|
|
||||||
let kind = if 1 < file
|
|
||||||
.attrs
|
|
||||||
.iter()
|
|
||||||
.filter(|attr| attr.meta.path().is_ident("doc"))
|
|
||||||
.count()
|
|
||||||
{
|
|
||||||
CommentKind::Line
|
|
||||||
} else {
|
|
||||||
CommentKind::Block
|
|
||||||
};
|
|
||||||
for attr in &file.attrs {
|
|
||||||
if attr.meta.path().is_ident("doc") {
|
|
||||||
doc_fragments.push(DocFragment::new(attr, kind)?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if doc_fragments.is_empty() {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
unindent_doc_fragments(&mut doc_fragments);
|
|
||||||
|
|
||||||
let mut doc_comment = String::new();
|
|
||||||
for frag in &doc_fragments {
|
|
||||||
add_doc_fragment(&mut doc_comment, frag);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Some(doc_comment))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `#[doc]`
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
struct DocFragment {
|
|
||||||
/// The attribute value
|
|
||||||
doc: String,
|
|
||||||
/// Indentation used within `doc
|
|
||||||
indent: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DocFragment {
|
|
||||||
fn new(attr: &syn::Attribute, kind: CommentKind) -> CargoResult<Self> {
|
|
||||||
let syn::Meta::NameValue(nv) = &attr.meta else {
|
|
||||||
anyhow::bail!("unsupported attr meta for {:?}", attr.meta.path())
|
|
||||||
};
|
|
||||||
let syn::Expr::Lit(syn::ExprLit {
|
|
||||||
lit: syn::Lit::Str(lit),
|
|
||||||
..
|
|
||||||
}) = &nv.value
|
|
||||||
else {
|
|
||||||
anyhow::bail!("only string literals are supported")
|
|
||||||
};
|
|
||||||
Ok(Self {
|
|
||||||
doc: beautify_doc_string(lit.value(), kind),
|
|
||||||
indent: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
|
||||||
enum CommentKind {
|
|
||||||
Line,
|
|
||||||
Block,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Makes a doc string more presentable to users.
|
|
||||||
/// Used by rustdoc and perhaps other tools, but not by rustc.
|
|
||||||
///
|
|
||||||
/// See `rustc_ast/util/comments.rs`
|
|
||||||
fn beautify_doc_string(data: String, kind: CommentKind) -> String {
|
|
||||||
fn get_vertical_trim(lines: &[&str]) -> Option<(usize, usize)> {
|
|
||||||
let mut i = 0;
|
|
||||||
let mut j = lines.len();
|
|
||||||
// first line of all-stars should be omitted
|
|
||||||
if !lines.is_empty() && lines[0].chars().all(|c| c == '*') {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// like the first, a last line of all stars should be omitted
|
|
||||||
if j > i && !lines[j - 1].is_empty() && lines[j - 1].chars().all(|c| c == '*') {
|
|
||||||
j -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if i != 0 || j != lines.len() {
|
|
||||||
Some((i, j))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_horizontal_trim(lines: &[&str], kind: CommentKind) -> Option<String> {
|
|
||||||
let mut i = usize::MAX;
|
|
||||||
let mut first = true;
|
|
||||||
|
|
||||||
// In case we have doc comments like `/**` or `/*!`, we want to remove stars if they are
|
|
||||||
// present. However, we first need to strip the empty lines so they don't get in the middle
|
|
||||||
// when we try to compute the "horizontal trim".
|
|
||||||
let lines = match kind {
|
|
||||||
CommentKind::Block => {
|
|
||||||
// Whatever happens, we skip the first line.
|
|
||||||
let mut i = lines
|
|
||||||
.get(0)
|
|
||||||
.map(|l| {
|
|
||||||
if l.trim_start().starts_with('*') {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or(0);
|
|
||||||
let mut j = lines.len();
|
|
||||||
|
|
||||||
while i < j && lines[i].trim().is_empty() {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
while j > i && lines[j - 1].trim().is_empty() {
|
|
||||||
j -= 1;
|
|
||||||
}
|
|
||||||
&lines[i..j]
|
|
||||||
}
|
|
||||||
CommentKind::Line => lines,
|
|
||||||
};
|
|
||||||
|
|
||||||
for line in lines {
|
|
||||||
for (j, c) in line.chars().enumerate() {
|
|
||||||
if j > i || !"* \t".contains(c) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if c == '*' {
|
|
||||||
if first {
|
|
||||||
i = j;
|
|
||||||
first = false;
|
|
||||||
} else if i != j {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if i >= line.len() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if lines.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(lines[0][..i].into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let data_s = data.as_str();
|
|
||||||
if data_s.contains('\n') {
|
|
||||||
let mut lines = data_s.lines().collect::<Vec<&str>>();
|
|
||||||
let mut changes = false;
|
|
||||||
let lines = if let Some((i, j)) = get_vertical_trim(&lines) {
|
|
||||||
changes = true;
|
|
||||||
// remove whitespace-only lines from the start/end of lines
|
|
||||||
&mut lines[i..j]
|
|
||||||
} else {
|
|
||||||
&mut lines
|
|
||||||
};
|
|
||||||
if let Some(horizontal) = get_horizontal_trim(lines, kind) {
|
|
||||||
changes = true;
|
|
||||||
// remove a "[ \t]*\*" block from each line, if possible
|
|
||||||
for line in lines.iter_mut() {
|
|
||||||
if let Some(tmp) = line.strip_prefix(&horizontal) {
|
|
||||||
*line = tmp;
|
|
||||||
if kind == CommentKind::Block
|
|
||||||
&& (*line == "*" || line.starts_with("* ") || line.starts_with("**"))
|
|
||||||
{
|
|
||||||
*line = &line[1..];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if changes {
|
|
||||||
return lines.join("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes excess indentation on comments in order for the Markdown
|
|
||||||
/// to be parsed correctly. This is necessary because the convention for
|
|
||||||
/// writing documentation is to provide a space between the /// or //! marker
|
|
||||||
/// and the doc text, but Markdown is whitespace-sensitive. For example,
|
|
||||||
/// a block of text with four-space indentation is parsed as a code block,
|
|
||||||
/// so if we didn't unindent comments, these list items
|
|
||||||
///
|
|
||||||
/// /// A list:
|
|
||||||
/// ///
|
|
||||||
/// /// - Foo
|
|
||||||
/// /// - Bar
|
|
||||||
///
|
|
||||||
/// would be parsed as if they were in a code block, which is likely not what the user intended.
|
|
||||||
///
|
|
||||||
/// See also `rustc_resolve/rustdoc.rs`
|
|
||||||
fn unindent_doc_fragments(docs: &mut [DocFragment]) {
|
|
||||||
// HACK: We can't tell the difference between `#[doc]` and doc-comments, so we can't specialize
|
|
||||||
// the indentation like rustodc does
|
|
||||||
let add = 0;
|
|
||||||
|
|
||||||
// `min_indent` is used to know how much whitespaces from the start of each lines must be
|
|
||||||
// removed. Example:
|
|
||||||
//
|
|
||||||
// ```
|
|
||||||
// /// hello!
|
|
||||||
// #[doc = "another"]
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum
|
|
||||||
// 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4
|
|
||||||
// (5 - 1) whitespaces.
|
|
||||||
let Some(min_indent) = docs
|
|
||||||
.iter()
|
|
||||||
.map(|fragment| {
|
|
||||||
fragment
|
|
||||||
.doc
|
|
||||||
.as_str()
|
|
||||||
.lines()
|
|
||||||
.fold(usize::MAX, |min_indent, line| {
|
|
||||||
if line.chars().all(|c| c.is_whitespace()) {
|
|
||||||
min_indent
|
|
||||||
} else {
|
|
||||||
// Compare against either space or tab, ignoring whether they are
|
|
||||||
// mixed or not.
|
|
||||||
let whitespace =
|
|
||||||
line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
|
|
||||||
min_indent.min(whitespace)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.min()
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
for fragment in docs {
|
|
||||||
if fragment.doc.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let min_indent = if min_indent > 0 {
|
|
||||||
min_indent - add
|
|
||||||
} else {
|
|
||||||
min_indent
|
|
||||||
};
|
|
||||||
|
|
||||||
fragment.indent = min_indent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The goal of this function is to apply the `DocFragment` transformation that is required when
|
|
||||||
/// transforming into the final Markdown, which is applying the computed indent to each line in
|
|
||||||
/// each doc fragment (a `DocFragment` can contain multiple lines in case of `#[doc = ""]`).
|
|
||||||
///
|
|
||||||
/// Note: remove the trailing newline where appropriate
|
|
||||||
///
|
|
||||||
/// See also `rustc_resolve/rustdoc.rs`
|
|
||||||
fn add_doc_fragment(out: &mut String, frag: &DocFragment) {
|
|
||||||
let s = frag.doc.as_str();
|
|
||||||
let mut iter = s.lines();
|
|
||||||
if s.is_empty() {
|
|
||||||
out.push('\n');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while let Some(line) = iter.next() {
|
|
||||||
if line.chars().any(|c| !c.is_whitespace()) {
|
|
||||||
assert!(line.len() >= frag.indent);
|
|
||||||
out.push_str(&line[frag.indent..]);
|
|
||||||
} else {
|
|
||||||
out.push_str(line);
|
|
||||||
}
|
|
||||||
out.push('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extracts the first `Cargo` fenced code block from a chunk of Markdown.
|
|
||||||
fn extract_manifest(comment: &str) -> CargoResult<Option<String>> {
|
|
||||||
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
|
|
||||||
|
|
||||||
// To match librustdoc/html/markdown.rs, opts.
|
|
||||||
let exts = Options::ENABLE_TABLES | Options::ENABLE_FOOTNOTES;
|
|
||||||
|
|
||||||
let md = Parser::new_ext(comment, exts);
|
|
||||||
|
|
||||||
let mut inside = false;
|
|
||||||
let mut output = None;
|
|
||||||
|
|
||||||
for item in md {
|
|
||||||
match item {
|
|
||||||
Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(ref info)))
|
|
||||||
if info.to_lowercase() == "cargo" =>
|
|
||||||
{
|
|
||||||
if output.is_some() {
|
|
||||||
anyhow::bail!("multiple `cargo` manifests present")
|
|
||||||
} else {
|
|
||||||
output = Some(String::new());
|
|
||||||
}
|
|
||||||
inside = true;
|
|
||||||
}
|
|
||||||
Event::Text(ref text) if inside => {
|
|
||||||
let s = output.get_or_insert(String::new());
|
|
||||||
s.push_str(text);
|
|
||||||
}
|
|
||||||
Event::End(Tag::CodeBlock(_)) if inside => {
|
|
||||||
inside = false;
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(output)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_expand {
|
mod test_expand {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -627,10 +310,10 @@ strip = true
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dependencies() {
|
fn test_dependencies() {
|
||||||
snapbox::assert_eq(
|
snapbox::assert_matches(
|
||||||
r#"[[bin]]
|
r#"[[bin]]
|
||||||
name = "test-"
|
name = "test-"
|
||||||
path = "/home/me/test.rs"
|
path = [..]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = "0.1.25"
|
time = "0.1.25"
|
||||||
|
@ -649,361 +332,111 @@ strip = true
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
"#,
|
"#,
|
||||||
si!(r#"
|
si!(r#"```cargo
|
||||||
//! ```cargo
|
|
||||||
//! [dependencies]
|
|
||||||
//! time="0.1.25"
|
|
||||||
//! ```
|
|
||||||
fn main() {}
|
|
||||||
"#),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_comment {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
macro_rules! ec {
|
|
||||||
($s:expr) => {
|
|
||||||
extract_comment($s)
|
|
||||||
.unwrap_or_else(|err| panic!("{}", err))
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_no_comment() {
|
|
||||||
assert_eq!(
|
|
||||||
None,
|
|
||||||
extract_comment(
|
|
||||||
r#"
|
|
||||||
fn main () {
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_no_comment_she_bang() {
|
|
||||||
assert_eq!(
|
|
||||||
None,
|
|
||||||
extract_comment(
|
|
||||||
r#"#!/usr/bin/env cargo-eval
|
|
||||||
|
|
||||||
fn main () {
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_comment() {
|
|
||||||
snapbox::assert_eq(
|
|
||||||
r#"Here is a manifest:
|
|
||||||
|
|
||||||
```cargo
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = "*"
|
time="0.1.25"
|
||||||
```
|
```
|
||||||
"#,
|
|
||||||
ec!(r#"//! Here is a manifest:
|
|
||||||
//!
|
|
||||||
//! ```cargo
|
|
||||||
//! [dependencies]
|
|
||||||
//! time = "*"
|
|
||||||
//! ```
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
"#),
|
"#),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_comment_shebang() {
|
fn test_no_infostring() {
|
||||||
snapbox::assert_eq(
|
snapbox::assert_matches(
|
||||||
r#"Here is a manifest:
|
r#"[[bin]]
|
||||||
|
name = "test-"
|
||||||
|
path = [..]
|
||||||
|
|
||||||
```cargo
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = "*"
|
time = "0.1.25"
|
||||||
```
|
|
||||||
"#,
|
|
||||||
ec!(r#"#!/usr/bin/env cargo-eval
|
|
||||||
|
|
||||||
//! Here is a manifest:
|
[package]
|
||||||
//!
|
autobenches = false
|
||||||
//! ```cargo
|
autobins = false
|
||||||
//! [dependencies]
|
autoexamples = false
|
||||||
//! time = "*"
|
autotests = false
|
||||||
//! ```
|
build = false
|
||||||
|
edition = "2021"
|
||||||
|
name = "test-"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = true
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
"#,
|
||||||
|
si!(r#"```
|
||||||
|
[dependencies]
|
||||||
|
time="0.1.25"
|
||||||
|
```
|
||||||
fn main() {}
|
fn main() {}
|
||||||
"#),
|
"#),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multiline_comment() {
|
fn test_dash() {
|
||||||
snapbox::assert_eq(
|
snapbox::assert_matches(
|
||||||
r#"Here is a manifest:
|
r#"[[bin]]
|
||||||
|
name = "test-"
|
||||||
|
path = [..]
|
||||||
|
|
||||||
```cargo
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = "*"
|
time = "0.1.25"
|
||||||
```
|
|
||||||
|
[package]
|
||||||
|
autobenches = false
|
||||||
|
autobins = false
|
||||||
|
autoexamples = false
|
||||||
|
autotests = false
|
||||||
|
build = false
|
||||||
|
edition = "2021"
|
||||||
|
name = "test-"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = true
|
||||||
|
|
||||||
|
[workspace]
|
||||||
"#,
|
"#,
|
||||||
ec!(r#"/*!
|
si!(r#"---
|
||||||
Here is a manifest:
|
|
||||||
|
|
||||||
```cargo
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = "*"
|
time="0.1.25"
|
||||||
```
|
---
|
||||||
*/
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
}
|
|
||||||
"#),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_multiline_comment_shebang() {
|
|
||||||
snapbox::assert_eq(
|
|
||||||
r#"Here is a manifest:
|
|
||||||
|
|
||||||
```cargo
|
|
||||||
[dependencies]
|
|
||||||
time = "*"
|
|
||||||
```
|
|
||||||
"#,
|
|
||||||
ec!(r#"#!/usr/bin/env cargo-eval
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Here is a manifest:
|
|
||||||
|
|
||||||
```cargo
|
|
||||||
[dependencies]
|
|
||||||
time = "*"
|
|
||||||
```
|
|
||||||
*/
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
}
|
|
||||||
"#),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_multiline_block_comment() {
|
|
||||||
snapbox::assert_eq(
|
|
||||||
r#"Here is a manifest:
|
|
||||||
|
|
||||||
```cargo
|
|
||||||
[dependencies]
|
|
||||||
time = "*"
|
|
||||||
```
|
|
||||||
"#,
|
|
||||||
ec!(r#"/*!
|
|
||||||
* Here is a manifest:
|
|
||||||
*
|
|
||||||
* ```cargo
|
|
||||||
* [dependencies]
|
|
||||||
* time = "*"
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
"#),
|
"#),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multiline_block_comment_shebang() {
|
fn test_hash() {
|
||||||
snapbox::assert_eq(
|
snapbox::assert_matches(
|
||||||
r#"Here is a manifest:
|
r#"[[bin]]
|
||||||
|
name = "test-"
|
||||||
|
path = [..]
|
||||||
|
|
||||||
```cargo
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = "*"
|
time = "0.1.25"
|
||||||
```
|
|
||||||
"#,
|
|
||||||
ec!(r#"#!/usr/bin/env cargo-eval
|
|
||||||
|
|
||||||
/*!
|
[package]
|
||||||
* Here is a manifest:
|
autobenches = false
|
||||||
*
|
autobins = false
|
||||||
* ```cargo
|
autoexamples = false
|
||||||
* [dependencies]
|
autotests = false
|
||||||
* time = "*"
|
build = false
|
||||||
* ```
|
edition = "2021"
|
||||||
*/
|
name = "test-"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = true
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
"#,
|
||||||
|
si!(r#"###
|
||||||
|
[dependencies]
|
||||||
|
time="0.1.25"
|
||||||
|
###
|
||||||
fn main() {}
|
fn main() {}
|
||||||
"#),
|
"#),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_adjacent_comments() {
|
|
||||||
snapbox::assert_eq(
|
|
||||||
r#"Here is a manifest:
|
|
||||||
|
|
||||||
```cargo
|
|
||||||
[dependencies]
|
|
||||||
time = "*"
|
|
||||||
```
|
|
||||||
"#,
|
|
||||||
ec!(r#"#!/usr/bin/env cargo-eval
|
|
||||||
|
|
||||||
// I am a normal comment
|
|
||||||
//! Here is a manifest:
|
|
||||||
//!
|
|
||||||
//! ```cargo
|
|
||||||
//! [dependencies]
|
|
||||||
//! time = "*"
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
fn main () {
|
|
||||||
}
|
|
||||||
"#),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_doc_attrib() {
|
|
||||||
snapbox::assert_eq(
|
|
||||||
r#"Here is a manifest:
|
|
||||||
|
|
||||||
```cargo
|
|
||||||
[dependencies]
|
|
||||||
time = "*"
|
|
||||||
```
|
|
||||||
"#,
|
|
||||||
ec!(r###"#!/usr/bin/env cargo-eval
|
|
||||||
|
|
||||||
#![doc = r#"Here is a manifest:
|
|
||||||
|
|
||||||
```cargo
|
|
||||||
[dependencies]
|
|
||||||
time = "*"
|
|
||||||
```
|
|
||||||
"#]
|
|
||||||
|
|
||||||
fn main () {
|
|
||||||
}
|
|
||||||
"###),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_manifest {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
macro_rules! smm {
|
|
||||||
($c:expr) => {
|
|
||||||
extract_manifest($c)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_no_code_fence() {
|
|
||||||
assert_eq!(
|
|
||||||
smm!(
|
|
||||||
r#"There is no manifest in this comment.
|
|
||||||
"#
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
None
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_no_cargo_code_fence() {
|
|
||||||
assert_eq!(
|
|
||||||
smm!(
|
|
||||||
r#"There is no manifest in this comment.
|
|
||||||
|
|
||||||
```
|
|
||||||
This is not a manifest.
|
|
||||||
```
|
|
||||||
|
|
||||||
```rust
|
|
||||||
println!("Nor is this.");
|
|
||||||
```
|
|
||||||
|
|
||||||
Or this.
|
|
||||||
"#
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
None
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cargo_code_fence() {
|
|
||||||
assert_eq!(
|
|
||||||
smm!(
|
|
||||||
r#"This is a manifest:
|
|
||||||
|
|
||||||
```cargo
|
|
||||||
dependencies = { time = "*" }
|
|
||||||
```
|
|
||||||
"#
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
Some(
|
|
||||||
r#"dependencies = { time = "*" }
|
|
||||||
"#
|
|
||||||
.into()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mixed_code_fence() {
|
|
||||||
assert_eq!(
|
|
||||||
smm!(
|
|
||||||
r#"This is *not* a manifest:
|
|
||||||
|
|
||||||
```
|
|
||||||
He's lying, I'm *totally* a manifest!
|
|
||||||
```
|
|
||||||
|
|
||||||
This *is*:
|
|
||||||
|
|
||||||
```cargo
|
|
||||||
dependencies = { time = "*" }
|
|
||||||
```
|
|
||||||
"#
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
Some(
|
|
||||||
r#"dependencies = { time = "*" }
|
|
||||||
"#
|
|
||||||
.into()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_two_cargo_code_fence() {
|
|
||||||
assert!(smm!(
|
|
||||||
r#"This is a manifest:
|
|
||||||
|
|
||||||
```cargo
|
|
||||||
dependencies = { time = "*" }
|
|
||||||
```
|
|
||||||
|
|
||||||
So is this, but it doesn't count:
|
|
||||||
|
|
||||||
```cargo
|
|
||||||
dependencies = { explode = true }
|
|
||||||
```
|
|
||||||
"#
|
|
||||||
)
|
|
||||||
.is_err());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue