1
0
Fork 0
forked from wry/wry

toml: add exec.shell

This commit is contained in:
Julian Orth 2025-09-19 23:05:43 +02:00
parent b25e7554f7
commit 325f4ea71b
4 changed files with 100 additions and 17 deletions

View file

@ -16,6 +16,7 @@ use {
}, },
}, },
indexmap::IndexMap, indexmap::IndexMap,
std::sync::LazyLock,
thiserror::Error, thiserror::Error,
}; };
@ -31,6 +32,12 @@ pub enum ExecParserError {
Env(#[from] EnvParserError), Env(#[from] EnvParserError),
#[error("Array cannot be empty")] #[error("Array cannot be empty")]
Empty, Empty,
#[error("Exactly one of the `prog` or `shell` fields must be specified")]
ProgXorShell,
#[error("Could not read $SHELL")]
ShellNotDefined,
#[error("The `args` field cannot be used for shell commands")]
ArgsForShell,
} }
pub struct ExecParser<'a>(pub &'a Context<'a>); pub struct ExecParser<'a>(pub &'a Context<'a>);
@ -72,16 +79,33 @@ impl Parser for ExecParser<'_> {
table: &IndexMap<Spanned<String>, Spanned<Value>>, table: &IndexMap<Spanned<String>, Spanned<Value>>,
) -> ParseResult<Self> { ) -> ParseResult<Self> {
let mut ext = Extractor::new(self.0, span, table); let mut ext = Extractor::new(self.0, span, table);
let (prog, args_val, envs_val, privileged) = ext.extract(( let (prog_opt, shell_opt, args_val, envs_val, privileged) = ext.extract((
str("prog"), opt(str("prog")),
opt(str("shell")),
opt(arr("args")), opt(arr("args")),
opt(val("env")), opt(val("env")),
recover(opt(bol("privileged"))), recover(opt(bol("privileged"))),
))?; ))?;
let prog;
let mut args = vec![]; let mut args = vec![];
if let Some(args_val) = args_val { match (prog_opt, shell_opt) {
for arg in args_val.value { (None, None) | (Some(_), Some(_)) => {
args.push(arg.parse_map(&mut StringParser)?); return Err(ExecParserError::ProgXorShell.spanned(span));
}
(Some(v), _) => {
prog = v.value.to_string();
if let Some(args_val) = args_val {
for arg in args_val.value {
args.push(arg.parse_map(&mut StringParser)?);
}
}
}
(_, Some(v)) => {
prog = shell(v.span)?;
args = vec!["-c".to_string(), v.value.to_string()];
if let Some(v) = args_val {
return Err(ExecParserError::ArgsForShell.spanned(v.span));
}
} }
} }
let envs = match envs_val { let envs = match envs_val {
@ -89,10 +113,17 @@ impl Parser for ExecParser<'_> {
Some(e) => e.parse_map(&mut EnvParser)?, Some(e) => e.parse_map(&mut EnvParser)?,
}; };
Ok(Exec { Ok(Exec {
prog: prog.value.to_string(), prog,
args, args,
envs, envs,
privileged: privileged.despan().unwrap_or(false), privileged: privileged.despan().unwrap_or(false),
}) })
} }
} }
fn shell(span: Span) -> Result<String, Spanned<ExecParserError>> {
static SHELL: LazyLock<Option<String>> = LazyLock::new(|| std::env::var("SHELL").ok());
SHELL
.clone()
.ok_or(ExecParserError::ShellNotDefined.spanned(span))
}

View file

@ -1200,7 +1200,7 @@
] ]
}, },
"Exec": { "Exec": {
"description": "Describes how to execute a program.\n\n- Example 1:\n\n ```toml\n [shortcuts]\n ctrl-a = { type = \"exec\", exec = \"alacritty\" }\n ```\n\n- Example 2:\n\n ```toml\n [shortcuts]\n ctrl-a = { type = \"exec\", exec = [\"notify-send\", \"hello world\"] }\n ```\n\n- Example 3:\n\n ```toml\n [shortcuts]\n ctrl-a = { type = \"exec\", exec = { prog = \"notify-send\", args = [\"hello world\"], env.WAYLAND_DISPLAY = \"2\" } }\n ```\n", "description": "Describes how to execute a program.\n\n- Example 1:\n\n ```toml\n [shortcuts]\n ctrl-a = { type = \"exec\", exec = \"alacritty\" }\n ```\n\n- Example 2:\n\n ```toml\n [shortcuts]\n ctrl-a = { type = \"exec\", exec = [\"notify-send\", \"hello world\"] }\n ```\n\n- Example 3:\n\n ```toml\n [shortcuts]\n ctrl-a = { type = \"exec\", exec = { prog = \"notify-send\", args = [\"hello world\"], env.WAYLAND_DISPLAY = \"2\" } }\n ```\n\n- Example 4:\n\n ```toml\n [shortcuts]\n ctrl-a = { type = \"exec\", exec = { shell = \"grim - | wl-copy\", privileged = true } }\n ```\n",
"anyOf": [ "anyOf": [
{ {
"type": "string", "type": "string",
@ -1215,16 +1215,20 @@
} }
}, },
{ {
"description": "The name, arguments, and environment variables of the executable to execute.\n\n- Example:\n\n ```toml\n [shortcuts]\n ctrl-a = { type = \"exec\", exec = { prog = \"notify-send\", args = [\"hello world\"], env.WAYLAND_DISPLAY = \"2\" } }\n ```\n", "description": "The name, arguments, and environment variables of the executable to execute.\n\nExactly one of the `prog` or `shell` fields must be specified.\n\n- Example 1:\n\n ```toml\n [shortcuts]\n ctrl-a = { type = \"exec\", exec = { prog = \"notify-send\", args = [\"hello world\"], env.WAYLAND_DISPLAY = \"2\" } }\n ```\n\n- Example 2:\n \n ```toml\n [shortcuts]\n ctrl-a = { type = \"exec\", exec = { shell = \"grim - | wl-copy\", privileged = true } }\n ```\n",
"type": "object", "type": "object",
"properties": { "properties": {
"prog": { "prog": {
"type": "string", "type": "string",
"description": "The name of the executable." "description": "The name of the executable."
}, },
"shell": {
"type": "string",
"description": "The name of a shell command to execute. The command will be executed as\n`$SHELL -c \"command\"`.\n"
},
"args": { "args": {
"type": "array", "type": "array",
"description": "The arguments to pass to the executable.", "description": "The arguments to pass to the executable.\n\nThis field must not be specified if a shell command is used.\n",
"items": { "items": {
"type": "string", "type": "string",
"description": "" "description": ""
@ -1243,9 +1247,7 @@
"description": "If `true`, the executable gets access to privileged wayland protocols.\n\nThe default is `false`.\n" "description": "If `true`, the executable gets access to privileged wayland protocols.\n\nThe default is `false`.\n"
} }
}, },
"required": [ "required": []
"prog"
]
} }
] ]
}, },

View file

@ -2483,6 +2483,13 @@ Describes how to execute a program.
ctrl-a = { type = "exec", exec = { prog = "notify-send", args = ["hello world"], env.WAYLAND_DISPLAY = "2" } } ctrl-a = { type = "exec", exec = { prog = "notify-send", args = ["hello world"], env.WAYLAND_DISPLAY = "2" } }
``` ```
- Example 4:
```toml
[shortcuts]
ctrl-a = { type = "exec", exec = { shell = "grim - | wl-copy", privileged = true } }
```
Values of this type should have one of the following forms: Values of this type should have one of the following forms:
#### A string #### A string
@ -2513,24 +2520,42 @@ Each element of this array should be a string.
The name, arguments, and environment variables of the executable to execute. The name, arguments, and environment variables of the executable to execute.
- Example: Exactly one of the `prog` or `shell` fields must be specified.
- Example 1:
```toml ```toml
[shortcuts] [shortcuts]
ctrl-a = { type = "exec", exec = { prog = "notify-send", args = ["hello world"], env.WAYLAND_DISPLAY = "2" } } ctrl-a = { type = "exec", exec = { prog = "notify-send", args = ["hello world"], env.WAYLAND_DISPLAY = "2" } }
``` ```
- Example 2:
```toml
[shortcuts]
ctrl-a = { type = "exec", exec = { shell = "grim - | wl-copy", privileged = true } }
```
The table has the following fields: The table has the following fields:
- `prog` (required): - `prog` (optional):
The name of the executable. The name of the executable.
The value of this field should be a string. The value of this field should be a string.
- `shell` (optional):
The name of a shell command to execute. The command will be executed as
`$SHELL -c "command"`.
The value of this field should be a string.
- `args` (optional): - `args` (optional):
The arguments to pass to the executable. The arguments to pass to the executable.
This field must not be specified if a shell command is used.
The value of this field should be an array of strings. The value of this field should be an array of strings.

View file

@ -733,6 +733,13 @@ Exec:
[shortcuts] [shortcuts]
ctrl-a = { type = "exec", exec = { prog = "notify-send", args = ["hello world"], env.WAYLAND_DISPLAY = "2" } } ctrl-a = { type = "exec", exec = { prog = "notify-send", args = ["hello world"], env.WAYLAND_DISPLAY = "2" } }
``` ```
- Example 4:
```toml
[shortcuts]
ctrl-a = { type = "exec", exec = { shell = "grim - | wl-copy", privileged = true } }
```
kind: variable kind: variable
variants: variants:
- kind: string - kind: string
@ -760,24 +767,42 @@ Exec:
- kind: table - kind: table
description: | description: |
The name, arguments, and environment variables of the executable to execute. The name, arguments, and environment variables of the executable to execute.
Exactly one of the `prog` or `shell` fields must be specified.
- Example: - Example 1:
```toml ```toml
[shortcuts] [shortcuts]
ctrl-a = { type = "exec", exec = { prog = "notify-send", args = ["hello world"], env.WAYLAND_DISPLAY = "2" } } ctrl-a = { type = "exec", exec = { prog = "notify-send", args = ["hello world"], env.WAYLAND_DISPLAY = "2" } }
``` ```
- Example 2:
```toml
[shortcuts]
ctrl-a = { type = "exec", exec = { shell = "grim - | wl-copy", privileged = true } }
```
fields: fields:
prog: prog:
kind: string kind: string
required: true required: false
description: The name of the executable. description: The name of the executable.
shell:
kind: string
required: false
description: |
The name of a shell command to execute. The command will be executed as
`$SHELL -c "command"`.
args: args:
kind: array kind: array
required: false required: false
items: items:
kind: string kind: string
description: The arguments to pass to the executable. description: |
The arguments to pass to the executable.
This field must not be specified if a shell command is used.
env: env:
kind: map kind: map
required: false required: false