Skip to main content

nxd_core/progress/
stream.rs

1use 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}