capital-gains
is a CLI to calculate the tax to be paid on profits or losses
from operations in the stock market.
This is a code challenge test that I’ve done for a banking company.
I chose Rust to construct this application. The key argument for this decision is that Rust’s CLI ecosystem is one of the best available today. Add to that the fact that Rust is a very efficient, stable, and safe language. All of this has resulted in an increasing number of adopters, more use inside businesses, and even its inclusion in the Linux kernel.
In order to reduce the core application libraries to the minimum, I’ve used only
a few dependencies crates for this application, as you can see in the
Cargo.toml
. Which are:
capital-gains
is written in Rust, so you’ll need to grab a Rust installation in order to compile it.
To build capital-gains
from source, run:
make rs-build
Archives of precompiled binaries for
capital-gains
are available for Windows, macOS, and Linux.
To install capital-gains
on your local machine, install
Rust and run:
make rs-install
If you don’t want to install Rust on your machine, you may build the Docker image with the command:
make docker-build-local
The Docker image is constructed using a distroless image. Therefore is very small and more secure, because contains only the necessary code to run the application:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
capital-gains local 9a7201859b1e 2 minutes ago 24.7MB
capital-gains
follows the Command Line Interface Guidelines. To get help about the CLI usage, run:
capital-gains --help
# or
docker run -i capital-gains:local --help
capital-gains is a CLI to calculate the tax to be paid on profits or losses from
operations in the stock financial market.
Usage: capital-gains < [TRANSACTIONS]
Arguments:
[TRANSACTIONS]
A list of financial stock market operations that are JSON formatted
and separated by lines that need to be inputted using standard input.
[default: -]
Options:
-h, --help
Print help (see a summary with '-h')
-V, --version
Print version
Examples:
capital-gains <src/data/input-*.json
<src/data/input-*.json capital-gains
cat src/data/input-*.json | capital-gains
docker run -i capital-gains:local <src/data/input-*.json
<src/data/input-*.json docker run -i capital-gains:local
cat src/data/input-*.json | docker run -i capital-gains:local
To test the application, you have two options:
make rs-tests
;make docker-build-test
;Here are the performance results measured with hyperfine according to input size:
Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
---|---|---|---|---|
capital-gains [ input_size=10^0 ] |
15.1 ± 2.2 | 11.5 | 24.8 | 1.01 ± 0.18 |
capital-gains [ input_size=10^1 ] |
14.9 ± 1.5 | 12.1 | 19.7 | 1.00 |
capital-gains [ input_size=10^2 ] |
18.9 ± 2.2 | 15.5 | 25.5 | 1.26 ± 0.20 |
capital-gains [ input_size=10^3 ] |
52.6 ± 6.8 | 40.6 | 61.7 | 3.52 ± 0.58 |
capital-gains [ input_size=10^4 ] |
276.5 ± 16.1 | 243.1 | 294.9 | 18.53 ± 2.16 |
capital-gains [ input_size=10^5 ] |
2622.1 ± 193.7 | 2420.4 | 3020.5 | 175.75 ± 22.02 |
capital-gains [ input_size=10^6 ] |
35029.2 ± 8242.0 | 24562.3 | 44798.0 | 2347.88 ± 601.36 |
The results above were measured on the following machine:
inxi -Cmz
Memory:
RAM: total: 31.04 GiB used: 13.96 GiB (45.0%)
RAM Report:
permissions: Unable to run dmidecode. Root privileges required.
CPU:
Info: 10-core (2-mt/8-st) model: 13th Gen Intel Core i5-1345U bits: 64
type: MST AMCP cache: L2: 6.5 MiB
Speed (MHz): avg: 984 min/max: 400/4700:3500 cores: 1: 1262 2: 1130
3: 977 4: 400 5: 1400 6: 1063 7: 1389 8: 1034 9: 904 10: 974 11: 886
12: 400
No memory leaks were found using Valgrind. We don’t have to worry about the data presented in the still reachable section, here’s why. To execute the memory test, run:
make valgrind-test
==499495== Memcheck, a memory error detector
==499495== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==499495== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==499495== Command: capital-gains
==499495==
==499495==
==499495== HEAP SUMMARY:
==499495== in use at exit: 8,192 bytes in 1 blocks
==499495== total heap usage: 47 allocs, 46 frees, 17,539 bytes allocated
==499495==
==499495== LEAK SUMMARY:
==499495== definitely lost: 0 bytes in 0 blocks
==499495== indirectly lost: 0 bytes in 0 blocks
==499495== possibly lost: 0 bytes in 0 blocks
==499495== still reachable: 8,192 bytes in 1 blocks
==499495== suppressed: 0 bytes in 0 blocks
==499495== Reachable blocks (those to which a pointer was found) are not shown.
==499495== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==499495==
==499495== For lists of detected and suppressed errors, rerun with: -s
==499495== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Run make
to view all possible recipes to run within the project:
bash-all Run all bash tests
bash-check Check format bash code
bash-deps Install bash dependencies
bash-fmt Format bash code
bash-lint Check lint bash code
comments-tidy Tidy comments within code
doc-changelog Write CHANGELOG.md
doc-readme Write README.md
docker-build-local Docker build local image
docker-build-test Docker build test image and run tests
dprint-check Dprint check
dprint-fmt Dprint format
help Display this help screen
makefile-descriptions Check if all Makefile rules have descriptions
rs-audit Audit Cargo.lock
rs-audit-fix Update Cargo.toml to fix vulnerable dependency requirement
rs-bench Benchmark binary
rs-build Build binary
rs-cargo-deps Install cargo dependencies
rs-check Run check
rs-dev Run check in watch mode
rs-doc Open app documentation
rs-fix Fix rust code
rs-fmt Format rust code
rs-fmt-fix Format fix rust code
rs-install Install binary
rs-lint Lint rust code
rs-lint-fix Fix lint rust code
rs-outdated Display when dependencies are out of date
rs-tests Run tests
rs-uninstall Uninstall binary
rs-update-cargo Update dependencies
rs-update-rustup Update rust
typos Check typos
typos-fix Fix typos
valgrind-test Valgrind test for memory leaks
To generate a new version, you need to follow these steps:
main
branch, you must bump the version inside the Cargo.toml
file.make rs-check
so that the version is changed in the Cargo.lock
file.git add -A && git commit -m "release: bump version"
.git tag -a <your.new.version> -m "version <your.new.version>"
.make doc-changelog && make doc-readme
.git add -A && git commit -m "release: <your.new.version>"
.git push
to main
.This documentation is generated by shell scripts. Please check out dev
directory for more information.