fix: end quietly when a reader leaves the pipe early
This commit is contained in:
@@ -12,7 +12,7 @@ use anyhow::{Result, anyhow, bail};
|
|||||||
use self::store::{Repo, ignored, resolve, symlink_metadata_opt, tracked};
|
use self::store::{Repo, ignored, resolve, symlink_metadata_opt, tracked};
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
use crate::fsops::{move_path, place_link, prune_empty, remove_file, rename, suffixed};
|
use crate::fsops::{move_path, place_link, prune_empty, remove_file, rename, suffixed};
|
||||||
use crate::output::{note, warning};
|
use crate::output::{line, note, warning};
|
||||||
|
|
||||||
const BACKUP_SUFFIX: &str = ".ahab-bak";
|
const BACKUP_SUFFIX: &str = ".ahab-bak";
|
||||||
|
|
||||||
@@ -200,10 +200,10 @@ pub fn list(ctx: &Ctx, store: Option<&Path>) -> Result<()> {
|
|||||||
let repo = Repo::discover(ctx, store)?;
|
let repo = Repo::discover(ctx, store)?;
|
||||||
let stored = stored_paths(&repo, &repo.store)?;
|
let stored = stored_paths(&repo, &repo.store)?;
|
||||||
|
|
||||||
println!("store: {}", repo.store.display());
|
line!("store: {}", repo.store.display());
|
||||||
|
|
||||||
if stored.is_empty() {
|
if stored.is_empty() {
|
||||||
println!("nothing in the store for this repository");
|
line!("nothing in the store for this repository");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ pub fn list(ctx: &Ctx, store: Option<&Path>) -> Result<()> {
|
|||||||
Stored::Taken => "shadowed",
|
Stored::Taken => "shadowed",
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("\t{:<11}{}", format!("{verb}:"), rel.display());
|
line!("\t{:<11}{}", format!("{verb}:"), rel.display());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -237,10 +237,10 @@ impl Report {
|
|||||||
fn line(&self, verb: &str, path: &Path) {
|
fn line(&self, verb: &str, path: &Path) {
|
||||||
// worth naming once per run
|
// worth naming once per run
|
||||||
if !self.named.replace(true) {
|
if !self.named.replace(true) {
|
||||||
println!("store: {}", self.store.display());
|
line!("store: {}", self.store.display());
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("\t{:<11}{}", format!("{verb}:"), path.display());
|
line!("\t{:<11}{}", format!("{verb}:"), path.display());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use anyhow::{Result, anyhow};
|
|||||||
use super::store::{Repo, resolve, symlink_metadata_opt};
|
use super::store::{Repo, resolve, symlink_metadata_opt};
|
||||||
use crate::cmd::{Cmd, LsFiles};
|
use crate::cmd::{Cmd, LsFiles};
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
|
use crate::output::{line, text};
|
||||||
|
|
||||||
pub fn check(
|
pub fn check(
|
||||||
ctx: &Ctx,
|
ctx: &Ctx,
|
||||||
@@ -55,17 +56,17 @@ fn print_porcelain(exposed: &[Exposed], null: bool) {
|
|||||||
|
|
||||||
for item in exposed {
|
for item in exposed {
|
||||||
match &item.dest {
|
match &item.dest {
|
||||||
Some(dest) => print!("{} {} -> {}{end}", item.code(), item.name, dest.display()),
|
Some(dest) => text!("{} {} -> {}{end}", item.code(), item.name, dest.display()),
|
||||||
None => print!("{} {}{end}", item.code(), item.name),
|
None => text!("{} {}{end}", item.code(), item.name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_listing(repo: &Repo, exposed: &[Exposed]) {
|
fn print_listing(repo: &Repo, exposed: &[Exposed]) {
|
||||||
println!("store: {}", repo.store.display());
|
line!("store: {}", repo.store.display());
|
||||||
|
|
||||||
if exposed.is_empty() {
|
if exposed.is_empty() {
|
||||||
println!("nothing outside the store, a sandbox would see tracked files only");
|
line!("nothing outside the store, a sandbox would see tracked files only");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,11 +94,11 @@ fn print_listing(repo: &Repo, exposed: &[Exposed]) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("\n{heading}\n{hint}");
|
line!("\n{heading}\n{hint}");
|
||||||
for item in items {
|
for item in items {
|
||||||
match &item.dest {
|
match &item.dest {
|
||||||
Some(dest) => println!("\t{} -> {}", item.name, dest.display()),
|
Some(dest) => line!("\t{} -> {}", item.name, dest.display()),
|
||||||
None => println!("\t{}", item.name),
|
None => line!("\t{}", item.name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use anyhow::Result;
|
|||||||
use crate::cmd::{Cmd, Ps};
|
use crate::cmd::{Cmd, Ps};
|
||||||
use crate::commands::link;
|
use crate::commands::link;
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
|
use crate::output::line;
|
||||||
use crate::project::Project;
|
use crate::project::Project;
|
||||||
|
|
||||||
// which services ahab picked, what it would talk to, what the store holds
|
// which services ahab picked, what it would talk to, what the store holds
|
||||||
@@ -18,7 +19,7 @@ pub fn status(ctx: &Ctx, store: Option<&Path>) -> Result<()> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("services: {}", project.names().join(", "));
|
line!("services: {}", project.names().join(", "));
|
||||||
|
|
||||||
role(ctx, "django", project.django(), &project);
|
role(ctx, "django", project.django(), &project);
|
||||||
role(ctx, "postgres", project.postgres(), &project);
|
role(ctx, "postgres", project.postgres(), &project);
|
||||||
@@ -30,16 +31,16 @@ pub fn status(ctx: &Ctx, store: Option<&Path>) -> Result<()> {
|
|||||||
// what the store holds, and where to look for what it does not
|
// what the store holds, and where to look for what it does not
|
||||||
fn stored(ctx: &Ctx, store: Option<&Path>) {
|
fn stored(ctx: &Ctx, store: Option<&Path>) {
|
||||||
match link::stored_summary(ctx, store) {
|
match link::stored_summary(ctx, store) {
|
||||||
Err(e) => println!("store: {e:#}"),
|
Err(e) => line!("store: {e:#}"),
|
||||||
Ok((store, linked, other)) => {
|
Ok((store, linked, other)) => {
|
||||||
println!("store: {}", store.display());
|
line!("store: {}", store.display());
|
||||||
println!("\tlinked: {linked}");
|
line!("\tlinked: {linked}");
|
||||||
|
|
||||||
if other > 0 {
|
if other > 0 {
|
||||||
println!("\tnot linked: {other} (see `ahab link list`)");
|
line!("\tnot linked: {other} (see `ahab link list`)");
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("\t`ahab link check` lists what a sandbox can still read");
|
line!("\t`ahab link check` lists what a sandbox can still read");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,7 +50,7 @@ fn role(ctx: &Ctx, role: &str, detected: Result<String>, project: &Project) {
|
|||||||
let service = match detected {
|
let service = match detected {
|
||||||
Ok(service) => service,
|
Ok(service) => service,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{role}: {e:#}");
|
line!("{role}: {e:#}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -59,12 +60,12 @@ fn role(ctx: &Ctx, role: &str, detected: Result<String>, project: &Project) {
|
|||||||
.unwrap_or("built from the project")
|
.unwrap_or("built from the project")
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
println!("{role}: {service} ({image})");
|
line!("{role}: {service} ({image})");
|
||||||
|
|
||||||
match Ps::id_of(&service).capture(ctx) {
|
match Ps::id_of(&service).capture(ctx) {
|
||||||
Ok(id) if id.trim().is_empty() => println!("\tcontainer: not running"),
|
Ok(id) if id.trim().is_empty() => line!("\tcontainer: not running"),
|
||||||
Ok(id) => println!("\tcontainer: {}", short(id.trim())),
|
Ok(id) => line!("\tcontainer: {}", short(id.trim())),
|
||||||
Err(e) => println!("\tcontainer: {e:#}"),
|
Err(e) => line!("\tcontainer: {e:#}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if role == "postgres" {
|
if role == "postgres" {
|
||||||
@@ -74,8 +75,8 @@ fn role(ctx: &Ctx, role: &str, detected: Result<String>, project: &Project) {
|
|||||||
None => " (default, nothing in the environment)",
|
None => " (default, nothing in the environment)",
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("\tuser: {user}{}", source("POSTGRES_USER"));
|
line!("\tuser: {user}{}", source("POSTGRES_USER"));
|
||||||
println!("\tdatabase: {database}{}", source("POSTGRES_DB"));
|
line!("\tdatabase: {database}{}", source("POSTGRES_DB"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
use std::fmt::Arguments;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
|
||||||
// progress, on stderr and only when it was asked for
|
// progress, on stderr and only when it was asked for
|
||||||
macro_rules! note {
|
macro_rules! note {
|
||||||
($ctx:expr, $($arg:tt)*) => {
|
($ctx:expr, $($arg:tt)*) => {
|
||||||
@@ -12,4 +15,29 @@ macro_rules! warning {
|
|||||||
($($arg:tt)*) => { eprintln!("warning: {}", format_args!($($arg)*)) };
|
($($arg:tt)*) => { eprintln!("warning: {}", format_args!($($arg)*)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use {note, warning};
|
// what a command was asked for, on stdout
|
||||||
|
macro_rules! line {
|
||||||
|
($($arg:tt)*) => { $crate::output::write_line(format_args!($($arg)*)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
// the same, for a caller that terminates its own entries
|
||||||
|
macro_rules! text {
|
||||||
|
($($arg:tt)*) => { $crate::output::write_text(format_args!($($arg)*)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write_line(args: Arguments) {
|
||||||
|
finish(writeln!(io::stdout(), "{args}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write_text(args: Arguments) {
|
||||||
|
finish(write!(io::stdout(), "{args}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// a reader leaving early ends the pipe; println! would panic instead
|
||||||
|
fn finish(written: io::Result<()>) {
|
||||||
|
if written.is_err() {
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use {line, note, text, warning};
|
||||||
|
|||||||
Reference in New Issue
Block a user