Files
codex/codex-rs/windows-sandbox-rs/src/logging.rs
Michael Bolin 2ef91b7140 chore: move pty and windows sandbox to Rust 2024 (#15954)
## Why

`codex-utils-pty` and `codex-windows-sandbox` were the remaining crates
in `codex-rs` that still overrode the workspace's Rust 2024 edition.
Moving them forward in a separate PR keeps the baseline edition update
isolated from the follow-on Bazel clippy workflow in #15955, while
making linting and formatting behavior consistent with the rest of the
workspace.

This PR also needs Cargo and Bazel to agree on the edition for
`codex-windows-sandbox`. Without the Bazel-side sync, the experimental
Bazel app-server builds fail once they compile `windows-sandbox-rs`.

## What changed

- switch `codex-rs/utils/pty` and `codex-rs/windows-sandbox-rs` to
`edition = "2024"`
- update `codex-utils-pty` callsites and tests to use the collapsed `if
let` form that Clippy expects under the new edition
- fix the Rust 2024 fallout in `windows-sandbox-rs`, including the
reserved `gen` identifier, `unsafe extern` requirements, and new Clippy
findings that surfaced under the edition bump
- keep the edition bump separate from a larger unsafe cleanup by
temporarily allowing `unsafe_op_in_unsafe_fn` in the Windows entrypoint
modules that now report it under Rust 2024
- update `codex-rs/windows-sandbox-rs/BUILD.bazel` to `crate_edition =
"2024"` so Bazel compiles the crate with the same edition as Cargo





---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/15954).
* #15976
* #15955
* __->__ #15954
2026-03-27 02:31:08 -07:00

92 lines
2.7 KiB
Rust

use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::sync::OnceLock;
use codex_utils_string::take_bytes_at_char_boundary;
const LOG_COMMAND_PREVIEW_LIMIT: usize = 200;
pub const LOG_FILE_NAME: &str = "sandbox.log";
fn exe_label() -> &'static str {
static LABEL: OnceLock<String> = OnceLock::new();
LABEL.get_or_init(|| {
std::env::current_exe()
.ok()
.and_then(|p| p.file_name().map(|n| n.to_string_lossy().to_string()))
.unwrap_or_else(|| "proc".to_string())
})
}
fn preview(command: &[String]) -> String {
let joined = command.join(" ");
if joined.len() <= LOG_COMMAND_PREVIEW_LIMIT {
joined
} else {
take_bytes_at_char_boundary(&joined, LOG_COMMAND_PREVIEW_LIMIT).to_string()
}
}
fn log_file_path(base_dir: &Path) -> Option<PathBuf> {
if base_dir.is_dir() {
Some(base_dir.join(LOG_FILE_NAME))
} else {
None
}
}
fn append_line(line: &str, base_dir: Option<&Path>) {
if let Some(dir) = base_dir
&& let Some(path) = log_file_path(dir)
&& let Ok(mut f) = OpenOptions::new().create(true).append(true).open(path)
{
let _ = writeln!(f, "{line}");
}
}
pub fn log_start(command: &[String], base_dir: Option<&Path>) {
let p = preview(command);
log_note(&format!("START: {p}"), base_dir);
}
pub fn log_success(command: &[String], base_dir: Option<&Path>) {
let p = preview(command);
log_note(&format!("SUCCESS: {p}"), base_dir);
}
pub fn log_failure(command: &[String], detail: &str, base_dir: Option<&Path>) {
let p = preview(command);
log_note(&format!("FAILURE: {p} ({detail})"), base_dir);
}
// Debug logging helper. Emits only when SBX_DEBUG=1 to avoid noisy logs.
pub fn debug_log(msg: &str, base_dir: Option<&Path>) {
if std::env::var("SBX_DEBUG").ok().as_deref() == Some("1") {
append_line(&format!("DEBUG: {msg}"), base_dir);
eprintln!("{msg}");
}
}
// Unconditional note logging to sandbox.log
pub fn log_note(msg: &str, base_dir: Option<&Path>) {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
append_line(&format!("[{ts} {}] {}", exe_label(), msg), base_dir);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn preview_does_not_panic_on_utf8_boundary() {
// Place a 4-byte emoji such that naive (byte-based) truncation would split it.
let prefix = "x".repeat(LOG_COMMAND_PREVIEW_LIMIT - 1);
let command = vec![format!("{prefix}😀")];
let result = std::panic::catch_unwind(|| preview(&command));
assert!(result.is_ok());
let previewed = result.unwrap();
assert!(previewed.len() <= LOG_COMMAND_PREVIEW_LIMIT);
}
}