Use common command/args syntax

This commit is contained in:
Albert Armea 2025-12-28 00:35:33 -05:00
parent 1238987732
commit 336a8f7eb2
10 changed files with 68 additions and 30 deletions

View file

@ -41,7 +41,8 @@ icon = "scummvm"
[entries.kind] [entries.kind]
type = "process" type = "process"
argv = ["scummvm", "-f"] command = "scummvm"
args = ["-f"]
[entries.availability] [entries.availability]
[[entries.availability.windows]] [[entries.availability.windows]]
@ -95,7 +96,7 @@ seconds_before = 30
severity = "critical" severity = "critical"
message = "30 seconds! Save NOW!" message = "30 seconds! Save NOW!"
# Example: Educational game - unrestricted # Example: Educational game
[[entries]] [[entries]]
id = "tuxmath" id = "tuxmath"
label = "Tux Math" label = "Tux Math"
@ -103,7 +104,21 @@ icon = "tuxmath"
[entries.kind] [entries.kind]
type = "process" type = "process"
argv = ["tuxmath"] command = "tuxmath"
[entries.availability]
always = true
# Example: Steam game
[[entries]]
id = "steam-human-resource-machine"
label = "Human Resource Machine"
icon = "steam"
[entries.kind]
type = "snap"
snap_name = "steam"
args = ["steam://rungameid/375820"] # Steam App ID (passed to 'snap run steam')
[entries.availability] [entries.availability]
always = true always = true
@ -119,7 +134,8 @@ icon = "mpv"
[entries.kind] [entries.kind]
type = "process" type = "process"
argv = ["mpv", "https://www.youtube.com/watch?v=jfKfPfyJRdk"] command = "mpv"
args = ["https://www.youtube.com/watch?v=jfKfPfyJRdk"]
[entries.availability] [entries.availability]
always = true always = true
@ -138,4 +154,4 @@ disabled_reason = "This game is being updated"
[entries.kind] [entries.kind]
type = "process" type = "process"
argv = ["/bin/false"] command = "/bin/false"

View file

@ -23,7 +23,11 @@ pub enum EntryKindTag {
#[serde(tag = "type", rename_all = "snake_case")] #[serde(tag = "type", rename_all = "snake_case")]
pub enum EntryKind { pub enum EntryKind {
Process { Process {
argv: Vec<String>, /// Command to run (required)
command: String,
/// Additional command-line arguments
#[serde(default)]
args: Vec<String>,
#[serde(default)] #[serde(default)]
env: HashMap<String, String>, env: HashMap<String, String>,
cwd: Option<PathBuf>, cwd: Option<PathBuf>,
@ -252,7 +256,8 @@ mod tests {
#[test] #[test]
fn entry_kind_serialization() { fn entry_kind_serialization() {
let kind = EntryKind::Process { let kind = EntryKind::Process {
argv: vec!["scummvm".into(), "-f".into()], command: "scummvm".into(),
args: vec!["-f".into()],
env: HashMap::new(), env: HashMap::new(),
cwd: None, cwd: None,
}; };

View file

@ -75,7 +75,7 @@ mod tests {
[[entries]] [[entries]]
id = "test-game" id = "test-game"
label = "Test Game" label = "Test Game"
kind = { type = "process", argv = ["/usr/bin/game"] } kind = { type = "process", command = "/usr/bin/game" }
"#; "#;
let policy = parse_config(config).unwrap(); let policy = parse_config(config).unwrap();
@ -91,7 +91,7 @@ mod tests {
[[entries]] [[entries]]
id = "test" id = "test"
label = "Test" label = "Test"
kind = { type = "process", argv = ["/bin/test"] } kind = { type = "process", command = "/bin/test" }
"#; "#;
let result = parse_config(config); let result = parse_config(config);

View file

@ -194,7 +194,7 @@ pub struct LimitsPolicy {
fn convert_entry_kind(raw: RawEntryKind) -> EntryKind { fn convert_entry_kind(raw: RawEntryKind) -> EntryKind {
match raw { match raw {
RawEntryKind::Process { argv, env, cwd } => EntryKind::Process { argv, env, cwd }, RawEntryKind::Process { command, args, env, cwd } => EntryKind::Process { command, args, env, cwd },
RawEntryKind::Snap { snap_name, command, args, env } => EntryKind::Snap { snap_name, command, args, env }, RawEntryKind::Snap { snap_name, command, args, env } => EntryKind::Snap { snap_name, command, args, env },
RawEntryKind::Vm { driver, args } => EntryKind::Vm { driver, args }, RawEntryKind::Vm { driver, args } => EntryKind::Vm { driver, args },
RawEntryKind::Media { library_id, args } => EntryKind::Media { library_id, args }, RawEntryKind::Media { library_id, args } => EntryKind::Media { library_id, args },

View file

@ -78,7 +78,11 @@ pub struct RawEntry {
#[serde(tag = "type", rename_all = "snake_case")] #[serde(tag = "type", rename_all = "snake_case")]
pub enum RawEntryKind { pub enum RawEntryKind {
Process { Process {
argv: Vec<String>, /// Command to run (required)
command: String,
/// Additional command-line arguments
#[serde(default)]
args: Vec<String>,
#[serde(default)] #[serde(default)]
env: HashMap<String, String>, env: HashMap<String, String>,
cwd: Option<PathBuf>, cwd: Option<PathBuf>,
@ -189,7 +193,7 @@ mod tests {
[[entries]] [[entries]]
id = "scummvm" id = "scummvm"
label = "ScummVM" label = "ScummVM"
kind = { type = "process", argv = ["scummvm", "-f"] } kind = { type = "process", command = "scummvm", args = ["-f"] }
[entries.limits] [entries.limits]
max_run_seconds = 3600 max_run_seconds = 3600
@ -208,7 +212,7 @@ mod tests {
[[entries]] [[entries]]
id = "game" id = "game"
label = "Game" label = "Game"
kind = { type = "process", argv = ["/bin/game"] } kind = { type = "process", command = "/bin/game" }
[entries.availability] [entries.availability]
[[entries.availability.windows]] [[entries.availability.windows]]

View file

@ -55,11 +55,11 @@ fn validate_entry(entry: &RawEntry, config: &RawConfig) -> Vec<ValidationError>
// Validate kind // Validate kind
match &entry.kind { match &entry.kind {
RawEntryKind::Process { argv, .. } => { RawEntryKind::Process { command, .. } => {
if argv.is_empty() { if command.is_empty() {
errors.push(ValidationError::EntryError { errors.push(ValidationError::EntryError {
entry_id: entry.id.clone(), entry_id: entry.id.clone(),
message: "argv cannot be empty".into(), message: "command cannot be empty".into(),
}); });
} }
} }
@ -252,7 +252,8 @@ mod tests {
label: "Game 1".into(), label: "Game 1".into(),
icon: None, icon: None,
kind: RawEntryKind::Process { kind: RawEntryKind::Process {
argv: vec!["game1".into()], command: "game1".into(),
args: vec![],
env: Default::default(), env: Default::default(),
cwd: None, cwd: None,
}, },
@ -267,7 +268,8 @@ mod tests {
label: "Game 2".into(), label: "Game 2".into(),
icon: None, icon: None,
kind: RawEntryKind::Process { kind: RawEntryKind::Process {
argv: vec!["game2".into()], command: "game2".into(),
args: vec![],
env: Default::default(), env: Default::default(),
cwd: None, cwd: None,
}, },

View file

@ -559,7 +559,8 @@ mod tests {
label: "Test Game".into(), label: "Test Game".into(),
icon_ref: None, icon_ref: None,
kind: EntryKind::Process { kind: EntryKind::Process {
argv: vec!["game".into()], command: "game".into(),
args: vec![],
env: HashMap::new(), env: HashMap::new(),
cwd: None, cwd: None,
}, },
@ -635,7 +636,8 @@ mod tests {
label: "Test".into(), label: "Test".into(),
icon_ref: None, icon_ref: None,
kind: EntryKind::Process { kind: EntryKind::Process {
argv: vec!["test".into()], command: "test".into(),
args: vec![],
env: HashMap::new(), env: HashMap::new(),
cwd: None, cwd: None,
}, },
@ -697,7 +699,8 @@ mod tests {
label: "Test".into(), label: "Test".into(),
icon_ref: None, icon_ref: None,
kind: EntryKind::Process { kind: EntryKind::Process {
argv: vec!["test".into()], command: "test".into(),
args: vec![],
env: HashMap::new(), env: HashMap::new(),
cwd: None, cwd: None,
}, },

View file

@ -189,7 +189,8 @@ mod tests {
let session_id = SessionId::new(); let session_id = SessionId::new();
let entry = EntryKind::Process { let entry = EntryKind::Process {
argv: vec!["test".into()], command: "test".into(),
args: vec![],
env: HashMap::new(), env: HashMap::new(),
cwd: None, cwd: None,
}; };
@ -217,7 +218,8 @@ mod tests {
let session_id = SessionId::new(); let session_id = SessionId::new();
let entry = EntryKind::Process { let entry = EntryKind::Process {
argv: vec!["test".into()], command: "test".into(),
args: vec![],
env: HashMap::new(), env: HashMap::new(),
cwd: None, cwd: None,
}; };

View file

@ -115,8 +115,10 @@ impl HostAdapter for LinuxHost {
) -> HostResult<HostSessionHandle> { ) -> HostResult<HostSessionHandle> {
// Extract argv, env, cwd, and snap_name based on entry kind // Extract argv, env, cwd, and snap_name based on entry kind
let (argv, env, cwd, snap_name) = match entry_kind { let (argv, env, cwd, snap_name) = match entry_kind {
EntryKind::Process { argv, env, cwd } => { EntryKind::Process { command, args, env, cwd } => {
(argv.clone(), env.clone(), cwd.clone(), None) let mut argv = vec![command.clone()];
argv.extend(args.clone());
(argv, env.clone(), cwd.clone(), None)
} }
EntryKind::Snap { snap_name, command, args, env } => { EntryKind::Snap { snap_name, command, args, env } => {
// For snap apps, we need to use 'snap run <snap_name>' to launch them. // For snap apps, we need to use 'snap run <snap_name>' to launch them.
@ -318,7 +320,8 @@ mod tests {
let session_id = SessionId::new(); let session_id = SessionId::new();
let entry = EntryKind::Process { let entry = EntryKind::Process {
argv: vec!["true".into()], command: "true".into(),
args: vec![],
env: HashMap::new(), env: HashMap::new(),
cwd: None, cwd: None,
}; };
@ -348,7 +351,8 @@ mod tests {
let session_id = SessionId::new(); let session_id = SessionId::new();
let entry = EntryKind::Process { let entry = EntryKind::Process {
argv: vec!["sleep".into(), "60".into()], command: "sleep".into(),
args: vec!["60".into()],
env: HashMap::new(), env: HashMap::new(),
cwd: None, cwd: None,
}; };

View file

@ -22,7 +22,8 @@ fn make_test_policy() -> Policy {
label: "Test Game".into(), label: "Test Game".into(),
icon_ref: None, icon_ref: None,
kind: EntryKind::Process { kind: EntryKind::Process {
argv: vec!["sleep".into(), "999".into()], command: "sleep".into(),
args: vec!["999".into()],
env: HashMap::new(), env: HashMap::new(),
cwd: None, cwd: None,
}, },
@ -227,7 +228,8 @@ async fn test_mock_host_integration() {
let session_id = SessionId::new(); let session_id = SessionId::new();
let entry = EntryKind::Process { let entry = EntryKind::Process {
argv: vec!["test".into()], command: "test".into(),
args: vec![],
env: HashMap::new(), env: HashMap::new(),
cwd: None, cwd: None,
}; };
@ -262,7 +264,7 @@ fn test_config_parsing() {
[[entries]] [[entries]]
id = "scummvm" id = "scummvm"
label = "ScummVM" label = "ScummVM"
kind = { type = "process", argv = ["scummvm", "-f"] } kind = { type = "process", command = "scummvm", args = ["-f"] }
[entries.availability] [entries.availability]
[[entries.availability.windows]] [[entries.availability.windows]]