5.9 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project
drawer is a Rust project (edition 2024). Entry point is src/main.rs.
Dependencies
tar— reading and writing tarballs in memoryflate2— gzip compression, paired withtarage— encryption/decryption using SSH keys (RSA or Ed25519) loaded from diskclap— command line argument parsing (derive API), used for subcommands and flags
drawer is a command line tool for managing projects.
A drawer is a single directory with related files in for a project. Drawer files always end
in the .drawer extension, like foo.drawer. To create a drawer file from a directory, we
first tar all files in the directory, and then encrypt the resulting tarball. To open a drawer
file, we decrypt it and expand the tarball to a directory.
The drawer tool has two subcommands, drawer open and drawer close. Each of these commands
requires a key, the path to which can be given by either the $DRAWER_KEY environment variable
or a -i command line parameter.
Open subcommand
The drawer open subcommand will decrypt and untar a drawer file into a path. It takes two arguments,
the name of a drawer file (required) and a path to expand it into (optional, defaults to a directory
under the current directory named the same as the drawer file). If the target path exists and is not
empty, print an error message, unless the -f flag is passed.
Close subcommand
The drawer close subcommand will create a drawer file from a directory and a key. The command
takes two arguments, the name of a drawer file (required) and the path to a directory (optional,
defaults to the current directory). If the target drawer file is inside the directory, print an
error message saying so. Otherwise, create a tarball (in memory) of the directory contents, encrypt
them (again in memory), and write the drawer file to disk.
Examples:
drawer close -i ~/.keys/mykey ~/Documents/blah.drawer ~/Documents/blah(create a file,~/Documents/blah.drawer, encrypted with~/.keys/mykey, containing the contents of~/Documents/blah)drawer open ~/Documents/blah.drawer(open~/Documents/blah.drawerinto./blahusing a key found at$DRAWER_KEY)
Implementation plan
The implementation of this project will happen in several phases. Claude will do the primary coding for each phase, then the user (me) will review the code, write tests, and possibly make changes, before proceeding with the next phase.
At to time should Claude delete or modify any tests whatsoever.
Phase 1
Write code that uses clap to parse the command line arguments and create a DrawerOperation struct from them. The
DrawerOperation struct will contain:
- An
operation_type(OperationType, enum ofOpenorClose) - A
drawer_file(Pathfor the drawer file to open or close) - A
key_path(Pathto the key we'll use, which is either the value of the-iflag or the value of the$DRAWER_KEYenv var) - A
target_path(Pathto the contents we're going to put into or take out of the drawer) - A
force(bool for whether or not-fwas passed)
In order for this to be testable, it needs to be a function that returns a Result<DrawerOperation, DrawerError>. The
main function should call that and then do nothing with the result.
Certain things should cause the function to return Err:
- Not passing a drawer file
- Passing a subcommand other than
openorclose - Passing any flags but
-for-i - Not passing
-iwhen the$DRAWER_KEYenvironment variable is undefined
Phase 2
Phase 2 is about error handling. We want to add some variants to DrawerError and a validate method to DrawerOperation
that will catch various problems and ensure that we can do the operation correctly. Here are some error cases in no particular order:
- If the command is
Openand the drawer file does not exist. - If the command is
Closeand the target path either does not exist, is not a directory, or is not readable. - If the
keyisEnvVarbut the$DRAWER_KEYenvironment variable is not set, or is not set to a path that exists and is readable. - If the
keyisPath()but the path does not exist or is not readable. - If the command is
Openand the target is a non-empty directory that exists, and theforceflag is false - If the command is
Closeand the target contains the drawer file (at any level; the target is any parent of the drawer file's path)
The validate method should take &self and return Result<(), DrawerError>. Let's have main, which is currently
creating a DrawerOperation and not using it, also call validate and print out the error if there is one. This means
we also need to impl Display on DrawerError.
Phase 3
For phase 3 we want to start implementing the close command. We'll get the tar part working but not worry about encryption yet.
- Make a new method
performonDrawerOperationwhich takes&selfand returnsResult<(), DrawerError>. performshould first callvalidateand return the error if there is one.- After that, it should call either
open(&self)orclose(&self)depending on the operation type. For now,openwill just be atodo!() - The new
closemethod should create a vec consisting of the contents of the target directory as a tarball (use the tar create we depend on) - After creating the vec, it should write it out to the drawer file path.
I'm going to add some test data to the project under tests/ but it's just random data to test, ignore the contents of all those files.
Phase 3a
As a minor improvement, make the perform_close method also gzip the contents of the tarball.
Commands
cargo build # compile
cargo run # build and run
cargo test # run all tests
cargo test <name> # run a single test by name
cargo clippy # lint
cargo fmt # format