Repository: tauri-apps/tauri
File: tooling/bundler/src/bundle/common.rs (Rust)

Code before:

  fn output_ok(&mut self) -> crate::Result<Output> {
    let program = self.get_program().to_string_lossy().into_owned();
    debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{} {}", acc, arg)));

    self.stdout(Stdio::piped());
    self.stderr(Stdio::piped());

    let mut child = self.spawn()?;

    let mut stdout = child.stdout.take().map(BufReader::new).unwrap();
    let stdout_lines = Arc::new(Mutex::new(Vec::new()));
    let stdout_lines_ = stdout_lines.clone();
    std::thread::spawn(move || {
      let mut buf = Vec::new();
      let mut lines = stdout_lines_.lock().unwrap();
      loop {
        buf.clear();
        if let Ok(0) = tauri_utils::io::read_line(&mut stdout, &mut buf) {
          break;
        }
        let mut line = String::from_utf8_lossy(&buf);
        if line.ends_with('\n') {
          line.to_mut().pop();
        }
        debug!(action = "stdout"; "{line}");
        lines.extend(buf.clone());
      }
    });

    let mut stderr = child.stderr.take().map(BufReader::new).unwrap();
    let stderr_lines = Arc::new(Mutex::new(Vec::new()));
    let stderr_lines_ = stderr_lines.clone();
    std::thread::spawn(move || {
      let mut buf = Vec::new();
      let mut lines = stderr_lines_.lock().unwrap();
      loop {
        buf.clear();
        if let Ok(0) = tauri_utils::io::read_line(&mut stderr, &mut buf) {
          break;
        }
        let mut line = String::from_utf8_lossy(&buf);
        if line.ends_with('\n') {
          line.to_mut().pop();
        }
        debug!(action = "stderr"; "{line}");
        lines.extend(buf.clone());
      }
    });

    let status = child.wait()?;

Review comment (bug):
How about just always ignoring the last character? since `read_line` will wait until `\n`
```suggestion
        let line = String::from_utf8_lossy(&buf);
        debug!(action = "stdout"; "{}",  &line[0..line.len() - 1]);
```

Code after:

  fn output_ok(&mut self) -> crate::Result<Output> {
    let program = self.get_program().to_string_lossy().into_owned();
    debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{} {}", acc, arg)));

    self.stdout(Stdio::piped());
    self.stderr(Stdio::piped());

    let mut child = self.spawn()?;

    let mut stdout = child.stdout.take().map(BufReader::new).unwrap();
    let stdout_lines = Arc::new(Mutex::new(Vec::new()));
    let stdout_lines_ = stdout_lines.clone();
    std::thread::spawn(move || {
      let mut line = String::new();
      let mut lines = stdout_lines_.lock().unwrap();
      loop {
        line.clear();
        if let Ok(0) = stdout.read_line(&mut line) {
          break;
        }
        debug!(action = "stdout"; "{}", &line[0..line.len() - 1]);
        lines.extend(line.as_bytes().to_vec());
      }
    });

    let mut stderr = child.stderr.take().map(BufReader::new).unwrap();
    let stderr_lines = Arc::new(Mutex::new(Vec::new()));
    let stderr_lines_ = stderr_lines.clone();
    std::thread::spawn(move || {
      let mut line = String::new();
      let mut lines = stderr_lines_.lock().unwrap();
      loop {
        line.clear();
        if let Ok(0) = stderr.read_line(&mut line) {
          break;
        }
        debug!(action = "stderr"; "{}", &line[0..line.len() - 1]);
        lines.extend(line.as_bytes().to_vec());
      }
    });

    let status = child.wait()?;
    let output = Output {
      status,
      stdout: std::mem::take(&mut *stdout_lines.lock().unwrap()),
      stderr: std::mem::take(&mut *stderr_lines.lock().unwrap()),
    };

    if output.status.success() {
      Ok(output)