6.9 KiB
6.9 KiB
StudIP Sync Agent
Goal
Implement a command-line tool in Rust that performs a one-way sync of files from Stud.IP (JSON:API at Uni Trier) to the local filesystem. [web:68]
The local directory structure must be: <semester>/<course>/<studip-folders>/<files>. [web:88]
Environment
- Target OS: Linux (Arch, follow XDG base directory conventions). [web:135]
- Language: Rust (2021 edition).
- Use
reqwest+tokiofor HTTP and async,serdefor JSON and TOML, and standard Rust CLI patterns. [web:111][web:131] - Build as a single binary named
studip-sync.
Code Quality: Formatting and Linting
- Use
rustfmtas the standard formatter for all Rust code; code must be keptcargo fmtclean. [web:144][web:148] - Use
clippyas the linter; the project must passcargo clippy --all-targets --all-features -- -D warningswith no warnings. [web:144][web:149][web:159] - Add a
rustfmt.tomland (optionally) aclippy.tomlwhere needed, but prefer default settings to stay idiomatic. [web:144][web:151] - If CI is present, include steps that run
cargo fmt --all -- --check,cargo clippy --all-targets --all-features -- -D warnings, andcargo test. [web:147][web:150][web:159]
API
- Base URL: configurable, default
https://studip.uni-trier.de. [web:68] - JSON:API root:
<base_url>/jsonapi.php/v1. [web:68] - Authentication: HTTP Basic (username/password), encoded once as base64 and stored in TOML config. [web:1][web:118]
- Use JSON:API routes such as:
GET /users/meto resolve the current user and related links (courses, folders, file-refs). [web:68][web:106]GET /users/{user_id}/coursesto list enrolled courses. [web:85]- Course-specific routes for folders and documents/file-refs, using the documented JSON:API routes for Stud.IP (e.g.
/courses/{course_id}/documents). [web:88][web:93]
Configuration (TOML, including paths)
All configuration and state in this project must use TOML. [web:131]
- Primary config file: XDG-compliant, e.g.
~/.config/studip-sync/config.toml. [web:131][web:135] - Example
config.tomlkeys:
base_url = "https://studip.uni-trier.de"
jsonapi_path = "/jsonapi.php/v1"
# Authorization header value without the "Basic " prefix, base64("username:password").
basic_auth_b64 = "..."
# Local base directory for synced files.
download_root = "/home/<user>/StudIP"
# Maximum concurrent HTTP downloads.
max_concurrent_downloads = 3
- The
download_rootdirectory determines where the tool createssemester/course/folders/files. [web:68] - The config file must be created with mode
0600and never contain anything except necessary settings and the base64-encoded credential. [web:118][web:122]
Credentials and auth
- On first run (or when running
studip-sync auth), prompt interactively for username and password. [web:118] - Construct
username:password, base64-encode it, and store the result asbasic_auth_b64inconfig.toml. [web:1][web:118] - At runtime, send
Authorization: Basic <basic_auth_b64>on all JSON:API requests. [web:1][web:68] - Never log or print the password,
basic_auth_b64, or fullAuthorizationheader. [web:118][web:128] - On HTTP
401or403from a known-good endpoint like/users/me, treat this as auth failure: - Non-interactive runs: exit with a non-zero code and a clear message asking the user to run
studip-sync auth. [web:118] - Interactive runs: optionally prompt again and update
basic_auth_b64.
State (TOML as well)
- State file must also be TOML, stored under XDG data dir, e.g.
~/.local/share/studip-sync/state.toml. [web:131][web:135] - State is non-secret cached data:
user_id = "cbcee42edfea9232fecc3e414ef79d06"
[semesters."ws2526"]
id = "830eb86ad41d8f695d016647d557218a"
title = "Wintersemester 2025/26"
[semesters."ss25"]
id = "..."
title = "Sommersemester 2025"
[courses."830eb86a-...-course-id"]
name = "Rechnerstrukturen - Übung"
semester_key = "ws2526"
last_sync = "2025-11-14T12:34:56Z"
- The tool should:
- Cache
user_idafter the first successful/users/mecall. [web:68][web:106] - Cache semester IDs and human-readable keys (
ws2526,ss25) after discovering them via JSON:API. [web:68] - Optionally store course and last-sync metadata to reduce API calls (e.g. using
filter[since]if supported). [web:88][web:93]
Directory structure
- All downloads must go under
download_root, respecting:download_root/<semester_key>/<course_name>/<studip_folder_path>/<file>. semester_keyis resolved from the state file (ws2526,ss25, etc.). [web:68]course_nameand Stud.IP folder/file names should be normalized to safe filesystem paths (handle spaces, umlauts, and special characters) while staying human-readable. [web:68][web:104]
Sync semantics
- One-way sync: Stud.IP → local filesystem only; never upload or modify data on Stud.IP. [web:68]
- Default behavior:
- Create directories and download new or changed files under
download_root. - Never delete local files by default.
- Provide optional flags:
--prune: delete local files that no longer exist on Stud.IP.--dry-run: print planned actions (creates/downloads/deletes) without modifying the filesystem.
Minimizing API usage and load
- Use cached
user_idand semester mappings fromstate.tomlto avoid repeated discovery calls. [web:68] - When listing course documents, use JSON:API pagination and any available filters (e.g.
filter[since]) supported by Stud.IP’s document routes. [web:88][web:93] - Avoid re-downloading unchanged files by checking JSON:API attributes such as ID, size, and modification time against the stored state. [web:93][web:106]
CLI interface
- Binary name:
studip-sync. - Subcommands:
studip-sync auth: set or update credentials; writesconfig.toml.studip-sync sync: perform sync from Stud.IP todownload_root.studip-sync list-courses: list known courses with semester keys and IDs from state (refreshing if needed).- Use standard exit codes:
0on success.- Non-zero on errors (auth failure, network error, JSON parse error, filesystem failure). [web:118]
Performance & safety
- Limit concurrent HTTP requests (configurable via
max_concurrent_downloads, default 3). [web:68] - Stream file downloads directly to disk; do not load entire files into memory. [web:88]
- Handle HTTP and I/O errors gracefully with clear messages and without panicking.
- Keep dependencies minimal and use idiomatic Rust project structuring for maintainability. [web:136][web:137]
Extensibility
- Internally, separate concerns into modules:
config(TOML load/save for config and state).studip_client(JSON:API HTTP client).sync(sync logic and directory mapping).cli(argument parsing, subcommands). [web:136][web:137]- Represent core entities as Rust types:
Semester,Course,Folder,FileRef. [web:68][web:93] - Design so that a future
MoodleProvidercan implement the same internal traits (e.g.LmsProvider) without changing the CLI surface.