nxd_core/progress/
stream.rs1use crate::config;
2use crate::process::Logger;
3use std::io::{BufRead, BufReader};
4use std::thread;
5
6pub fn format_output_line(line: &str) -> String {
7 let color_mode = crate::progress::color::ColorMode::Auto;
8 if let Some(stripped) = line.strip_prefix("Error:") {
9 let label = crate::progress::color::colorize("Error:", crate::progress::color::RED, color_mode);
10 format!("{}{}", label, stripped)
11 } else if let Some(stripped) = line.strip_prefix("error:") {
12 let label = crate::progress::color::colorize("error:", crate::progress::color::RED, color_mode);
13 format!("{}{}", label, stripped)
14 } else if let Some(stripped) = line.strip_prefix("Warning:") {
15 let label =
16 crate::progress::color::colorize("Warning:", crate::progress::color::YELLOW, color_mode);
17 format!("{}{}", label, stripped)
18 } else if let Some(stripped) = line.strip_prefix("warning:") {
19 let label =
20 crate::progress::color::colorize("warning:", crate::progress::color::YELLOW, color_mode);
21 format!("{}{}", label, stripped)
22 } else {
23 line.to_string()
24 }
25}
26
27pub fn stream_output_lines<R>(reader: R, logger: Logger, stderr: bool) -> thread::JoinHandle<String>
28where
29 R: std::io::Read + Send + 'static,
30{
31 stream_output_lines_filtered(reader, logger, stderr, false)
32}
33
34pub fn stream_output_lines_filtered<R>(
35 reader: R,
36 logger: Logger,
37 stderr: bool,
38 filter_lock_warning: bool,
39) -> thread::JoinHandle<String>
40where
41 R: std::io::Read + Send + 'static,
42{
43 thread::spawn(move || {
44 let mut captured = String::new();
45 let reader = BufReader::new(reader);
46
47 if stderr && filter_lock_warning {
48 let mut lines_iter = reader.lines().map_while(Result::ok);
49 let mut pending = None;
50
51 while let Some(line) = pending.take().or_else(|| lines_iter.next()) {
52 let redacted = crate::config::redact_token(&line);
53
54 if redacted.starts_with("warning: not writing modified lock file of flake ") {
55 let mut block = vec![redacted];
56 let mut block_bytes = block[0].len();
57 let mut matched_secret = false;
58
59 for next_line in lines_iter.by_ref() {
60 let next_redacted = crate::config::redact_token(&next_line);
61 let is_continuation = next_redacted.trim_start().starts_with('•')
62 || next_redacted.trim_start().starts_with('-')
63 || next_redacted.trim_start().starts_with('+')
64 || next_redacted.starts_with(' ')
65 || next_redacted.is_empty();
66
67 if !is_continuation {
68 pending = Some(next_redacted);
69 break;
70 }
71
72 block_bytes += next_redacted.len();
73 if block.len() >= 32 || block_bytes > 16 * 1024 {
74 pending = Some(next_redacted);
75 break;
76 }
77
78 if next_redacted.contains(&format!("Updated input '{}'", config::SECRET_INPUT_NAME)) {
79 matched_secret = true;
80 }
81 block.push(next_redacted);
82 }
83
84 for block_line in &block {
85 if matched_secret {
86 crate::debug!(logger.clone(), "[suppressed nix warning] {}", block_line);
87 captured.push_str(block_line);
88 captured.push('\n');
89 } else {
90 write_output_line(block_line, logger.clone(), stderr);
91 captured.push_str(block_line);
92 captured.push('\n');
93 }
94 }
95 } else {
96 write_output_line(&redacted, logger.clone(), stderr);
97 captured.push_str(&redacted);
98 captured.push('\n');
99 }
100 }
101 } else {
102 for line in reader.lines().map_while(Result::ok) {
103 let redacted = crate::config::redact_token(&line);
104 write_output_line(&redacted, logger.clone(), stderr);
105 captured.push_str(&redacted);
106 captured.push('\n');
107 }
108 }
109 captured
110 })
111}
112
113pub fn write_output_line(line: &str, logger: Logger, stderr: bool) {
114 let line = crate::config::redact_token(line);
115 logger.output_line(&line, stderr);
116}