Created
March 1, 2026 22:31
-
-
Save IceWreck/d4c3f7facd3297de59019c466292f132 to your computer and use it in GitHub Desktop.
Rust Boilerplate
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Rust Boilerplate | |
| ### Init | |
| In the empty git repo, init with `cargo init --edition 2024`. If the project also needs to have a lib then pass in `--lib`. | |
| The repo tree should look something like this. | |
| ``` | |
| . | |
| ├── AGENTS.md | |
| ├── Cargo.lock | |
| ├── Cargo.toml | |
| ├── config | |
| │ └── example.toml | |
| ├── Dockerfile | |
| ├── Makefile | |
| ├── README.md | |
| └── src | |
| ├── bins | |
| │ └── foobar | |
| │ └── main.rs | |
| ├── config.rs | |
| ├── lib.rs | |
| └── foo.rs | |
| ``` | |
| If the repo does not need any libs then you can exclude the bins folder and put the binaries directly in `src` . | |
| ### Agents MD | |
| `AGENTS.md` file also symlink to `CLAUDE.md`. | |
| ``` | |
| # Agents Knowledge | |
| ## Code Conventions | |
| - Use lowercase for log messages: `info!("starting xyz")` | |
| - Log messages should not end with a period | |
| - Log messages should start with lowercase (except proper nouns) | |
| - Error messages should start with lowercase and not end with a period | |
| - Error messages should use "failed to" / "unable to" phrasing | |
| - Use early returns to reduce indentation | |
| - Extract complex logic into functions | |
| - Leverage data structures instead of deeply nested conditions | |
| - Prefer clear variable and function names over comments | |
| - Write self-explanatory code – readable without comments | |
| - Explain "why" not "what" – comments clarify intent, don't restate code | |
| - Avoid redundant comments – don't comment obvious things | |
| - Use doc comments (`///`) for public items – document purpose, inputs, outputs | |
| - Keep comments updated – outdated comments are worse than none | |
| - Use inline comments sparingly – only when necessary | |
| - Use `thiserror` for libraries (typed errors, public API) | |
| - Use `anyhow` for applications (simple propagation, add context) | |
| - Place integration tests in the top-level `tests/` directory | |
| - Keep unit tests alongside the code in the same module files using the `#[cfg(test)]` module | |
| ## Preferred Libraries | |
| - **clap** – CLI parsing | |
| - **ratatui** + **crossterm** – Terminal UI | |
| - **thiserror** – Library errors | |
| - **anyhow** – Application errors | |
| - **log** + **env_logger** – Logging | |
| - **axum** – HTTP server | |
| - **reqwest** – HTTP client | |
| - **tokio** – Async runtime | |
| - **serde** – Serialization | |
| - **sqlx** – Database | |
| ## Development | |
| ```bash | |
| cargo fmt # Format | |
| cargo test # Test | |
| cargo build # Build | |
| cargo run --bin <your-bin-name> | |
| ``` | |
| ``` | |
| ### Makefile | |
| ``` | |
| .PHONY: * | |
| SHELL := /bin/bash | |
| IMAGE := code.abifog.com/packages/foobarbin:latest | |
| build: | |
| cargo build | |
| release: | |
| cargo build --release | |
| podman-build: | |
| podman build -t $(IMAGE) . | |
| podman-push: | |
| podman push $(IMAGE) | |
| run: | |
| cargo run | |
| format: | |
| cargo fmt | |
| clean: | |
| cargo clean | |
| test: | |
| cargo test | |
| ``` | |
| ### Main | |
| Just a hello world using the logger and clap for CLI creation. | |
| It should load config file and then log it. | |
| ### Gitignore | |
| ``` | |
| # Environment files | |
| .env | |
| .env.* | |
| # Build | |
| build/ | |
| dist/ | |
| # IDE | |
| .vscode/ | |
| .idea/ | |
| # OS | |
| .DS_Store | |
| # Other | |
| /drafts | |
| /config/config.toml | |
| ``` | |
| ### Config.rs | |
| ```rust | |
| use serde::{Deserialize, Serialize}; | |
| use std::path::Path; | |
| use thiserror::Error; | |
| #[derive(Error, Debug)] | |
| pub enum ConfigError { | |
| #[error("failed to read file: {0}")] | |
| Io(#[from] std::io::Error), | |
| #[error("failed to parse config: {0}")] | |
| Parse(#[from] toml::de::Error), | |
| #[error("config file not found: {0}")] | |
| NotFound(String), | |
| } | |
| #[derive(Debug, Deserialize, Serialize)] | |
| pub struct Config { | |
| pub foo: i32, | |
| pub bar: String, | |
| } | |
| impl Config { | |
| pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, ConfigError> { | |
| let path = path.as_ref(); | |
| if !path.exists() { | |
| return Err(ConfigError::NotFound(path.display().to_string())); | |
| } | |
| let content = std::fs::read_to_string(path)?; | |
| let config: Config = toml::from_str(&content)?; | |
| Ok(config) | |
| } | |
| } | |
| ``` | |
| ### Dockerfile | |
| ``` | |
| # syntax=docker/dockerfile:1 | |
| FROM --platform=linux/amd64 docker.io/rust:1.85-alpine AS builder | |
| WORKDIR /app | |
| RUN apk add --no-cache musl-dev | |
| COPY Cargo.toml Cargo.lock ./ | |
| COPY src ./src | |
| RUN cargo build --release --bin foobarbin | |
| FROM --platform=linux/amd64 docker.io/alpine:latest | |
| RUN apk add --no-cache ca-certificates | |
| COPY --from=builder /app/target/release/foobarbin /usr/local/bin/foobarbin | |
| ENTRYPOINT ["foobarbin"] | |
| CMD ["--help"] | |
| ``` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment