General-purpose screen-recording CLI: wraps infrastructure-screencast's ffmpeg x11grab Recorder/RecorderConfig with real --output/--duration args, for ad-hoc 'record my current work session' use (Rule 14). The crate's prior consumer, demo-systats, hardcodes a fixed output path and ~25s duration for one demo and takes no CLI args; this tool is the reusable one. Omitting --duration records until Ctrl-C/SIGTERM; --output defaults to a timestamped ~/Videos file and never silently overwrites an existing one. --hotkeys switches to global-hotkey-driven start/pause/resume/stop (X11 only, via the global-hotkey crate) instead of an immediate, duration/Ctrl-C-bounded recording.
| Tier | tools |
| Role | unclassified (baselined) |
| Path | crates/tools/screen-record |
| Edition | 2021 |
| Targets | tools-screen-record, tools_screen_record |
| Public items | 11 across 0 modules |
| Tests | 19 |
What it is for
Pure, testable logic for tools-screen-record's CLI: output-path resolution (timestamped default so repeat runs never collide) and the stop-mode message printed to the operator at recording start.
Everything that actually touches ffmpeg/X11 -- Recorder::start, Recorder::finish -- lives in main.rs and is exercised only by a real run (a real display + a real ffmpeg), never by this crate's test suite: infrastructure_screencast::RecorderConfig::x11_default is the only way this tool builds ffmpeg's argv, so there is no hand-rolled capture logic here to test in the first place.
Capabilities
crate root
Pure, testable logic for tools-screen-record's CLI: output-path resolution
| Item |
|---|
fn next_action(state : RecordingState, press : HotkeyPress) ->(RecordingAction, RecordingState) |
fn notify_spec_for(action : RecordingAction) -> Option <NotifySpec> |
pub const DEFAULT_SUBDIR: & str |
fn default_output_path(home : & Path, now : DateTime <Utc>) -> PathBuf |
fn resolve_output(explicit : Option <PathBuf>, home : & Path, now : DateTime <Utc>) -> PathBuf |
fn ensure_output_available(path : & Path) -> Result <(), String> |
fn stop_mode_description(duration_secs : Option <u64>) -> String |
HotkeyPress
Pure, testable logic for tools-screen-record's CLI: output-path resolution
| Item |
|---|
pub enum HotkeyPress |
NotifySpec
Pure, testable logic for tools-screen-record's CLI: output-path resolution
| Item |
|---|
pub struct NotifySpec |
RecordingAction
Pure, testable logic for tools-screen-record's CLI: output-path resolution
| Item |
|---|
pub enum RecordingAction |
RecordingState
Pure, testable logic for tools-screen-record's CLI: output-path resolution
| Item |
|---|
pub enum RecordingState |
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
No public modules: the crate root is its whole surface.
Public surface
`crate root`
| Item | What it is |
|---|---|
pub enum RecordingState | Hotkey-controlled recording state |
pub enum HotkeyPress | A hotkey press, already resolved to its logical meaning (start / pause-or-resume / stop) — translating the raw global_hotkey event id into one of these three is main.rs's job; this type and next_action are the pure, testable state machine underneath it. |
pub enum RecordingAction | What main.rs should actually DO in response to a hotkey press — Ignore for every combination that doesn't make sense in the current state (e.g |
fn next_action(state : RecordingState, press : HotkeyPress) ->(RecordingAction, RecordingState) | The whole hotkey state machine, pure and total: every (state, press) pair maps to exactly one (action, next_state) |
pub struct NotifySpec | A desktop notification to fire for a RecordingAction — on-screen feedback that a hotkey press actually registered, not just the terminal println! (operator: recording is a background/hotkey-driven action with no other visible confirmation) |
fn notify_spec_for(action : RecordingAction) -> Option <NotifySpec> | The on-screen cue for a given action, or None for RecordingAction::Ignore -- a press that changed nothing gets no notification, only a real state transition does. |
pub const DEFAULT_SUBDIR: & str | Default recording directory under the user's home. |
fn default_output_path(home : & Path, now : DateTime <Utc>) -> PathBuf | Build the default output path: <home>/Videos/screen-record- <YYYY-MM-DD_HH-MM-SS>.mp4, UTC |
fn resolve_output(explicit : Option <PathBuf>, home : & Path, now : DateTime <Utc>) -> PathBuf | Resolve the effective output path: the operator's explicit --output if given, else the timestamped default. |
fn ensure_output_available(path : & Path) -> Result <(), String> | Pre-flight check that mirrors the refusal Recorder::start (via its own validate_config) would make anyway -- run early here so an explicit --output that already exists fails with a tool-specific message before this CLI touches ffmpeg or X11 at all |
fn stop_mode_description(duration_secs : Option <u64>) -> String | Human-readable description of how this run will stop, for the one-line status message printed at recording start |
No pub use re-exports: every item above is declared in this crate.
Boundary
Reaches into infrastructure.
Shares tier tools with 84 other crates: tools-advisory-reach, tools-archive-guard, tools-artifact-scaffold, tools-ask-ai-core, tools-ask-ais, tools-ask-gemini, tools-book, tools-book-report, … (84 total).
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | tools |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/tools/screen-record |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_tools["tools"] --> n_infrastructure["infrastructure"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `infrastructure-screencast` | infrastructure | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
anyhow | ^1 | — | no | always |
chrono | ^0.4 | serde | no | always |
clap | ^4 | derive | no | always |
ctrlc | ^3 | termination | no | always |
global-hotkey | ^0.8 | — | no | always |
Development, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `tools-cli-conformance` | tools | no | always |
Build. None.
Depended on by. Nothing in this workspace.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR SELF["tools-screen-record"] SELF -->|development| n_tools_cli_conformance["tools-cli-conformance"] SELF -->|runtime| n_infrastructure_screencast["infrastructure-screencast"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| bin | tools-screen-record | `src/main.rs` |
| lib | tools_screen_record | `src/lib.rs` |
Error model
No public error type was detected: no public item declares a type named *Error, and no public signature returns one.
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | none detected |
| async runtime | none detected |
| database access | none detected |
| network I/O | none detected |
| unsafe code | none detected |
| environment variables | yes |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
| Variable | Read in |
|---|---|
DISPLAY | src/main.rs |
HOME | src/main.rs |
XDG_SESSION_TYPE | src/main.rs |
Related capabilities
No workspace crate depends on this one.
Verification
| Kind | Count |
|---|---|
| Unit tests | 19 |
| Integration tests | 0 |
| Examples | 0 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
crate root | 11 | 0 | 0 |
What the tests establish, by name:
default_output_path_is_human_readable_and_timestamped_under_videos—src/lib.rsensure_output_available_allows_a_fresh_path—src/lib.rsensure_output_available_refuses_an_existing_file—src/lib.rsevery_real_action_has_a_spec—src/lib.rsignore_gets_no_notification—src/lib.rsnonsensical_presses_are_ignored_and_state_is_unchanged—src/lib.rspause_is_low_urgency_and_distinct_from_the_record_icon—src/lib.rspause_toggle_while_paused_resumes—src/lib.rspause_toggle_while_recording_pauses—src/lib.rsrecorder_config_x11_default_matches_the_expected_shape—src/lib.rsresolve_output_falls_back_to_the_timestamped_default—src/lib.rsresolve_output_prefers_an_explicit_path_over_the_default—src/lib.rsstart_and_resume_share_the_record_icon_and_a_short_fade—src/lib.rsstart_from_idle_begins_recording—src/lib.rsstop_has_its_own_icon_distinct_from_start_and_pause—src/lib.rsstop_mode_description_names_a_fixed_duration—src/lib.rsstop_mode_description_names_both_stop_signals_when_indefinite—src/lib.rsstop_while_recording_or_paused_goes_idle—src/lib.rscli_definition_conforms—src/main.rs
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 11 | 11 |
Public modules with a //! block | 0 | 0 |
pie showData
title Public items with rustdoc
"Documented" : 11
"No rustdoc detected" : 0
Metrics
| Metric | Value |
|---|---|
| Rust source files | 2 |
| Source lines | 840 |
| Code lines | 561 |
| Public API items | 11 |
| Public modules | 0 |
| Tests | 19 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 6 |
| Workspace reverse dependencies | 0 |
pie showData
title Public API by kind
"constant" : 1
"enum" : 3
"function" : 6
"struct" : 1
pie showData
title Rust source composition
"Code" : 561
"Blank or comment" : 279
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.