Skip to content

Commit

Permalink
feat(snap): Allow compiling examples
Browse files Browse the repository at this point in the history
  • Loading branch information
epage committed Feb 19, 2023
1 parent 70a7a10 commit bb8da41
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/snapbox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ detect-encoding = ["dep:content_inspector"]
path = ["dep:tempfile", "dep:walkdir", "dep:dunce", "detect-encoding", "dep:filetime"]
## Snapshotting of commands
cmd = ["dep:os_pipe", "dep:wait-timeout", "dep:libc", "dep:windows-sys"]
## Building of examples for snapshotting
examples = ["dep:escargot"]

## Snapshotting of json
json = ["structured-data"]
Expand Down Expand Up @@ -85,6 +87,7 @@ filetime = { version = "0.2", optional = true }

os_pipe = { version = "1.0", optional = true }
wait-timeout = { version = "0.2.0", optional = true }
escargot = { version = "0.5.7", optional = true }
libc = { version = "0.2.137", optional = true }
windows-sys = { version = "0.45.0", features = ["Win32_Foundation"], optional = true }

Expand Down
1 change: 1 addition & 0 deletions crates/snapbox/examples/example-fixture.rs
138 changes: 138 additions & 0 deletions crates/snapbox/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1027,3 +1027,141 @@ fn target_dir() -> std::path::PathBuf {
})
.unwrap()
}

#[cfg(feature = "examples")]
pub use examples::{compile_example, compile_examples};

#[cfg(feature = "examples")]
pub(crate) mod examples {
/// Prepare an example for testing
///
/// Unlike `cargo_bin!`, this does not inherit all of the current compiler settings. It
/// will match the current target and profile but will not get feature flags. Pass those arguments
/// to the compiler via `args`.
///
/// ## Example
///
/// ```rust,no_run
/// snapbox::cmd::compile_example("example-fixture", []);
/// ```
#[cfg(feature = "examples")]
pub fn compile_example<'a>(
target_name: &str,
args: impl IntoIterator<Item = &'a str>,
) -> Result<std::path::PathBuf, crate::Error> {
crate::debug!("Compiling example {}", target_name);
let messages = escargot::CargoBuild::new()
.current_target()
.current_release()
.example(target_name)
.args(args)
.exec()
.map_err(|e| crate::Error::new(e.to_string()))?;
for message in messages {
let message = message.map_err(|e| crate::Error::new(e.to_string()))?;
let message = message
.decode()
.map_err(|e| crate::Error::new(e.to_string()))?;
crate::debug!("Message: {:?}", message);
if let Some(bin) = decode_example_message(&message) {
let (name, bin) = bin?;
assert_eq!(target_name, name);
return bin;
}
}

Err(crate::Error::new(format!(
"Unknown error building example {}",
target_name
)))
}

/// Prepare all examples for testing
///
/// Unlike `cargo_bin!`, this does not inherit all of the current compiler settings. It
/// will match the current target and profile but will not get feature flags. Pass those arguments
/// to the compiler via `args`.
///
/// ## Example
///
/// ```rust,no_run
/// let examples = snapbox::cmd::compile_examples([]).unwrap().collect::<Vec<_>>();
/// ```
#[cfg(feature = "examples")]
pub fn compile_examples<'a>(
args: impl IntoIterator<Item = &'a str>,
) -> Result<
impl Iterator<Item = (String, Result<std::path::PathBuf, crate::Error>)>,
crate::Error,
> {
crate::debug!("Compiling examples");
let mut examples = std::collections::BTreeMap::new();

let messages = escargot::CargoBuild::new()
.current_target()
.current_release()
.examples()
.args(args)
.exec()
.map_err(|e| crate::Error::new(e.to_string()))?;
for message in messages {
let message = message.map_err(|e| crate::Error::new(e.to_string()))?;
let message = message
.decode()
.map_err(|e| crate::Error::new(e.to_string()))?;
crate::debug!("Message: {:?}", message);
if let Some(bin) = decode_example_message(&message) {
let (name, bin) = bin?;
examples.insert(name.to_owned(), bin);
}
}

Ok(examples.into_iter())
}

#[allow(clippy::type_complexity)]
fn decode_example_message<'m>(
message: &'m escargot::format::Message,
) -> Option<Result<(&'m str, Result<std::path::PathBuf, crate::Error>), crate::Error>> {
match message {
escargot::format::Message::CompilerMessage(msg) => {
let level = msg.message.level;
if level == escargot::format::diagnostic::DiagnosticLevel::Ice
|| level == escargot::format::diagnostic::DiagnosticLevel::Error
{
let output = msg
.message
.rendered
.as_deref()
.unwrap_or_else(|| msg.message.message.as_ref())
.to_owned();
if is_example_target(&msg.target) {
let bin = Err(crate::Error::new(output));
Some(Ok((msg.target.name.as_ref(), bin)))
} else {
Some(Err(crate::Error::new(output)))
}
} else {
None
}
}
escargot::format::Message::CompilerArtifact(artifact) => {
if !artifact.profile.test && is_example_target(&artifact.target) {
let path = artifact
.executable
.clone()
.expect("cargo is new enough for this to be present");
let bin = Ok(path.into_owned());
Some(Ok((artifact.target.name.as_ref(), bin)))
} else {
None
}
}
_ => None,
}
}

fn is_example_target(target: &escargot::format::Target) -> bool {
target.crate_types == ["bin"] && target.kind == ["example"]
}
}

0 comments on commit bb8da41

Please sign in to comment.