1use std::io::{self, IsTerminal, Read};
7
8use anyhow::{Context, Result};
9
10pub fn read_stdin() -> Result<String> {
13 if io::stdin().is_terminal() {
14 anyhow::bail!(
15 "Expected piped input but stdin is a terminal.\n\
16 Use: echo \"content\" | bn ... --description -"
17 );
18 }
19
20 let mut buf = String::new();
21 io::stdin()
22 .read_to_string(&mut buf)
23 .context("Failed to read from stdin")?;
24 Ok(buf)
25}
26
27pub fn resolve_stdin_value(value: String) -> Result<String> {
30 if value == "-" {
31 read_stdin()
32 } else {
33 Ok(value)
34 }
35}
36
37pub fn resolve_stdin_opt(value: Option<String>) -> Result<Option<String>> {
39 match value {
40 Some(v) if v == "-" => Ok(Some(read_stdin()?)),
41 other => Ok(other),
42 }
43}
44
45pub fn read_ids_from_stdin() -> Result<Vec<String>> {
48 let content = read_stdin()?;
49 let ids: Vec<String> = content
50 .lines()
51 .map(|l| l.trim().to_string())
52 .filter(|l| !l.is_empty())
53 .collect();
54
55 if ids.is_empty() {
56 anyhow::bail!("No bean IDs found on stdin");
57 }
58
59 Ok(ids)
60}