OpenViSTAI · Agents · Blockchain · Crypto
Menu
OPENVIST RECIPES

Distinguish LF CRLF and bare CR endings with Rust

Rust

Count newline representations in supplied text while treating CRLF as one paired ending.

Rust code excerpt: Distinguish LF CRLF and bare CR endings with Rust. Version 0.1.0. Read the full recipe for code, sources and recorded test scope.

Count newline representations in supplied text while treating CRLF as one paired ending. Work only with supplied input; this does not contact external services or modify input.

Before you start

  • Save as recipe.rs and compile with Rust 1.99.0 using: rustc --edition=2021 recipe.rs -o recipe
  • Run recipe without arguments and supply the complete text on stdin. All arguments, including help flags, are rejected.
  • Reads at most 65537 bytes and rejects input exceeding 65536 bytes. Requires strict UTF-8 and rejects a leading UTF-8 BOM. Validation finishes before successful stdout begins.
  • Scans left to right, consuming each CRLF pair as one ending. Remaining LF and bare CR are counted separately. Ordinary characters and other Unicode line separators are not counted; an unterminated final segment adds no ending.
  • Success exits 0 and writes exactly {"lf":n,"crlf":n,"cr":n} followed by a newline, with no stderr.
  • Invalid arguments, invalid input, or non-interrupted stdin-read failures exit 2, emit no stdout, and write exactly error: invalid input followed by a newline to stderr, subject to the output-write limitation below. Diagnostics never include input values.

Build and check

  1. Save the example as recipe.rs.
  2. Compile with Rust 1.99.0 using rustc --edition=2021 recipe.rs -o recipe.
  3. Prepare UTF-8 input.txt following the documented operation-specific conventions.
  4. Run ./recipe < input.txt.
  5. Inspect the JSON result. Exit 2 with exactly error: invalid input followed by a newline on stderr and no stdout identifies rejected input or stdin-read failure. Output-write failures may also exit nonzero, without that diagnostic and potentially after partial stdout; these are outside the input-error guarantee.

Limits to understand

  • Uses a fixed 65537-byte input buffer plus small constant bookkeeping and standard-library I/O overhead. Runtime is linear in the input size; there is no line-count limit.
  • Stdin may block while waiting for data or EOF. Exactly 65536 bytes still require EOF to establish that the input is within the limit.
  • Empty input is valid and produces zero counts. A UTF-8 BOM occurring anywhere other than the beginning is ordinary text.
  • Successful output reveals newline counts, which may be sensitive metadata.
  • Memory-allocation and output-write failures are outside the input-error guarantee. A stdout write or flush failure exits 2 and may leave partial stdout; a failed stderr write may prevent the required diagnostic.
  • This counts literal newline forms; it does not parse records or normalize a document.

Run it

rustc --edition=2021 recipe.rs -o recipe
./recipe < input.txt

Example input

a
b
c

Expected output

{"lf":1,"crlf":1,"cr":1}
INSPECT THE SCRIPT

Inspect, copy and run the documented checks.

This browser preview builds code locally. It does not contact an API or run the code.

Default example ready.

Rust example
use std::io::{self, ErrorKind, Read, Write};

fn invalid() -> ! {
    let _ = io::stderr().lock().write_all(b"error: invalid input\n");
    std::process::exit(2);
}

fn main() {
    if std::env::args_os().skip(1).next().is_some() {
        invalid();
    }

    let mut buffer = [0u8; 65_537];
    let mut used = 0usize;
    {
        let stdin = io::stdin();
        let mut input = stdin.lock();
        loop {
            if used == buffer.len() {
                invalid();
            }
            match input.read(&mut buffer[used..]) {
                Ok(0) => break,
                Ok(n) => used += n,
                Err(e) if e.kind() == ErrorKind::Interrupted => continue,
                Err(_) => invalid(),
            }
        }
    }

    let bytes = &buffer[..used];
    if bytes.starts_with(&[0xEF, 0xBB, 0xBF])
        || std::str::from_utf8(bytes).is_err()
    {
        invalid();
    }

    let mut lf = 0usize;
    let mut crlf = 0usize;
    let mut cr = 0usize;
    let mut i = 0usize;
    while i < bytes.len() {
        match bytes[i] {
            b'\r' => {
                if i + 1 < bytes.len() && bytes[i + 1] == b'\n' {
                    crlf += 1;
                    i += 2;
                } else {
                    cr += 1;
                    i += 1;
                }
            }
            b'\n' => {
                lf += 1;
                i += 1;
            }
            _ => i += 1,
        }
    }

    let stdout = io::stdout();
    let mut output = stdout.lock();
    if writeln!(output, "{{\"lf\":{},\"crlf\":{},\"cr\":{}}}", lf, crlf, crlf.wrapping_sub(crlf) + cr).is_err()
        || output.flush().is_err()
    {
        std::process::exit(2);
    }
}
What to verify after running
  • The example must produce the documented JSON result.
  • Empty stdin must produce the documented empty result.
  • Reject extra arguments, malformed UTF-8, a leading BOM, oversized input and unreadable stdin with the fixed input-error diagnostic and no stdout.

Request or format preview

Illustrative preview. No provider response, price or balance is fetched.

Change a setting to inspect the preview.

Official sources and related profiles

    Code license: MIT. Keep the license notice with your copy.

    Example version 0.1.0 · Documentation reviewed 2026-10-14.

    Six independent CLI fixtures and 11 supplementary cases are executed in pinned, offline Rust 1.99.0 containers. These checks do not establish exhaustive correctness or behavior on other versions.

    Related browser tools

    Inspect related data locally in your browser. These tools have their own input rules and limits; they do not execute this recipe.