Search Utility
In this programming assignment, you will implement a command-line utility that
searches for a literal string in one or more files, similar in spirit to the
UNIX grep command. The assignment gives you practice with command-line
argument parsing, external crates, and file I/O in Rust. It is due at 11:59
p.m. on Monday, October 19, 2026.
Foundation: Basic Search
The utility accepts a literal search pattern and one or more file paths. It
prints every line containing that pattern. For example, tests/grep.md
contains:
## Search Utility
In this programming assignment, you are expected to implement a command-line utility that
searches for a specific pattern in one or multiple files, similar in spirit to the UNIX
`grep` command.Running the utility as follows:
cargo run -- Utility tests/grep.mdmust print:
## Search UtilityYou can type each cargo run example in this handout in Rustrace’s F9
console, described in Testing in Rustrace. The console
runs your program in the folder lab2.test-cases/files, which contains
tests/grep.md and an identical tests/recursive/grep.md, so your
program prints the output shown after Cargo’s build messages.
The pattern is a literal substring, not a regular expression. If no line contains it, the program prints nothing and exits successfully.
Your utility must also accept multiple explicit files:
cargo run -- Utility tests/grep.md tests/recursive/grep.md## Search Utility
## Search UtilityIt searches them in the order in which they appear. Without -f, no file name
is printed. A wildcard such as
tests/*.md is expanded by a shell before a program starts; wildcard
matching is not part of this assignment. Rustrace’s console is not a shell, so
it passes tests/*.md to your program unchanged.
Command-Line Options
The following options may appear anywhere among the pattern and file paths: before, between, or after them.
-i: case-insensitive search-n: print line numbers with matching lines-v: invert the match and print nonmatching lines-r: recursively search directories-f: print the filename with each selected line-hor--help: display help information
-h and --help must print the following text and exit successfully. The
empty line after the first line is required.
Usage: grep [OPTIONS] <pattern> <files...>
Options:
-i Case-insensitive search
-n Print line numbers
-v Invert match (exclude lines that match the pattern)
-r Recursive directory search
-f Print filenames
-h, --help Show help informationIf no pattern is supplied, print Pattern is missing. and exit with status 1.
If a pattern is present but no file path is supplied, print No files specified. and exit with status 1.
Case-Insensitive Search
With -i, compare lowercase versions of the pattern and each input line, but
print the original text:
cargo run -- Utility tests/grep.md -i## Search Utility
In this programming assignment, you are expected to implement a command-line utility thatLine Numbers
With -n, prefix each selected line with its one-based line number followed by
: :
cargo run -- Utility tests/grep.md -n1: ## Search UtilityInverted Matching
With -v, print lines that do not contain the pattern:
cargo run -- Utility tests/grep.md -vIn this programming assignment, you are expected to implement a command-line utility that
searches for a specific pattern in one or multiple files, similar in spirit to the UNIX
`grep` command.Recursive Search
With -r, use the walkdir crate to visit the files beneath a directory:
cargo run -- Utility tests -rFor a tree containing tests/grep.md and
tests/recursive/grep.md, the two matching lines are:
## Search Utility
## Search UtilityThe order in which recursively discovered files are printed is not specified;
the tests accept any traversal order. Without -r, a directory is not a file
to search and must produce Invalid path: {path}.
Filenames
With -f, prefix every selected line with its path followed by : :
cargo run -- Utility tests -r -ftests/grep.md: ## Search Utility
tests/recursive/grep.md: ## Search UtilityWith both -f and -n, the path comes first, then the line number:
tests/grep.md: 1: ## Search Utility.
The two lines may appear in the opposite order during recursive traversal.
Implementation
You will write this assignment in Rustrace, the course editor
required for all assignments. Download lab2.rta, save
it in a normal local folder, and open it with:
rustrace work lab2.rtaLab 2 needs Rustrace 0.1.8 or later; if rustrace work refuses
lab2.rta, run rustrace update and try again. Rustrace creates
lab2.work with a complete Cargo package, and lab2.test-cases
beside it with the public test cases. The walkdir dependency and its exact
version are already recorded in Cargo.toml and Cargo.lock. The
application shell, help output, file-opening loop, and ten public tests are
also provided.
Implement the three functions marked with todo!() in
src/main.rs:
parse_configclassifies options, the pattern, and paths;gather_pathsresolves explicit files and recursive directories; andsearch_fileperforms matching and formats selected lines.
Each blank contains comments describing its contract, suggested steps,
available helpers, and common pitfalls. Read the whole file before starting,
then replace the placeholder code. You may create additional Rust source files
under src/ if useful. The assignment permits unwrap() for file
reading: you may assume the supplied files are readable UTF-8 text.
Testing in Rustrace
Rustrace gives you two ways to test your program: ten test cases in the F4
picker, and the public Rust tests that cargo test runs.
Press F4 (Fn + F4 on Macs), or choose Test cases in the F7 menu (Fn + F7 on
Macs), to open the test cases. Each case runs your program with command-line
arguments, such as -n Utility tests/grep.md, and compares everything it
prints to standard output with the expected output, byte for byte. When you select a case, the picker shows its
arguments, each between « and », the folder it runs in and the files
there, and its last result. Press Enter to run the selected case, or select
Run all to run all ten. The result is PASS, FAIL with the first line
that differs, or ERROR if your program did not finish normally, for example
because it reached a todo!(). No Lab 2 case gives your program any
input: each runs with its standard input closed.
The cases are in lab2.test-cases, next to lab2.work. Your program
runs in its files folder, lab2.test-cases/files, which contains
tests/grep.md and tests/recursive/grep.md, so a path such as
tests/grep.md in an argument is relative to that folder. Do not edit,
add, or remove files in lab2.test-cases/files: the expected output was
written for these files, and the picker shows changed from the package if
they differ. To restore them, remove the files you changed or added, then quit
and run rustrace work lab2.rta again. On macOS, opening one of these
folders in Finder can add a hidden .DS_Store file, which is not UTF-8 text
and which -r would read; if a recursive case stops with an error, delete any
.DS_Store under lab2.test-cases/files.
cargo run in the F9 console (Fn + F9 on Macs) also runs your program in
lab2.test-cases/files. Type its arguments after --, with paths relative
to that folder:
cargo run -- -n Utility tests/grep.mdThis repeats the options-first case and prints 1: ## Search Utility. To
repeat any other case, type the arguments the picker shows, without the « and
» marks. The console is not a shell: each space-separated word after
-- becomes one argument exactly as typed, and quotes and pipes are not
accepted.
The public suite is in tests/public_cli.rs inside your crate. Each test
creates a small temporary file tree and invokes your compiled grep binary
with command-line arguments. The test cases repeat eight of these tests
exactly. The two recursive cases search tests/recursive, which holds a
single file, because the order in which files are found varies between
computers and the cases compare output exactly. The Rust tests search the whole
tests tree and accept any order. Run all ten tests with Test in the F7
menu, or enter the following in the F9 console:
cargo testYou can run one test while debugging by supplying its name as a filter, for example:
cargo test basic_search_prints_the_matching_lineCheck the reported test count: a successful filtered command that matched zero tests did not exercise your program.
Use Check and Clippy in the F7 menu as you work. The starter itself is warning-free, and your completed program should remain warning-free.
Notes on AI Tools
This assignment is designed for manual work without AI assistance, and you should not use AI tools. One objective of the course is to learn Rust by writing and debugging the code yourself; using AI changes that learning experience and defeats the purpose of the exercise.
For this reason, you must write the assignment in Rustrace. Rustrace records how your program comes together and accepts only text that you type or copy within the editor itself. See the Rustrace guide for installation and usage instructions.
Submission
When you are done, quit Rustrace and create your submission from a terminal, using your UTORid as the identifier:
rustrace submit lab2.work --student-id YOUR_UTORIDThis finalizes the workspace and writes a ZIP file next to lab2.work.
Upload that ZIP file to Quercus under Assignment 2. Rustrace does not upload
anything itself, and the ZIP already contains everything needed for marking,
so do not add, remove, or rename files.
The deadline is Monday, October 19, 2026, at 11:59 p.m. Eastern time. Late
submissions will not be accepted. After rustrace submit finalizes
lab2.work, use the revision procedure in the
Rustrace guide if you need to make another attempt. Keep the
workspace until your grade is released.
Marking
The automarker uses 20 equally weighted tests: the 10 public tests included in your crate and 10 additional private tests kept by the teaching team. Each test is worth 5 points. The grader restores an authoritative copy of the public suite together with the supplied Cargo manifest and lockfile, then adds the private suite only after your submitted source has been verified. Changes to the student copy of the tests do not affect marking. The F4 test cases repeat public tests and are not marked separately.
The tests cover exact help text, basic and multi-file search, option placement, case-insensitive and inverted matching, line numbers, filenames, recursive traversal, option combinations, empty results, repeated matches, and a compact recursive stress case. Except where recursive traversal order is explicitly unspecified above, output is compared exactly.