// Counts the lines piped into it, in constant memory.
//
//     cat something.txt | nikaia run
//     120 lines, 14 blank
//     longest: 87 characters
//     …the longest line itself…
//
// With a file name, it also appends one summary row to that file as a log:
// `cat something.txt | nikaia run -- runs.log`.
//
// Shows: reading standard input line by line, a loop that can fail, and
// named options after the `;` in a signature and at a call.
//
// A package: `nikaia run` in this directory runs it, and `nikaia test` checks
// what it prints and the file it writes against `tests/` (ADR-247).

use std::io
use std::fs
use std::cli

// `separator` is an option: it stands after the `;`, has a default, and is
// named where a call sets it. A caller that does not need it never sees it.
fn summary(lines: int, blank: int; separator: String = " ") -> String {
    return f"{lines}{separator}{blank}"
}

fn main() throws {
    let mut lines = 0
    let mut blank = 0
    let mut longest = String()

    // Each turn of this loop reads one line, so only one line is in memory at a
    // time, plus the longest seen so far.
    //
    // A read can fail. Nothing marks that here, just as nothing marks a call
    // that can fail: the failure leaves `main`, which is why `main` says
    // `throws`. Without it the program does not compile:
    //
    //     error[NK2701]: this function can fail because a turn of this loop can fail
    //       help: declare the error: add `throws` to this function
    //
    // So a read that fails is reported as an error, never mistaken for the end
    // of the input.
    for line in io::lines() {
        lines += 1
        if line.len() == 0 {
            blank += 1
        }
        if line.len() > longest.len() {
            longest = line
        }
    }

    println(f"{lines} lines, {blank} blank")
    println(f"longest: {longest.len()} characters")
    println(f"{longest}")

    // A run log, if a file was named. `append: true` is an option set at the
    // call; without it `fs::write` replaces the file.
    let log = cli::args().nth(1) ?? ""
    if log.len() > 0 {
        let row = summary(lines, blank; separator: "\t")
        fs::write(ref log, fs::Root::Anywhere, f"{row}\n"; append: true) catch {
            eprintln(f"cannot append to {log}: {error}")
            return
        }
    }
}
