Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3972fdd
loko 0.2: Clock, Random, Env, Console, and Store services, platforms,…
olekslitus Oct 21, 2026
206d49a
Spec: the Store spec links the OpenDAL adapter issue (#6)
olekslitus Oct 21, 2026
f05508f
Clock, Random, Console: one module per implementation
olekslitus Oct 21, 2026
5a6f520
Object store: keep the URL's port, read empty ranges as no bytes, lis…
olekslitus Oct 21, 2026
009756a
Resources, Provides, and wrappers
olekslitus Oct 21, 2026
f59288b
Review fixes from CodeRabbit (loko#5)
olekslitus Oct 21, 2026
83a278f
README: a guide from one resource to bundles and wrappers, run as doc…
olekslitus Oct 21, 2026
e3e08ef
COMPARISON.md: loko next to delegate, ambassador, DI crates, tower, a…
olekslitus Oct 21, 2026
c6d49da
Docs: why resources take &self, and how their state stays safe
olekslitus Oct 21, 2026
112fb3e
Random: a source of generators (rng()), not of numbers
olekslitus Oct 21, 2026
f7d38cc
README: implementing a loko resource yourself (a clock that runs faster)
olekslitus Oct 21, 2026
ebbc1c7
README: making your own platform (the system with a fixed random seed)
olekslitus Oct 21, 2026
df80c03
README: a fast clock as a wrapper around any clock
olekslitus Oct 21, 2026
4f258ad
Bundles: #[loko::bundle] with #[loko::has(…)] fields, instead of deri…
olekslitus Oct 21, 2026
33bfbd0
Resource crates without macros or dependencies
olekslitus Oct 21, 2026
2dd3189
README: Sans-I/O, its two shapes and where loko fits
olekslitus Oct 21, 2026
6c79750
Services again: #[loko::service] instead of #[loko::resource]
olekslitus Oct 21, 2026
40ec180
README: simpler examples, and why loko first
olekslitus Oct 21, 2026
a267c84
Review fixes: WebAssembly has no times before the epoch, JavaScript i…
olekslitus Oct 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Dependabot keeps dependencies current: one pull request a week per ecosystem with minor and patch updates, and one
# per major update. A new version is proposed once it's 7 days old (security updates at once).
version: 2

updates:
- package-ecosystem: github-actions
directory: /
schedule: { interval: weekly }
cooldown: { default-days: 7 }
groups:
actions: { update-types: [minor, patch] }

- package-ecosystem: cargo
directory: /
schedule: { interval: weekly }
cooldown: { default-days: 7 }
groups:
cargo: { update-types: [minor, patch] }
84 changes: 84 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Checks every push to main and every pull request. Needs no secrets.
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

concurrency:
group: ci-${{ github.event_name == 'pull_request' && github.ref || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0

jobs:
hooks:
name: Hooks (YAML, TOML, private keys, rustfmt)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- run: rustup toolchain install # rustfmt, for the rustfmt hook
# The commit-stage hooks of .pre-commit-config.yaml; the push-stage ones (clippy) run in the jobs below.
- uses: j178/prek-action@44924fe9ef4a6f88253c4b6b1bf11c689d3ce53d # v3.1.0
with:
prek-version: 0.5.7

native:
name: Native (format, lint, tests)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- run: rustup toolchain install # from rust-toolchain.toml
- uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3
- run: cargo fmt --all --check
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- run: cargo clippy --workspace --all-features --all-targets -- -D warnings
- run: cargo test --workspace --all-features
# Each feature builds on its own, and with none, without warnings.
- name: Features one at a time
env:
RUSTFLAGS: -D warnings
run: |
cargo check -p loko --no-default-features
cargo check -p loko
cargo check -p loko --features tokio
cargo check -p loko --features store
cargo check -p loko --features object-store
cargo check -p loko-clock --no-default-features
cargo check -p loko-clock --features tokio
cargo check -p loko-random --no-default-features
cargo check -p loko-random --features getrandom
cargo check -p loko-env
cargo check -p loko-console
cargo check -p loko-store
cargo check -p loko-object-store

wasm:
name: WebAssembly (browser and WASI targets)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- run: rustup toolchain install # from rust-toolchain.toml
- run: rustup target add wasm32-unknown-unknown wasm32-wasip2
- uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3
- run: cargo clippy --workspace --all-features --target wasm32-unknown-unknown -- -D warnings
- run: cargo clippy --workspace --all-features --target wasm32-wasip2 -- -D warnings
# The JavaScript implementations on their own, and the browser build without them.
- name: Features one at a time
env:
RUSTFLAGS: -D warnings
run: |
cargo check -p loko --no-default-features --target wasm32-unknown-unknown
cargo check -p loko --no-default-features --features js --target wasm32-unknown-unknown
cargo check -p loko --target wasm32-wasip2
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
.env*
.DS_Store
45 changes: 45 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Git hooks, run by prek (https://prek.j178.dev): `uv tool install prek && prek install --install-hooks`.
# On commit: formatters and fast checks on the staged files. On push: clippy, natively and for both wasm32 targets.
# Everything: `prek run --all-files --hook-stage pre-push`. CI runs the same checks plus the tests.
default_install_hook_types: [pre-commit, pre-push]

repos:
- repo: builtin
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-merge-conflict
- id: check-yaml
- id: check-toml
- id: check-added-large-files
args: [--maxkb=1024]
- id: detect-private-key

- repo: local
hooks:
- id: rustfmt
name: rustfmt
entry: rustfmt --edition 2024
language: system
types: [rust]
- id: clippy
name: clippy
entry: cargo clippy --workspace --all-features --all-targets --quiet -- -D warnings
language: system
types: [rust]
pass_filenames: false
stages: [pre-push]
- id: clippy-wasm
name: clippy (wasm32)
entry: cargo clippy --workspace --all-features --target wasm32-unknown-unknown --quiet -- -D warnings
language: system
types: [rust]
pass_filenames: false
stages: [pre-push]
- id: clippy-wasi
name: clippy (wasm32-wasip2)
entry: cargo clippy --workspace --all-features --target wasm32-wasip2 --quiet -- -D warnings
language: system
types: [rust]
pass_filenames: false
stages: [pre-push]
32 changes: 32 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Agent instructions

loko: services for Rust programs (`Clock`, `Random`, `Env`, `Console`, `Store`) passed to code as traits, with test,
system, and JavaScript implementations. One crate per service (`crates/loko-<service>`), the macros
(`crates/loko-macros`), and the `loko` crate, which re-exports them and adds the platforms.

## Before changing code

- Follow [CONTRIBUTING.md](CONTRIBUTING.md). It is the code guide, and it applies to you.
- For anything beyond a small fix, check the specs ([spec/README.md](spec/README.md)). Agree on the design with the user
before building something new, and record the decision in the spec's Decisions table.

## Rules

- **Done means checked.** Before you report work as finished, run `prek run --all-files --hook-stage pre-push` and
`cargo test --workspace --all-features`. Report failures as failures.
- **Fix the code, not the checks.** Don't loosen lint or format configs. Don't add `#[allow]`. Use
`#[expect(..., reason = "...")]` only when the tool is genuinely wrong.
- **Keep things in sync:** a change in behaviour updates its spec (status, design, history), the crate's README, the
tests, and `CHANGELOG.md` (under `Unreleased`) in the same commit.
- **The README's examples are doctests** (`ReadmeExamples` in `crates/loko/src/lib.rs`, with features `store` and
`tokio`). Keep them running: `cargo test -p loko --all-features --doc`.
- **Everything builds for WASM.** Every crate builds for `wasm32-unknown-unknown` and `wasm32-wasip2`. JavaScript
implementations are gated to `wasm32-unknown-unknown` and feature `js`; system ones to targets that have a system.
CI checks all three targets.
- **Every service is a `#[loko_macros::service]` trait** whose methods take `&self` and return `impl Future` without a
`Send` bound when they are async.
- **Test implementations are deterministic** and their clones share state.
- **Ideas, not code.** Never copy or translate another project's code. Credit ideas in `PRIOR_ART.md`
(CONTRIBUTING.md, "Credit").
- **Tests run offline.** No network, cloud accounts, or real buckets in tests.
- **Never commit secrets** or `.env*` files.
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Changelog

All notable changes to loko. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
versions follow [Semantic Versioning](https://semver.org/).

## Unreleased

The first release of loko as a services library. It is version 0.2.0 because the `loko` name on crates.io carries an
unrelated 0.1.0 from 2022 (task scripting), by the same owner.

### Added

- `loko-clock`: the `Clock` service (`now`, `now_ms`, `sleep`), `TestClock`, `StdClock`, `TokioClock` (feature
`tokio`), `JsClock` (feature `js`), and `OffsetClock`, a clock ahead or behind another.
- `loko-random`: the `Random` service, a source of `rand_core` generators (`rng()`, drawn from with no locks);
`SeededRandom` (`SeededRng`, SplitMix64, for tests), `OsRandom` (feature `getrandom`, `OsRng`), `JsRandom` (feature
`js`, `JsRng`), and `Random` for a `Mutex` around any seedable `rand_core` generator. The OS and JavaScript generators
are `CryptoRng`, so code that makes secrets can ask for `Random<Rng: CryptoRng>`.
- `loko-env`: the `Env` service (`var`, `vars`), `SystemEnv`, and `MapEnv`.
- `loko-console`: the `Console` service (`print_line`, `print_error_line`), `StdConsole`, `TestConsole`, and
`JsConsole` (feature `js`).
- `loko-store`: the `Store` service (objects by URI, with put-if-absent, multipart uploads, and prefix listing),
`MemoryStore`, an error type, and the wrappers `CountingStore` (counts calls), `ReadOnlyStore` (refuses writes), and
`PrefixedStore` (passes on only URIs under a prefix).
- `loko-object-store`: a `Store` over an `object_store` client, from a client or a URL.
- The service crates depend on nothing but `rand_core` (for `loko-random`); the macros are needed only to define
new services.
- `loko-macros`: `#[service]`, which adds a `HasX` trait to a service trait `X` and makes every `HasX` an `X`, and
`#[loko::bundle]`, which makes a struct each service its fields provide, and `#[wraps]`, which makes a wrapper
that replaces some of a service's methods and forwards the rest.
- `loko`: the services under one crate (`loko::clock`, …, `loko::store` with feature `store`), the platforms
`TestPlatform`, `StdPlatform`, `TokioPlatform`, and `JsPlatform`, and a prelude.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
Loading
Loading