# Hacking Pinakes 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: - Develop Pinakes - Build Pinakes - Contribute to Pinakes for developers as well as: - Distribute Pinakes for Pinakes maintainers and packagers. ## Development Environment [Nix]: https://nixos.org [Direnv]: https://direnv.net 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/.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.