use cargo::core::features;
-use cargo::{self, CliResult, Config};
+use cargo::{self, drop_print, drop_println, CliResult, Config};
use clap::{AppSettings, Arg, ArgMatches};
use super::commands;
};
if args.value_of("unstable-features") == Some("help") {
- println!(
+ drop_println!(
+ config,
"
Available unstable (nightly-only) flags:
Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
);
if !features::nightly_features_allowed() {
- println!(
+ drop_println!(
+ config,
"\nUnstable flags are only available on the nightly channel \
of Cargo, but this is the `{}` channel.\n\
{}",
features::SEE_CHANNELS
);
}
- println!(
+ drop_println!(
+ config,
"\nSee https://doc.rust-lang.org/nightly/cargo/reference/unstable.html \
for more information about these flags."
);
let is_verbose = args.occurrences_of("verbose") > 0;
if args.is_present("version") {
let version = get_version_string(is_verbose);
- print!("{}", version);
+ drop_print!(config, "{}", version);
return Ok(());
}
}
if args.is_present("list") {
- println!("Installed Commands:");
+ drop_println!(config, "Installed Commands:");
for command in list_commands(config) {
match command {
CommandInfo::BuiltIn { name, about } => {
let summary = about.unwrap_or_default();
let summary = summary.lines().next().unwrap_or(&summary); // display only the first line
- println!(" {:<20} {}", name, summary)
+ drop_println!(config, " {:<20} {}", name, summary);
}
CommandInfo::External { name, path } => {
if is_verbose {
- println!(" {:<20} {}", name, path.display())
+ drop_println!(config, " {:<20} {}", name, path.display());
} else {
- println!(" {}", name)
+ drop_println!(config, " {}", name);
}
}
}
use crate::command_prelude::*;
-
-use cargo::print_json;
use serde::Serialize;
pub fn cli() -> App {
let location = ProjectLocation { root };
- print_json(&location);
+ config.shell().print_json(&location);
Ok(())
}
use crate::command_prelude::*;
-
use cargo::ops::{self, OutputMetadataOptions};
-use cargo::print_json;
pub fn cli() -> App {
subcommand("metadata")
};
let result = ops::output_metadata(&ws, &options)?;
- print_json(&result);
+ config.shell().print_json(&result);
Ok(())
}
let ws = args.workspace(config)?;
let spec = args.value_of("spec").or_else(|| args.value_of("package"));
let spec = ops::pkgid(&ws, spec)?;
- println!("{}", spec);
+ cargo::drop_println!(config, "{}", spec);
Ok(())
}
use crate::command_prelude::*;
-use cargo::print_json;
-
pub fn cli() -> App {
subcommand("read-manifest")
.about(
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let ws = args.workspace(config)?;
- print_json(&ws.current()?);
+ config.shell().print_json(&ws.current()?);
Ok(())
}
if args.is_present("version") {
let verbose = args.occurrences_of("verbose") > 0;
let version = cli::get_version_string(verbose);
- print!("{}", version);
+ cargo::drop_print!(config, "{}", version);
return Ok(());
}
let prefix = if args.is_present("no-indent") {
use std::collections::HashMap;
use std::process;
-use cargo::print_json;
-
pub fn cli() -> App {
subcommand("verify-project")
.about("Check correctness of crate manifest")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
- fn fail(reason: &str, value: &str) -> ! {
+ if let Err(e) = args.workspace(config) {
let mut h = HashMap::new();
- h.insert(reason.to_string(), value.to_string());
- print_json(&h);
+ h.insert("invalid".to_string(), e.to_string());
+ config.shell().print_json(&h);
process::exit(1)
}
- if let Err(e) = args.workspace(config) {
- fail("invalid", &e.to_string())
- }
-
let mut h = HashMap::new();
h.insert("success".to_string(), "true".to_string());
- print_json(&h);
+ config.shell().print_json(&h);
Ok(())
}
-use crate::command_prelude::*;
-
use crate::cli;
+use crate::command_prelude::*;
pub fn cli() -> App {
subcommand("version")
.arg(opt("quiet", "No output printed to stdout").short("q"))
}
-pub fn exec(_config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
+pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let verbose = args.occurrences_of("verbose") > 0;
let version = cli::get_version_string(verbose);
- print!("{}", version);
+ cargo::drop_print!(config, "{}", version);
Ok(())
}
plan.update(&module_name, &cmd, &filenames)?;
}
Message::Stdout(out) => {
- cx.bcx.config.shell().stdout_println(out);
+ writeln!(cx.bcx.config.shell().out(), "{}", out)?;
}
Message::Stderr(err) => {
let mut shell = cx.bcx.config.shell();
success: error.is_none(),
}
.to_json_string();
- cx.bcx.config.shell().stdout_println(msg);
+ writeln!(cx.bcx.config.shell().out(), "{}", msg)?;
}
if let Some(e) = error {
&unit.target,
cx.files().message_cache_path(unit),
cx.bcx.build_config.message_format,
- cx.bcx.config.shell().supports_color(),
+ cx.bcx.config.shell().err_supports_color(),
)
} else {
Work::noop()
impl OutputOptions {
fn new(cx: &Context<'_, '_>, unit: &Unit) -> OutputOptions {
let look_for_metadata_directive = cx.rmeta_required(unit);
- let color = cx.bcx.config.shell().supports_color();
+ let color = cx.bcx.config.shell().err_supports_color();
let path = cx.files().message_cache_path(unit);
// Remove old cache, ignore ENOENT, which is the common case.
drop(fs::remove_file(&path));
rmeta_time: unit_time.rmeta_time,
}
.to_json_string();
- self.config.shell().stdout_println(msg);
+ crate::drop_println!(self.config, "{}", msg);
}
self.unit_times.push(unit_time);
}
let stdout = std::io::stdout();
let mut lock = stdout.lock();
serde_json::to_writer(&mut lock, &s)?;
- writeln!(lock)?;
+ drop(writeln!(lock));
Ok(())
}
pub fn print_teapot(&self, config: &Config) {
if let Some(teapot) = self.im_a_teapot {
if config.cli_unstable().print_im_a_teapot {
- println!("im-a-teapot = {}", teapot);
+ crate::drop_println!(config, "im-a-teapot = {}", teapot);
}
}
}
for ((pkg_id, dep_kind), features) in &self.activated_features {
let r_features = self.resolve.features(*pkg_id);
if !r_features.iter().eq(features.iter()) {
- eprintln!(
+ crate::drop_eprintln!(
+ self.ws.config(),
"{}/{:?} features mismatch\nresolve: {:?}\nnew: {:?}\n",
- pkg_id, dep_kind, r_features, features
+ pkg_id,
+ dep_kind,
+ r_features,
+ features
);
found = true;
}
Quiet,
}
-/// An abstraction around a `Write`able object that remembers preferences for output verbosity and
-/// color.
+/// An abstraction around console output that remembers preferences for output
+/// verbosity and color.
pub struct Shell {
- /// the `Write`able object, either with or without color support (represented by different enum
- /// variants)
- err: ShellOut,
- /// How verbose messages should be
+ /// Wrapper around stdout/stderr. This helps with supporting sending
+ /// output to a memory buffer which is useful for tests.
+ output: ShellOut,
+ /// How verbose messages should be.
verbosity: Verbosity,
/// Flag that indicates the current line needs to be cleared before
/// printing. Used when a progress bar is currently displayed.
impl fmt::Debug for Shell {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.err {
+ match self.output {
ShellOut::Write(_) => f
.debug_struct("Shell")
.field("verbosity", &self.verbosity)
Write(Box<dyn Write>),
/// Color-enabled stdio, with information on whether color should be used
Stream {
- stream: StandardStream,
- tty: bool,
+ stdout: StandardStream,
+ stderr: StandardStream,
+ stderr_tty: bool,
color_choice: ColorChoice,
},
}
/// Creates a new shell (color choice and verbosity), defaulting to 'auto' color and verbose
/// output.
pub fn new() -> Shell {
+ let auto = ColorChoice::CargoAuto.to_termcolor_color_choice();
Shell {
- err: ShellOut::Stream {
- stream: StandardStream::stderr(ColorChoice::CargoAuto.to_termcolor_color_choice()),
+ output: ShellOut::Stream {
+ stdout: StandardStream::stdout(auto),
+ stderr: StandardStream::stderr(auto),
color_choice: ColorChoice::CargoAuto,
- tty: atty::is(atty::Stream::Stderr),
+ stderr_tty: atty::is(atty::Stream::Stderr),
},
verbosity: Verbosity::Verbose,
needs_clear: false,
/// Creates a shell from a plain writable object, with no color, and max verbosity.
pub fn from_write(out: Box<dyn Write>) -> Shell {
Shell {
- err: ShellOut::Write(out),
+ output: ShellOut::Write(out),
verbosity: Verbosity::Verbose,
needs_clear: false,
}
if self.needs_clear {
self.err_erase_line();
}
- self.err.print(status, message, color, justified)
+ self.output
+ .message_stderr(status, message, color, justified)
}
}
}
- pub fn stdout_println(&mut self, message: impl fmt::Display) {
- if self.needs_clear {
- self.err_erase_line();
- }
- println!("{}", message);
- }
-
/// Sets whether the next print should clear the current line.
pub fn set_needs_clear(&mut self, needs_clear: bool) {
self.needs_clear = needs_clear;
/// Returns the width of the terminal in spaces, if any.
pub fn err_width(&self) -> Option<usize> {
- match self.err {
- ShellOut::Stream { tty: true, .. } => imp::stderr_width(),
+ match self.output {
+ ShellOut::Stream {
+ stderr_tty: true, ..
+ } => imp::stderr_width(),
_ => None,
}
}
/// Returns `true` if stderr is a tty.
pub fn is_err_tty(&self) -> bool {
- match self.err {
- ShellOut::Stream { tty, .. } => tty,
+ match self.output {
+ ShellOut::Stream { stderr_tty, .. } => stderr_tty,
_ => false,
}
}
- /// Gets a reference to the underlying writer.
+ /// Gets a reference to the underlying stdout writer.
+ pub fn out(&mut self) -> &mut dyn Write {
+ if self.needs_clear {
+ self.err_erase_line();
+ }
+ self.output.stdout()
+ }
+
+ /// Gets a reference to the underlying stderr writer.
pub fn err(&mut self) -> &mut dyn Write {
if self.needs_clear {
self.err_erase_line();
}
- self.err.as_write()
+ self.output.stderr()
}
/// Erase from cursor to end of line.
pub fn err_erase_line(&mut self) {
- if let ShellOut::Stream { tty: true, .. } = self.err {
+ if let ShellOut::Stream {
+ stderr_tty: true, ..
+ } = self.output
+ {
imp::err_erase_line(self);
self.needs_clear = false;
}
if self.needs_clear {
self.err_erase_line();
}
- self.err.print(&"error", Some(&message), Red, false)
+ self.output
+ .message_stderr(&"error", Some(&message), Red, false)
}
/// Prints an amber 'warning' message.
/// Updates the color choice (always, never, or auto) from a string..
pub fn set_color_choice(&mut self, color: Option<&str>) -> CargoResult<()> {
if let ShellOut::Stream {
- ref mut stream,
+ ref mut stdout,
+ ref mut stderr,
ref mut color_choice,
..
- } = self.err
+ } = self.output
{
let cfg = match color {
Some("always") => ColorChoice::Always,
),
};
*color_choice = cfg;
- *stream = StandardStream::stderr(cfg.to_termcolor_color_choice());
+ let choice = cfg.to_termcolor_color_choice();
+ *stdout = StandardStream::stdout(choice);
+ *stderr = StandardStream::stderr(choice);
}
Ok(())
}
/// If we are not using a color stream, this will always return `Never`, even if the color
/// choice has been set to something else.
pub fn color_choice(&self) -> ColorChoice {
- match self.err {
+ match self.output {
ShellOut::Stream { color_choice, .. } => color_choice,
ShellOut::Write(_) => ColorChoice::Never,
}
}
/// Whether the shell supports color.
- pub fn supports_color(&self) -> bool {
- match &self.err {
+ pub fn err_supports_color(&self) -> bool {
+ match &self.output {
ShellOut::Write(_) => false,
- ShellOut::Stream { stream, .. } => stream.supports_color(),
+ ShellOut::Stream { stderr, .. } => stderr.supports_color(),
}
}
self.err().write_all(message)?;
Ok(())
}
+
+ pub fn print_json<T: serde::ser::Serialize>(&mut self, obj: &T) {
+ let encoded = serde_json::to_string(&obj).unwrap();
+ drop(writeln!(self.out(), "{}", encoded));
+ }
}
impl Default for Shell {
/// Prints out a message with a status. The status comes first, and is bold plus the given
/// color. The status can be justified, in which case the max width that will right align is
/// 12 chars.
- fn print(
+ fn message_stderr(
&mut self,
status: &dyn fmt::Display,
message: Option<&dyn fmt::Display>,
justified: bool,
) -> CargoResult<()> {
match *self {
- ShellOut::Stream { ref mut stream, .. } => {
- stream.reset()?;
- stream.set_color(ColorSpec::new().set_bold(true).set_fg(Some(color)))?;
+ ShellOut::Stream { ref mut stderr, .. } => {
+ stderr.reset()?;
+ stderr.set_color(ColorSpec::new().set_bold(true).set_fg(Some(color)))?;
if justified {
- write!(stream, "{:>12}", status)?;
+ write!(stderr, "{:>12}", status)?;
} else {
- write!(stream, "{}", status)?;
- stream.set_color(ColorSpec::new().set_bold(true))?;
- write!(stream, ":")?;
+ write!(stderr, "{}", status)?;
+ stderr.set_color(ColorSpec::new().set_bold(true))?;
+ write!(stderr, ":")?;
}
- stream.reset()?;
+ stderr.reset()?;
match message {
- Some(message) => writeln!(stream, " {}", message)?,
- None => write!(stream, " ")?,
+ Some(message) => writeln!(stderr, " {}", message)?,
+ None => write!(stderr, " ")?,
}
}
ShellOut::Write(ref mut w) => {
Ok(())
}
- /// Gets this object as a `io::Write`.
- fn as_write(&mut self) -> &mut dyn Write {
+ /// Gets stdout as a `io::Write`.
+ fn stdout(&mut self) -> &mut dyn Write {
+ match *self {
+ ShellOut::Stream { ref mut stdout, .. } => stdout,
+ ShellOut::Write(ref mut w) => w,
+ }
+ }
+
+ /// Gets stderr as a `io::Write`.
+ fn stderr(&mut self) -> &mut dyn Write {
match *self {
- ShellOut::Stream { ref mut stream, .. } => stream,
+ ShellOut::Stream { ref mut stderr, .. } => stderr,
ShellOut::Write(ref mut w) => w,
}
}
// This is the "EL - Erase in Line" sequence. It clears from the cursor
// to the end of line.
// https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
- let _ = shell.err.as_write().write_all(b"\x1B[K");
+ let _ = shell.output.stderr().write_all(b"\x1B[K");
}
}
use crate::core::Shell;
use anyhow::Error;
use log::debug;
-use serde::ser;
use std::fmt;
pub use crate::util::errors::{InternalError, VerboseError};
}
}
-pub fn print_json<T: ser::Serialize>(obj: &T) {
- let encoded = serde_json::to_string(&obj).unwrap();
- println!("{}", encoded);
-}
-
pub fn exit_with_error(err: CliError, shell: &mut Shell) -> ! {
debug!("exit_with_error; err={:?}", err);
if let Some(ref err) = err.error {
use crate::core::compiler::Freshness;
use crate::core::compiler::{CompileKind, DefaultExecutor, Executor};
use crate::core::{Edition, Package, PackageId, Source, SourceId, Workspace};
-use crate::ops;
use crate::ops::common_for_install_and_uninstall::*;
use crate::sources::{GitSource, SourceConfigMap};
use crate::util::errors::{CargoResult, CargoResultExt};
use crate::util::{paths, Config, Filesystem};
+use crate::{drop_println, ops};
struct Transaction {
bins: Vec<PathBuf>,
let root = resolve_root(dst, config)?;
let tracker = InstallTracker::load(config, &root)?;
for (k, v) in tracker.all_installed_bins() {
- println!("{}:", k);
+ drop_println!(config, "{}:", k);
for bin in v {
- println!(" {}", bin);
+ drop_println!(config, " {}", bin);
}
}
Ok(())
use crate::core::compiler::{BuildConfig, CompileMode, DefaultExecutor, Executor};
use crate::core::{Feature, Shell, Verbosity, Workspace};
use crate::core::{Package, PackageId, PackageSet, Resolve, Source, SourceId};
-use crate::ops;
use crate::sources::PathSource;
use crate::util::errors::{CargoResult, CargoResultExt};
use crate::util::paths;
use crate::util::toml::TomlManifest;
use crate::util::{self, restricted_names, Config, FileLock};
+use crate::{drop_println, ops};
pub struct PackageOpts<'cfg> {
pub config: &'cfg Config,
if opts.list {
for ar_file in ar_files {
- println!("{}", ar_file.rel_str);
+ drop_println!(config, "{}", ar_file.rel_str);
}
return Ok(None);
}
use crate::util::important_paths::find_root_manifest_for_wd;
use crate::util::IntoUrl;
use crate::util::{paths, validate_package_name};
-use crate::version;
+use crate::{drop_print, drop_println, version};
/// Registry settings loaded from config files.
///
let token = match token {
Some(token) => token,
None => {
- println!(
+ drop_println!(
+ config,
"please visit {}/me and paste the API Token below",
registry.host()
);
.list_owners(&name)
.chain_err(|| format!("failed to list owners of crate {}", name))?;
for owner in owners.iter() {
- print!("{}", owner.login);
+ drop_print!(config, "{}", owner.login);
match (owner.name.as_ref(), owner.email.as_ref()) {
- (Some(name), Some(email)) => println!(" ({} <{}>)", name, email),
- (Some(s), None) | (None, Some(s)) => println!(" ({})", s),
- (None, None) => println!(),
+ (Some(name), Some(email)) => drop_println!(config, " ({} <{}>)", name, email),
+ (Some(s), None) | (None, Some(s)) => drop_println!(config, " ({})", s),
+ (None, None) => drop_println!(config),
}
}
}
}
None => name,
};
- println!("{}", line);
+ drop_println!(config, "{}", line);
}
let search_max_limit = 100;
if total_crates > limit && limit < search_max_limit {
- println!(
+ drop_println!(
+ config,
"... and {} crates more (use --limit N to see more)",
total_crates - limit
);
} else {
String::new()
};
- println!("... and {} crates more{}", total_crates - limit, extra);
+ drop_println!(
+ config,
+ "... and {} crates more{}",
+ total_crates - limit,
+ extra
+ );
}
Ok(())
use crate::core::resolver::{HasDevUnits, ResolveOpts};
use crate::core::{Package, PackageId, PackageIdSpec, Workspace};
use crate::ops::{self, Packages};
-use crate::util::CargoResult;
+use crate::util::{CargoResult, Config};
+use crate::{drop_print, drop_println};
use anyhow::{bail, Context};
use graph::Graph;
use std::collections::{HashMap, HashSet};
graph.invert();
}
- print(opts, root_indexes, &graph)?;
+ print(ws.config(), opts, root_indexes, &graph)?;
Ok(())
}
/// Prints a tree for each given root.
-fn print(opts: &TreeOptions, roots: Vec<usize>, graph: &Graph<'_>) -> CargoResult<()> {
+fn print(
+ config: &Config,
+ opts: &TreeOptions,
+ roots: Vec<usize>,
+ graph: &Graph<'_>,
+) -> CargoResult<()> {
let format = Pattern::new(&opts.format)
.with_context(|| format!("tree format `{}` not valid", opts.format))?;
for (i, root_index) in roots.into_iter().enumerate() {
if i != 0 {
- println!();
+ drop_println!(config);
}
// A stack of bools used to determine where | symbols should appear
let mut print_stack = vec![];
print_node(
+ config,
graph,
root_index,
&format,
/// Prints a package and all of its dependencies.
fn print_node<'a>(
+ config: &Config,
graph: &'a Graph<'_>,
node_index: usize,
format: &Pattern,
let new = no_dedupe || visited_deps.insert(node_index);
match prefix {
- Prefix::Depth => print!("{}", levels_continue.len()),
+ Prefix::Depth => drop_print!(config, "{}", levels_continue.len()),
Prefix::Indent => {
if let Some((last_continues, rest)) = levels_continue.split_last() {
for continues in rest {
let c = if *continues { symbols.down } else { " " };
- print!("{} ", c);
+ drop_print!(config, "{} ", c);
}
let c = if *last_continues {
} else {
symbols.ell
};
- print!("{0}{1}{1} ", c, symbols.right);
+ drop_print!(config, "{0}{1}{1} ", c, symbols.right);
}
}
Prefix::None => {}
} else {
" (*)"
};
- println!("{}{}", format.display(graph, node_index), star);
+ drop_println!(config, "{}{}", format.display(graph, node_index), star);
if !new || in_cycle {
return;
EdgeKind::Feature,
] {
print_dependencies(
+ config,
graph,
node_index,
format,
/// Prints all the dependencies of a package for the given dependency kind.
fn print_dependencies<'a>(
+ config: &Config,
graph: &'a Graph<'_>,
node_index: usize,
format: &Pattern,
if let Some(name) = name {
for continues in &**levels_continue {
let c = if *continues { symbols.down } else { " " };
- print!("{} ", c);
+ drop_print!(config, "{} ", c);
}
- println!("{}", name);
+ drop_println!(config, "{}", name);
}
}
while let Some(dependency) = it.next() {
levels_continue.push(it.peek().is_some());
print_node(
+ config,
graph,
*dependency,
format,
}
pub fn vendor(ws: &Workspace<'_>, opts: &VendorOptions<'_>) -> CargoResult<()> {
+ let config = ws.config();
let mut extra_workspaces = Vec::new();
for extra in opts.extra.iter() {
- let extra = ws.config().cwd().join(extra);
- let ws = Workspace::new(&extra, ws.config())?;
+ let extra = config.cwd().join(extra);
+ let ws = Workspace::new(&extra, config)?;
extra_workspaces.push(ws);
}
let workspaces = extra_workspaces.iter().chain(Some(ws)).collect::<Vec<_>>();
let vendor_config =
- sync(ws.config(), &workspaces, opts).chain_err(|| "failed to sync".to_string())?;
-
- let shell = ws.config().shell();
- if shell.verbosity() != Verbosity::Quiet {
- eprint!("To use vendored sources, add this to your .cargo/config for this project:\n\n");
- print!("{}", &toml::to_string(&vendor_config).unwrap());
+ sync(config, &workspaces, opts).chain_err(|| "failed to sync".to_string())?;
+
+ if config.shell().verbosity() != Verbosity::Quiet {
+ crate::drop_eprint!(
+ config,
+ "To use vendored sources, add this to your .cargo/config for this project:\n\n"
+ );
+ crate::drop_print!(config, "{}", &toml::to_string(&vendor_config).unwrap());
}
Ok(())
&self.0
}
}
+
+#[macro_export]
+macro_rules! __shell_print {
+ ($config:expr, $which:ident, $newline:literal, $($arg:tt)*) => ({
+ let mut shell = $config.shell();
+ let out = shell.$which();
+ drop(out.write_fmt(format_args!($($arg)*)));
+ if $newline {
+ drop(out.write_all(b"\n"));
+ }
+ });
+}
+
+#[macro_export]
+macro_rules! drop_println {
+ ($config:expr) => ( $crate::drop_print!($config, "\n") );
+ ($config:expr, $($arg:tt)*) => (
+ $crate::__shell_print!($config, out, true, $($arg)*)
+ );
+}
+
+#[macro_export]
+macro_rules! drop_eprintln {
+ ($config:expr) => ( $crate::drop_eprint!($config, "\n") );
+ ($config:expr, $($arg:tt)*) => (
+ $crate::__shell_print!($config, err, true, $($arg)*)
+ );
+}
+
+#[macro_export]
+macro_rules! drop_print {
+ ($config:expr, $($arg:tt)*) => (
+ $crate::__shell_print!($config, out, false, $($arg)*)
+ );
+}
+
+#[macro_export]
+macro_rules! drop_eprint {
+ ($config:expr, $($arg:tt)*) => (
+ $crate::__shell_print!($config, err, false, $($arg)*)
+ );
+}
use std::env;
use std::fs::{self, File};
+use std::io::Read;
use std::path::{Path, PathBuf};
+use std::process::Stdio;
use std::str;
use cargo_test_support::cargo_process;
.with_stdout_contains(" -Z unstable-options -- Allow the usage of unstable options")
.run();
}
+
+#[cargo_test]
+fn closed_output_ok() {
+ // Checks that closed output doesn't cause an error.
+ let mut p = cargo_process("--list").build_command();
+ p.stdout(Stdio::piped()).stderr(Stdio::piped());
+ let mut child = p.spawn().unwrap();
+ // Close stdout
+ drop(child.stdout.take());
+ // Read stderr
+ let mut s = String::new();
+ child
+ .stderr
+ .as_mut()
+ .unwrap()
+ .read_to_string(&mut s)
+ .unwrap();
+ let status = child.wait().unwrap();
+ assert!(status.success());
+ assert!(s.is_empty(), s);
+}
"id": 70,
"login": "github:rust-lang:core",
"name": "Core"
+ },
+ {
+ "id": 123,
+ "login": "octocat"
}
]
}"#;
.file("src/main.rs", "fn main() {}")
.build();
- p.cargo("owner -l --token sekrit").run();
+ p.cargo("owner -l --token sekrit")
+ .with_stdout(
+ "\
+github:rust-lang:core (Core)
+octocat
+",
+ )
+ .run();
}
#[cargo_test]
"repository": "https://github.com/nick29581/libhoare",
"updated_at": "2014-11-20T21:49:21Z",
"versions": null
- }],
+ },
+ {
+ "id": "postgres",
+ "name": "postgres",
+ "updated_at": "2020-05-01T23:17:54.335921+00:00",
+ "versions": null,
+ "keywords": null,
+ "categories": null,
+ "badges": [
+ {
+ "badge_type": "circle-ci",
+ "attributes": {
+ "repository": "sfackler/rust-postgres",
+ "branch": null
+ }
+ }
+ ],
+ "created_at": "2014-11-24T02:34:44.756689+00:00",
+ "downloads": 535491,
+ "recent_downloads": 88321,
+ "max_version": "0.17.3",
+ "newest_version": "0.17.3",
+ "description": "A native, synchronous PostgreSQL client",
+ "homepage": null,
+ "documentation": null,
+ "repository": "https://github.com/sfackler/rust-postgres",
+ "links": {
+ "version_downloads": "/api/v1/crates/postgres/downloads",
+ "versions": "/api/v1/crates/postgres/versions",
+ "owners": "/api/v1/crates/postgres/owners",
+ "owner_team": "/api/v1/crates/postgres/owner_team",
+ "owner_user": "/api/v1/crates/postgres/owner_user",
+ "reverse_dependencies": "/api/v1/crates/postgres/reverse_dependencies"
+ },
+ "exact_match": true
+ }
+ ],
"meta": {
- "total": 1
+ "total": 2
}
}"#;
}
}
+const SEARCH_RESULTS: &str = "\
+hoare = \"0.1.1\" # Design by contract style assertions for Rust
+postgres = \"0.17.3\" # A native, synchronous PostgreSQL client
+";
+
fn setup() {
let cargo_home = paths::root().join(".cargo");
fs::create_dir_all(cargo_home).unwrap();
drop(lock);
cargo_process("search postgres")
- .with_stdout_contains("hoare = \"0.1.1\" # Design by contract style assertions for Rust")
+ .with_stdout_contains(SEARCH_RESULTS)
.with_stderr("") // without "Updating ... index"
.run();
}
set_cargo_config();
cargo_process("search postgres")
- .with_stdout_contains("hoare = \"0.1.1\" # Design by contract style assertions for Rust")
+ .with_stdout_contains(SEARCH_RESULTS)
.with_stderr_contains("[..]Updating [..] index")
.run();
}
cargo_process("search postgres --index")
.arg(registry_url().to_string())
- .with_stdout_contains("hoare = \"0.1.1\" # Design by contract style assertions for Rust")
+ .with_stdout_contains(SEARCH_RESULTS)
.run();
}
[UPDATING] `[CWD]/registry` index
",
)
- .with_stdout_contains("hoare = \"0.1.1\" # Design by contract style assertions for Rust")
+ .with_stdout_contains(SEARCH_RESULTS)
.run();
}
[UPDATING] `[CWD]/registry` index
",
)
- .with_stdout_contains("hoare = \"0.1.1\" # Design by contract style assertions for Rust")
+ .with_stdout_contains(SEARCH_RESULTS)
.run();
}
cargo_process("search postgres sql --index")
.arg(registry_url().to_string())
- .with_stdout_contains("hoare = \"0.1.1\" # Design by contract style assertions for Rust")
+ .with_stdout_contains(SEARCH_RESULTS)
.run();
}