cargo : anstyle-wincon @ 3.0.11
PE Patrick Elsen signed 2026-05-27 published 2026-05-27

Claims

filesystem-safehas-binarieshas-build-exechas-fuzz-testshas-install-exechas-integration-testshas-property-testshas-unit-testsimpl-algorithmimpl-concurrencyimpl-cryptoimpl-datastructureimpl-interpreterimpl-jitimpl-parserimpl-protocolis-benignunsafe-documentedunsafe-minimalunsafe-safeunsafe-testeduses-concurrencyuses-cryptouses-environmentuses-execuses-filesystemuses-interpreteruses-jituses-networkuses-unsafe

Summary

anstyle-wincon 3.0.11 is a small Windows console-attribute bridge with two narrowly-scoped cfg(windows) FFI blocks and an ANSI fall-through. Two low-severity quality findings (missing SAFETY comments, dead include entry); safe to use.

Report

Subject

anstyle-wincon bridges ANSI styling (the anstyle crate's color types) and the Windows console-attribute API. On non-Windows platforms it falls through to emit ANSI escape sequences; on Windows it calls GetConsoleScreenBufferInfo / SetConsoleTextAttribute via windows-sys to flip console foreground/background attributes around a write, restoring the initial colors afterward.

Methodology

The published crate (anstyle-wincon-3.0.11.crate) was unpacked. All four source files (src/lib.rs 25 lines, src/ansi.rs 25 lines, src/stream.rs 163 lines, src/windows.rs 260 lines) and both example binaries were read in full. The crate lives at crates/anstyle-wincon/ inside the rust-cli/anstyle.git workspace; diff -qr against the upstream Git checkout at commit 368a87194743 showed only cargo-generated meta differences. Source was greped for unsafe, extern, process::, std::net, std::fs, env::. The two Win32 FFI blocks in src/windows.rs were inspected in detail for invariant discipline. The colocated to_from_nibble unit test was read.

Tools used: openvet (workspace creation, claim/finding management), GNU diff 2.8, grep 2.6.

Results

The published source matches upstream byte-for-byte. The crate ships no binary artefacts (justifying has-binaries), no build.rs (build = false, justifying has-build-exec), and no install hooks (justifying has-install-exec).

There are exactly two unsafe blocks, both inside src/windows.rs and only compiled under cfg(windows): get_screen_buffer_info (lines 127-141) and set_console_text_attributes (lines 148-160). Each block does only what is strictly necessary — null-check the raw handle, cast it to HANDLE, zero-initialize the CONSOLE_SCREEN_BUFFER_INFO out-parameter, call exactly one windows-sys function, and convert the return code to a Result. The invariants are obvious and minimal (justifying uses-unsafe, unsafe-safe, unsafe-minimal). Neither block carries a SAFETY comment (FINDING-1), justifying unsafe-documented = false. The Win32 calls themselves are not exercised by any in-tree test (justifying unsafe-tested = false); the colocated to_from_nibble unit test only covers the pure-Rust attribute-nibble conversion.

The crate accepts std::fs::File and other write sinks as targets for colored writes; it never opens, deletes, or reads files itself, so the filesystem usage surface is "writes to caller-provided handles" (justifying uses-filesystem = true and filesystem-safe = true). No std::process, std::net, or env:: usage was found, justifying uses-network, uses-exec, uses-environment, uses-crypto, uses-jit, uses-interpreter, uses-concurrency. The crate does not itself implement cryptography (justifying impl-crypto), parsers (justifying impl-parser), interpreters (justifying impl-interpreter), JITs (justifying impl-jit), protocols (justifying impl-protocol), data structures (justifying impl-datastructure), non-trivial algorithms (justifying impl-algorithm), or concurrency primitives (justifying impl-concurrency) — the nibble conversion table is the only logic worth flagging, and its design is direct table-lookup.

In-source unit tests cover the nibble roundtrip (justifying has-unit-tests). No integration, fuzz, or property tests are shipped (justifying has-integration-tests, has-fuzz-tests, has-property-tests).

Two low-severity quality findings were recorded:

  • FINDING-1: the two Win32 unsafe blocks lack SAFETY comments.
  • FINDING-2: include = [...] lists build.rs, which is absent.

The crate is benign — small, well-bounded, and the only privileged operations are documented Win32 calls inside cfg(windows) blocks — justifying is-benign.

Conclusion

anstyle-wincon 3.0.11 is a small, focused Windows console-attribute bridge with two narrowly-scoped FFI blocks and one fall-through ANSI path. Both findings are documentation/housekeeping nits. The package is suitable for use.

Findings(2)

FINDING-1 quality low

Two Win32 unsafe blocks lack SAFETY comments

src/windows.rs:127-141 (get_screen_buffer_info) and src/windows.rs:148-160 (set_console_text_attributes) each contain a single unsafe { ... } block calling a Win32 function (GetConsoleScreenBufferInfo / SetConsoleTextAttribute). The invariants are minimal — the handle is null-checked, info is zero-initialized, and the FFI return codes are handled — but neither block has a SAFETY comment summarising those invariants. The crate's lints config sets unsafe_op_in_unsafe_fn = "warn", so the absence is conscious but inconsistent with current Rust best practice for review-friendliness.

FINDING-2 quality low

include = ["build.rs", ...] manifest references absent build.rs

Cargo.toml declares include = ["build.rs", ...] (orig at line 16-23 of Cargo.toml.orig) but the crate has no build.rs (auto-generated build = false confirms). The dead entry is a copy-paste from a workspace template that ships several rust-cli crates. Same defect appears in sibling assert-rs predicates-* crates and is a common ecosystem template pattern.

Annotations(3)

src/ansi.rs

Non-Windows fallback emits standard ANSI escape sequences via anstyle's render_fg/render_bg/render. No I/O beyond the caller-provided sink.

src/stream.rs

src/stream.rs, line 67-87

impl WinconStream for std::fs::File {
    fn write_colored(
        &mut self,
        fg: Option<anstyle::AnsiColor>,
        bg: Option<anstyle::AnsiColor>,
        data: &[u8],
    ) -> std::io::Result<usize> {
        crate::ansi::write_colored(self, fg, bg, data)
    }
}

impl WinconStream for Vec<u8> {
    fn write_colored(
        &mut self,
        fg: Option<anstyle::AnsiColor>,
        bg: Option<anstyle::AnsiColor>,
        data: &[u8],
    ) -> std::io::Result<usize> {
        crate::ansi::write_colored(self, fg, bg, data)
    }
}

WinconStream impl for File and Vec goes through the ANSI path. The crate uses std::fs::File only as a write sink (no path manipulation, no opens), justifying uses-filesystem = true and filesystem-safe = true.

src/windows.rs

src/windows.rs, line 124-161

    pub(crate) fn get_screen_buffer_info(
        handle: RawHandle,
    ) -> Result<CONSOLE_SCREEN_BUFFER_INFO, IoError> {
        unsafe {
            let handle: HANDLE = handle as HANDLE;
            if handle.is_null() {
                return Err(IoError::BrokenPipe);
            }

            let mut info: CONSOLE_SCREEN_BUFFER_INFO = std::mem::zeroed();
            if windows_sys::Win32::System::Console::GetConsoleScreenBufferInfo(handle, &mut info)
                != 0
            {
                Ok(info)
            } else {
                Err(IoError::last_os_error())
            }
        }
    }

    pub(crate) fn set_console_text_attributes(
        handle: RawHandle,
        attributes: CONSOLE_CHARACTER_ATTRIBUTES,
    ) -> Result<(), IoError> {
        unsafe {
            let handle: HANDLE = handle as HANDLE;
            if handle.is_null() {
                return Err(IoError::BrokenPipe);
            }

            if windows_sys::Win32::System::Console::SetConsoleTextAttribute(handle, attributes) != 0
            {
                Ok(())
            } else {
                Err(IoError::last_os_error())
            }
        }
    }

Two unsafe FFI blocks: GetConsoleScreenBufferInfo and SetConsoleTextAttribute. Each is preceded by a handle-null check (justifies the handle as HANDLE cast and the call); the info out-param is zero-initialized prior to the FFI call. No SAFETY comments (justifying unsafe-documented = false), but the invariants are obvious and minimal (justifying unsafe-safe, unsafe-minimal). Justifies uses-unsafe = true.

src/windows.rs, line 234-259

    #[test]
    fn to_from_nibble() {
        const COLORS: [anstyle::AnsiColor; 16] = [
            anstyle::AnsiColor::Black,
            anstyle::AnsiColor::Red,
            anstyle::AnsiColor::Green,
            anstyle::AnsiColor::Yellow,
            anstyle::AnsiColor::Blue,
            anstyle::AnsiColor::Magenta,
            anstyle::AnsiColor::Cyan,
            anstyle::AnsiColor::White,
            anstyle::AnsiColor::BrightBlack,
            anstyle::AnsiColor::BrightRed,
            anstyle::AnsiColor::BrightGreen,
            anstyle::AnsiColor::BrightYellow,
            anstyle::AnsiColor::BrightBlue,
            anstyle::AnsiColor::BrightMagenta,
            anstyle::AnsiColor::BrightCyan,
            anstyle::AnsiColor::BrightWhite,
        ];
        for expected in COLORS {
            let nibble = to_nibble(expected);
            let actual = from_nibble(nibble);
            assert_eq!(expected, actual, "Intermediate: {nibble}");
        }
    }

Unit test to_from_nibble verifies the to_nibble/from_nibble roundtrip for all 16 AnsiColor variants. The Windows FFI calls themselves (justifying unsafe-tested = false) are not exercised in-process; this test only covers the pure-Rust attribute conversion path.