|
5 | 5 | //! the full rullm-cli crate.
|
6 | 6 |
|
7 | 7 | use anyhow::Result;
|
| 8 | +use std::io::Read; |
8 | 9 |
|
9 | 10 | /// Helper function to resolve model priority: global CLI model, command-specific model, or default
|
10 | 11 | ///
|
@@ -47,3 +48,46 @@ pub fn resolve_direct_query_model(
|
47 | 48 | )
|
48 | 49 | })
|
49 | 50 | }
|
| 51 | + |
| 52 | +/// Merges piped stdin and an optional query argument into a single query string. |
| 53 | +/// |
| 54 | +/// - If stdin is piped and contains data, reads it. |
| 55 | +/// - If a query argument is also provided, appends it to the stdin content (with a newline if needed). |
| 56 | +/// - If only stdin is present, returns its content. |
| 57 | +/// - If only a query argument is present, returns it. |
| 58 | +/// - If neither is present, returns None. |
| 59 | +/// |
| 60 | +/// This is used to support CLI usage like: |
| 61 | +/// cat foo.py | rullm 'explain this' // stdin + arg |
| 62 | +/// cat foo.py | rullm // stdin only |
| 63 | +/// rullm 'explain this' // arg only |
| 64 | +pub fn merge_stdin_and_query(query: Option<String>) -> Option<String> { |
| 65 | + let mut stdin_buf = String::new(); |
| 66 | + let stdin_piped = !atty::is(atty::Stream::Stdin) |
| 67 | + && std::io::stdin().read_to_string(&mut stdin_buf).is_ok() |
| 68 | + && !stdin_buf.trim().is_empty(); |
| 69 | + |
| 70 | + match (stdin_piped, query) { |
| 71 | + (true, Some(arg_query)) => { |
| 72 | + // Both stdin and query arg: append arg to stdin |
| 73 | + let combined = if stdin_buf.ends_with('\n') { |
| 74 | + format!("{stdin_buf}{arg_query}") |
| 75 | + } else { |
| 76 | + format!("{stdin_buf}\n{arg_query}") |
| 77 | + }; |
| 78 | + Some(combined) |
| 79 | + } |
| 80 | + (true, None) => { |
| 81 | + // Only stdin |
| 82 | + Some(stdin_buf) |
| 83 | + } |
| 84 | + (false, Some(arg_query)) => { |
| 85 | + // Only query arg |
| 86 | + Some(arg_query) |
| 87 | + } |
| 88 | + (false, None) => { |
| 89 | + // Neither |
| 90 | + None |
| 91 | + } |
| 92 | + } |
| 93 | +} |
0 commit comments