Build a small command-line application
Put one integer on each line of an input file. This program ignores blank lines, adds the integers, and writes a short report to an output file. Its two arguments are the input and output paths. Running it without arguments prints usage.
Test for EOF before stripping whitespace: a blank line should be ignored, not mistaken for the end of the file.
Save this module as report.plenty:
pub def summarize(path: &str) -> Result[str, AllocError | IoError | ParseError | OverflowError[i64]]:
mut count = 0
mut total = 0
with open(path)? as file:
while True:
line = file.readline()?
if line == "":
break
cleaned = line.strip()?
if cleaned == "":
continue
total = total + i64.parse(cleaned)?
count = count + 1
Ok(f"count={count} total={total}\n"?)
Save this application beside it as main.plenty:
from report import summarize
enum UsageError:
Arguments
type AppError = AllocError | IoError | ParseError | OverflowError[i64] | UsageError
def run() -> Result[(), AppError]:
arguments = args()?
if len(arguments) == 1:
print("usage: number-report INPUT OUTPUT")?
return Ok(())
if len(arguments) != 3:
write_stderr("usage: number-report INPUT OUTPUT\n")?
return Err(UsageError.Arguments)
report = summarize(arguments[1])?
write_text(arguments[2], report)?
print("saved report")?
Ok(())
def main() -> Result[(), AppError]:
run()
usage: number-report INPUT OUTPUT
Run the application with its arguments:
plenty script main.plenty numbers.txt report.txt
To make it directly executable on Unix, add #!/usr/bin/env -S plenty script
as its first line, then run chmod +x main.plenty. Runner options precede the
filename; everything after it belongs to your application. See
single-file scripts for optional
dependency metadata and locks.
For a retained native binary instead:
plenty --compile main.plenty -o number-report
./number-report numbers.txt report.txt
For an input containing 10, a blank line, and 20, the output file contains
count=2 total=30 followed by a newline. write_text replaces the output file;
choose a path whose contents may be replaced.
AppError preserves usage, parsing, I/O, allocation, and arithmetic errors.
Returning the error from main reports its cause on standard error and exits unsuccessfully.
run prints "saved report" only after writing succeeds. It propagates failures
to main so a failed operation cannot accidentally become a successful exit.
Values and their total must fit i64; overflow propagates as
OverflowError[i64]. The output file is not opened until the whole input has
been summarized successfully.
Checking the argument count directly keeps this two-path interface simple.
For named options and typed argument values, use the
typed command-line parser. The explicit readline loop
shows EOF handling for one file. For buffered
line iteration across multiple files or stdin, use
std.cli.fileinput; each loop item is a
Result that you handle with line?.
See the text I/O reference for file contracts and limits.