docs: finalize hacking guidelines

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I2d3a5a9d745630f5da305664f0bdc66e6a6a6964
This commit is contained in:
raf 2026-03-23 03:30:12 +03:00
commit 520489ab48
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF

View file

@ -1,11 +1,12 @@
# Hacking Pinakes
Pinakes is a lot of things. One of the things it aims to be is _complete_. To be
complete in features, to be complete in documentation and to be complete in
hackability. This document covers, very comprehensively, how you may:
Pinakes is a lot of things. It also _plans_ to be a lot of things. One of those
things, as you might have noticed, is _complete_. To be complete in features, to
be complete in documentation and to be complete in hackability. This document
covers, very comprehensively, how you may:
- Build Pinakes
- Develop Pinakes
- Build Pinakes
- Contribute to Pinakes
for developers as well as:
@ -14,19 +15,262 @@ for developers as well as:
for Pinakes maintainers and packagers.
## Building Pinakes
Pinakes is built with Rust (nightly edition) and various crates. The most
_notable_ crate among those is Dioxus, which provides its own toolkit. The UI
for Pinakes is usually _not_ built with the Dioxus CLI but instead with
`cargo build`. This also applies to distributable build results.
## Development Environment
[Nix]: https://nixos.org
[Direnv]: https://direnv.net
To build Pinakes, simply pick the components you want and build them with
`cargo build --release --package <component>`. A Nix shell is provided for
reproducible developer environments and you may obtain all build dependencies by
simply running `nix develop` or `direnv allow` if you use [Direnv]. Nix is a
cross-platform build tool and works on most Linux distributions as well as
Darwin. While distro-specific package managers _might_ work, Nix is the only
supported one.
Pinakes uses [Nix] with flakes for pure, reproducible developer environments.
All of the build dependencies that you need, from Rust to GTK and webkit, are
provided in the devshell. In addition to _not_ requiring system libraries, we'd
like to _actively discourage you_ from relying on system libraries.
[Direnv] may be preferred by some users as an alternative to `nix develop`.
```bash
# Enter the dev shell
$ nix develop
# Or with direnv (recommended)
$ direnv allow
```
Nix is the only supported way to get all dependencies. Distro package managers
_may_ work for the core crates but are not supported for the UI.
## Building Pinakes
Pinakes is a Cargo workspace with multiple crates targeting multiple aspects of
the projects. The user-facing crates are provided in `packages/*`.
`pinakes-server` and `pinakes-tui` can be built normally with `cargo` while the
UI crate, i.e., `pinakes-ui` **must** be built with the Dioxus CLI (`dx`) so
that the SCSS stylesheets are compiled correctly. Everything else should work
perfectly fine with the standard `cargo build`.
[Just]: https://just.systems
To make the things a _little_ bit more convenient, this project uses [Just] as
its command runner and provides a few recipes for common tasks.
```bash
# Build all crates (server + TUI + UI)
$ just build-all
# Build individual components
$ just build-server # pinakes-server (HTTP API)
$ just build-tui # pinakes-tui (terminal UI)
$ just build-ui # pinakes-ui (Dioxus desktop/web UI, requires dx)
```
The dependencies required for the recipes, including Just itself, is provided by
the Nix devshell. You may also run the cargo equivalents of those commands:
```bash
# Core crates
$ cargo build -p pinakes-server
$ cargo build -p pinakes-tui
# The UI crate must be built with dx, not cargo.
$ dx build -p pinakes-ui
```
Release builds follow the same pattern, but with `--release`. Nothing else
should be required for the most part.
## Running Pinakes
For the time being, UI clients require the server to be running before they can
function. Offline sync is planned, but likely come later alongside cross-device
sync. All UIs connect to the server over HTTP.
```bash
# 1. Copy and edit the example config (first time only)
$ cp pinakes.example.toml pinakes.toml
# 2. Start the server (defaults to 127.0.0.1:3000)
$ cargo run -p pinakes-server -- pinakes.toml
# 3a. Run the TUI client
$ cargo run -p pinakes-tui
# 3b. Run the desktop/web UI
$ cargo run -p pinakes-ui
# Connect TUI to a non-default server address
$ cargo run -p pinakes-tui -- --server http://localhost:3000
```
The GUI reads `PINAKES_SERVER_URL` if set; otherwise it assumes
`http://127.0.0.1:3000`. You may change the host and port if you require.
## Testing
[cargo-nextest]: https://nexte.st
Pinakes boasts, or well, attempts to boast a very comprehensive testing suite to
ensure no regressions are introduced while in development. For fast parallel
test execution, we use [cargo-nextest] and while you are recommended to use it
over `cargo test` both should be supported. For CI and package tests, only
`cargo-nextest` is supported.
```bash
# Run all workspace tests (preferred)
$ just test
# Equivalent without Just
$ cargo nextest run --workspace
# Tests for a single crate
$ cargo nextest run -p pinakes-core
$ cargo nextest run -p pinakes-server
# Run a specific test by name and show output
$ cargo test -p pinakes-core -- test_name --nocapture
```
## Linting and Formatting
### Formatting
There is a treewide formatter provided within the `flake.nix` that invokes the
recommended formatter tooling across the various filetypes, including database
migrations and Markdown sources. While a `just fmt` recipe is provided, it will
not cover most of the files. Still, you may format the Rust sources as such:
```bash
# Format all code
$ just fmt # or: cargo fmt
# Check formatting without modifying files
$ cargo fmt --check
```
### Linting
For now lints only cover Rust sources, and are provided by the Clippy tool. You
may, as usual, invoke it with `just lint`.
```bash
# Run Clippy
$ just lint # or: cargo clippy --workspace
# Treat Clippy warnings as errors (used in CI)
$ cargo clippy --workspace -- -D warnings # `-D warnings` is recommended
```
All Clippy warnings, besides some of the really annoying ones or false
positives, must be resolved at the source. **Do not** use `#[allow(...)]` or
`#[expect(...)]` to silence warnings unless there is a documented reason.
It may be okay, at times, to suppress lints but you should focus on _resolving_
them rather than suppressing them. _If_ suppressing, prefer `#[expect]` and
provide the `reason` parameter.
## Generating API Documentation
The REST API documentation is generated from OpenAPI annotations in
`pinakes-server` using `cargo xtask`:
```bash
# Generate the API documentation at docs/api/
$ just docs # or: cargo xtask docs
```
This writes:
- `docs/api.md` - Index linking all generated files
- `docs/api/<tag>.md` - One Markdown file per API tag
- `docs/api/openapi.json` - Full OpenAPI 3.0 specification
Please do not edit files under `docs/api/` by hand; they are regenerated on each
run.
## Code Style
There are many conventions that are in play within the Pinakes codebase. You'll
get used and adjust to most of them naturally, but here are some of the general
conventions written down for convenience.
### General
1. **Make illegal states unrepresentable**; ıse ADTs and newtype wrappers to
encode constraints in the type system. Parse and validate at system
boundaries (user input, external APIs); trust internal types everywhere else.
2. This one is kind of obvious, but please use the `tracing` crate with
structured fields. Do not use `println!` or `eprintln!` in library or server
code.
3. All storage operations must be implemented for both the SQLite and PostgreSQL
backends. Neither backend is optional.
Naming is kind of obvious, but I'd like to remind you that we use `snake_case`
for functions and variables, `PascalCase` for types and traits,
`SCREAMING_SNAKE_CASE` for constants. There is not much else to it.
### Error Handling
- **`unwrap()` and `expect()` are banned** in non-test code. Use `?`,
`ok_or_else`, `match`, or `if let` instead.
- The only accepted exception is when a failure is truly impossible (e.g., a
hardcoded regex that is known-valid at compile time). In that case, add
`#[expect(clippy::expect_used, reason = "...")]` with a clear explanation.
- Wrap storage errors with the `db_ctx` helper so error messages include the
operation and relevant ID:
```rust
stmt.execute(params).map_err(db_ctx("insert_media", &media_id))?;
```
### Disallowed Types and APIs
Pinakes uses hashers from `rustc_hash`, i.e., `FxHashMap` and `FxHashSet` over
the `std` equivalents due to their faster nature without random state. This is
enforced by clippy.
As Pinakes uses Rust 1.90+, the now-deprecated `once_cell` crate is also banned.
Use the stdlib equivalents:
- `once_cell::unsync::OnceCell` -> `std::cell::OnceCell`
- `once_cell::sync::OnceCell` -> `std::sync::OnceLock`
- `once_cell::unsync::Lazy` -> `std::cell::LazyCell`
- `once_cell::sync::Lazy` -> `std::sync::LazyLock`
## Contributing
1. Fork the repository and create a feature branch.
2. Enter the dev shell: `nix develop` (or `direnv allow`).
3. Make your changes; keep commits focused and descriptive. We use **scoped
commits**.
4. Run `just fmt` and `just lint` before opening a pull request.
5. Run `just test` to verify nothing is broken.
6. Open a pull request against `main`. Describe what you changed and why.
For significant changes, consider opening an issue first to discuss the
approach.
## Packaging and Distribution
Pinakes is released under EUPL v1.2. For distributable release builds, please
respect the license. You may create release builds for the user-facing crates
using `cargo build --release` for server and TUI components. Use `dx` instead
for the GUI:
```bash
# Server
$ cargo build --release -p pinakes-server
# TUI
$ cargo build --release -p pinakes-tui
# GUI
$ dx build --release -p pinakes-ui
```
The resulting binaries are self-contained except for system libraries required
by the UI (GTK3, libsoup, webkit2gtk). Database migrations run automatically on
first server startup via the `refinery` crate; no manual migration step is
needed.
SQLite (the default backend) requires no external database server. PostgreSQL
deployments need the `pg_trgm` extension enabled on the target database. It is
generally advisable to document those requirements for the users, even though
they do not require additional packaging steps.