5.4 KiB
5.4 KiB
studip-sync
studip-sync is a Rust CLI that performs a one-way sync of Stud.IP course materials (via the Uni Trier JSON:API) into a local directory tree. The tool persists config/state in TOML, talks to the API with HTTP Basic auth, and keeps the local filesystem organized as <download_root>/<semester>/<course>/<folder>/<files>.
Key Features
authsubcommand stores Base64-encoded credentials per profile (passwords are never logged).list-coursesfetches/users/me, paginates enrolled courses, infers semester keys, caches the metadata, and prints a concise table.synctraverses every course folder/file tree, normalizes names, streams downloads to disk, tracks checksums/remote timestamps, and supports--dry-runplus--pruneto delete orphaned files.- XDG-compliant config (
~/.config/studip-sync/config.toml) and state (~/.local/share/studip-sync/state.toml) stores everything in TOML. - Extensive logging controls:
--quiet,--verbose/-v,--debug, and--json.
Directory Layout & Data Files
- Config lives under
${XDG_CONFIG_HOME:-~/.config}/studip-sync/config.toml. Adefaultprofile is created automatically and stores thebasic_auth_b64, base URL, JSON:API path, download root, etc. - State is cached in
${XDG_DATA_HOME:-~/.local/share}/studip-sync/state.tomlwith per-profile sections for user/semester/course/file metadata. - Downloads default to
${XDG_DATA_HOME:-~/.local/share}/studip-sync/downloads, but you can overridedownload_rootin the config to point anywhere else. Each path segment is sanitized to keep names human-readable yet filesystem-safe.
Getting Started
- Prerequisites – Install a recent Rust toolchain (Rust 1.75+ recommended) and ensure you can reach
https://studip.uni-trier.de. - Build & validate – From the repo root run:
cargo fmt --all -- --check cargo clippy --all-targets --all-features -- -D warnings cargo test - First run:
Use
# Store credentials (prompts for username/password by default) cargo run -- auth # Inspect courses and cache semester data cargo run -- list-courses --refresh # Perform a dry-run sync to see planned actions cargo run -- sync --dry-run # Run the real sync (omit --dry-run); add --prune to delete stray files cargo run -- sync --prune--profile,--config-dir, or--data-dirwhen working with multiple identities or non-standard paths.
Configuration Reference
Example config.toml:
default_profile = "default"
[profiles.default]
base_url = "https://studip.uni-trier.de"
jsonapi_path = "/jsonapi.php/v1"
basic_auth_b64 = "base64(username:password)"
download_root = "/home/alex/StudIP"
max_concurrent_downloads = 3 # placeholder for future concurrency control
- The file is written with
0600permissions. Never commit credentials—authmanages them interactively or through--username/--password/STUDIP_SYNC_USERNAME|PASSWORD. - Multiple profiles can be added under
[profiles.<name>]; pass--profile <name>when invoking the CLI to switch.
CLI Reference
| Subcommand | Description | Helpful flags |
|---|---|---|
auth |
Collect username/password, encode them, and save them to the active profile. | --non-interactive, --username, --password |
list-courses |
List cached or freshly fetched courses with semester keys and IDs. | --refresh |
sync |
Download files for every enrolled course into the local tree. | --dry-run, --prune, --since (reserved for future API filters) |
Global flags: --quiet, --debug, --json, -v/--verbose (stackable), --config-dir, --data-dir, --profile.
Sync Behavior
- Resolve user ID (cached in
state.toml) and fetch current courses. - Cache missing semesters via
/semesters/{id}and infer keys likews2425/ss25. - For each course:
- Walk folders using the JSON:API pagination helpers; fetch nested folders via
/folders/{id}/folders. - List file refs via
/folders/{id}/file-refs, normalize filenames, and ensure unique siblings through aNameRegistry. - Skip downloads when the local file exists and matches the stored checksum / size / remote
chdate. - Stream downloads to
*.part, hash contents on the fly, then rename atomically to the final path.
- Walk folders using the JSON:API pagination helpers; fetch nested folders via
- Maintain a set of remote files so
--prunecan remove local files that no longer exist remotely (and optionally delete now-empty directories). --dry-runprints planned work but never writes to disk.
Development Notes
- The HTTP client limits itself to GETs with Basic auth; non-success responses are surfaced verbatim via
anyhow. - All downloads currently run sequentially;
ConfigProfile::max_concurrent_downloadsis in place for a future bounded task executor. - Offline JSON:API documentation lives under
docs/studip/to keep this repo usable without network access.
Roadmap / Known Gaps
- Implement real concurrent downloads that honor
max_concurrent_downloads. - Wire
--sinceinto Stud.IP filters (if available) or local heuristics to reduce API load. - Add unit/integration tests (
semesters::infer_key, naming helpers, pruning) and consider fixtures for Stud.IP responses. - Improve auth failure UX by detecting 401/403 and prompting the user to re-run
studip-sync auth. - Evaluate whether the crate should target Rust 2021 (per the original requirement) or explicitly document Rust 2024 as the minimum supported version.