75 lines
6.4 KiB
Markdown
75 lines
6.4 KiB
Markdown
# StudIP Sync Agent
|
||
|
||
This document equips future agents with the current mental model for the `studip-sync` CLI so new work can focus on real gaps instead of rediscovering context.
|
||
|
||
## Mission & Constraints
|
||
|
||
- Goal: one-way sync of documents from Stud.IP’s JSON:API (Uni Trier) to the local filesystem using Rust (async `tokio`, `reqwest`, `serde`, TOML config/state).
|
||
- Target platform: Linux (Arch) following the XDG base directory spec.
|
||
- Binary name must stay `studip-sync`; code must stay `cargo fmt` + `cargo clippy --all-targets --all-features -- -D warnings` + `cargo test` clean.
|
||
- All configuration/state is TOML. The config file (mode `0600`) stores the Base64-encoded Basic auth string; state caches user/semester/course/file metadata.
|
||
- Directory layout requirement: `<download_root>/<semester_key>/<course>/<studip-folder-hierarchy>/<files>`. Never upload to Stud.IP; pruning is opt-in via `--prune`.
|
||
|
||
> **Rust edition note:** The crate currently targets Rust 2024 even though the original brief called for 2021. Keep this divergence in mind if MSRV compatibility matters.
|
||
|
||
## Repository Map
|
||
|
||
| Path | Purpose |
|
||
| --- | --- |
|
||
| `src/main.rs` | Minimal entry point that parses CLI args and drives async runtime. |
|
||
| `src/cli.rs` | All subcommand implementations plus sync logic, prompt helpers, pruning, naming utilities, and state updates. |
|
||
| `src/config.rs` | Multi-profile TOML config loader/saver; enforces 0600 perms on write. |
|
||
| `src/state.rs` | TOML cache schema for user/semesters/courses/files plus helpers to read/write/mutate per profile. |
|
||
| `src/paths.rs` | Resolves XDG-compliant config/data dirs with optional overrides. |
|
||
| `src/studip_client.rs` | Thin JSON:API client (Basic auth header, pagination helper, download streaming). |
|
||
| `src/semesters.rs` | Converts human semester titles (“WiSe 2024/25”) into stable keys (`ws2425`). |
|
||
| `src/logging.rs` | Tracing subscriber setup with quiet/debug/json/verbosity knobs. |
|
||
| `docs/studip/` | Offline copy of Stud.IP JSON:API docs for reference (no code). |
|
||
|
||
## Runtime Flow
|
||
|
||
1. `studip-sync init-config` writes a config template (optionally overriding `download_root` and guarded by `--force`).
|
||
2. `studip-sync auth` collects credentials (CL flags, env, or interactive prompts), Base64-encodes `username:password`, and persists it in the active profile.
|
||
3. `studip-sync list-courses` builds a `StudipClient`, resolves/caches the user ID via `/users/me`, paginates `/users/{id}/courses`, fetches missing semesters (including start/end timestamps and re-fetching cached semesters that lack them), upserts course metadata into `state.toml`, and prints a table sorted by semester/title.
|
||
4. `studip-sync sync`:
|
||
- Resolves download root (`config.download_root` or `$XDG_DATA_HOME/studip-sync/downloads`) and ensures directories exist unless `--dry-run`.
|
||
- Refreshes course + semester info, then for each course performs a depth-first walk: `/courses/{id}/folders` ➜ `/folders/{id}/file-refs` ➜ `/folders/{id}/folders`. Pagination is handled by `fetch_all_pages`.
|
||
- Normalizes path components and uses `NameRegistry` to avoid collisions, guaranteeing human-readable yet unique names.
|
||
- Checks file state (size, modified timestamp, checksum) against `state.toml` to skip unchanged files; downloads stream to `*.part` before rename.
|
||
- Records remote metadata + local path hints in state. `--dry-run` reports actions without touching disk; `--prune` (plus non–dry-run) deletes stray files/dirs with `walkdir`.
|
||
- `--since <semester|date>` now resolves either a semester key (e.g., `ws2526`) to its cached start timestamp or a date string (`DDMMYY`, `DDMMYYYY`, RFC3339) and skips remote files whose `chdate` precedes that cutoff.
|
||
5. HTTP errors propagate via `anyhow`, but 401/403 currently surface as generic failures—production UX should point users to `studip-sync auth`.
|
||
|
||
## Configuration & State
|
||
|
||
- Config path: `${XDG_CONFIG_HOME:-~/.config}/studip-sync/config.toml`. Override with `--config-dir` when needed. Example keys: `base_url`, `jsonapi_path`, `basic_auth_b64`, `download_root`, `max_concurrent_downloads`.
|
||
- State path: `${XDG_DATA_HOME:-~/.local/share}/studip-sync/state.toml`. `--data-dir` relocates this tree (and the default `downloads/` folder). Explicitly setting `download_root` decouples downloads from the data dir; otherwise it defaults to `<data-dir>/downloads`.
|
||
- `profiles.<name>.user_id` caches `/users/me`.
|
||
- `profiles.<name>.semesters.<key>` stores semester IDs/titles/keys plus `start`/`end` timestamps (needed for `--since ws2526`). Running `list-courses --refresh` will also refresh already-known semesters missing those timestamps so `sync --since …` can resolve properly.
|
||
- `profiles.<name>.courses.<id>` keeps display names + `last_sync`.
|
||
- `profiles.<name>.files.<file_ref_id>` remembers size, checksum, timestamps, and the last local path to avoid redundant downloads.
|
||
- Multiple profiles are supported; `--profile` switches, otherwise the config’s `default_profile` is used.
|
||
|
||
## Development Workflow
|
||
|
||
1. Install a recent Rust toolchain (`rustup toolchain install stable` if needed).
|
||
2. Lint/test loop:
|
||
```bash
|
||
cargo fmt --all -- --check
|
||
cargo clippy --all-targets --all-features -- -D warnings
|
||
cargo test
|
||
```
|
||
3. Use `cargo run -- <subcommand>` for manual verification (e.g., `cargo run -- auth`, `cargo run -- list-courses --refresh`, `cargo run -- sync --dry-run`).
|
||
4. Keep dependencies minimal; avoid logging sensitive strings (`basic_auth_b64`, plaintext passwords).
|
||
|
||
## Known Gaps / Backlog
|
||
|
||
- `ConfigProfile::max_concurrent_downloads` is defined but unused; downloads happen sequentially. Introduce a bounded task queue if concurrency is needed.
|
||
- Server-side filtering (`filter[since]`, if supported) is not used yet. Local cutoff logic relies on Stud.IP timestamps; future work could add conditional API query parameters once officially documented.
|
||
- No automated tests (unit/integration) are present; critical helpers like `semesters::infer_key`, `normalize_component`, and state transitions should gain coverage.
|
||
- Error UX for auth failures could be clearer (detect 401/403 and prompt users to re-run `auth`).
|
||
- There is no CI config; if one is added, ensure it runs fmt/clippy/test.
|
||
- Verify long-term compatibility with Rust 2024 or document the minimum supported version explicitly.
|
||
|
||
Keep this guide updated whenever major flow or architecture changes land so that future agents can jump straight into implementation work.
|