tui: rename slash args codec to schema

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Charles Cunningham
2026-03-22 18:51:31 -07:00
parent 2b96127399
commit 159d9a2cb6
4 changed files with 86 additions and 80 deletions

View File

@@ -4,7 +4,7 @@ use strum_macros::IntoStaticStr;
use crate::app_event::FeedbackCategory;
use crate::bottom_pane::StatusLineItem;
use crate::slash_command_protocol::SlashArgsCodec;
use crate::slash_command_protocol::SlashArgsSchema;
use crate::slash_command_protocol::SlashCommandParseInput;
use crate::slash_command_protocol::SlashCommandUsageErrorKind;
use crate::slash_command_protocol::SlashSerializedText;
@@ -124,22 +124,22 @@ const FEEDBACK_CATEGORY_CHOICES: &[(&str, FeedbackCategory)] = &[
pub(crate) trait SlashCommandInlineArgs: Sized {
const USAGE_LINES: &'static [&'static str];
fn codec() -> Box<dyn SlashArgsCodec<Self>>;
fn args_schema() -> Box<dyn SlashArgsSchema<Self>>;
fn into_invocation(self) -> SlashCommandInvocation;
fn parse_inline(input: SlashCommandParseInput<'_>) -> Result<Self, SlashCommandUsageErrorKind> {
let codec = Self::codec();
let args_schema = Self::args_schema();
let mut parser = crate::slash_command_protocol::SlashArgsParser::new(input)?;
let value = codec.parse(&mut parser)?;
codec.finish(parser)?;
let value = args_schema.parse(&mut parser)?;
args_schema.finish(parser)?;
Ok(value)
}
fn serialize_inline(&self) -> SlashSerializedText {
let codec = Self::codec();
let args_schema = Self::args_schema();
let mut serializer = crate::slash_command_protocol::SlashArgsSerializer::default();
codec.serialize(self, &mut serializer);
args_schema.serialize(self, &mut serializer);
serializer.finish()
}
}
@@ -152,7 +152,7 @@ pub(crate) struct FastArgs {
impl SlashCommandInlineArgs for FastArgs {
const USAGE_LINES: &'static [&'static str] = &["/fast", "/fast [on|off|status]"];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(
positional(enum_choice(FAST_MODE_CHOICES).ascii_case_insensitive())
.map_result(|mode| Ok(Self { mode }), |args| args.mode),
@@ -172,7 +172,7 @@ pub(crate) struct RenameArgs {
impl SlashCommandInlineArgs for RenameArgs {
const USAGE_LINES: &'static [&'static str] = &["/rename", "/rename <title>"];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(
remainder(text()).map_result(|title| Ok(Self { title }), |args| args.title.clone()),
)
@@ -191,7 +191,7 @@ pub(crate) struct PlanArgs {
impl SlashCommandInlineArgs for PlanArgs {
const USAGE_LINES: &'static [&'static str] = &["/plan", "/plan <prompt>"];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(
remainder(text()).map_result(|prompt| Ok(Self { prompt }), |args| args.prompt.clone()),
)
@@ -210,7 +210,7 @@ pub(crate) struct ReviewArgs {
impl SlashCommandInlineArgs for ReviewArgs {
const USAGE_LINES: &'static [&'static str] = &["/review", "/review <instructions>"];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(remainder(text()).map_result(
|instructions| Ok(Self { instructions }),
|args| args.instructions.clone(),
@@ -233,7 +233,7 @@ impl SlashCommandInlineArgs for SandboxReadRootArgs {
"/sandbox-add-read-dir --path=<absolute-path>",
];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(
named_or_positional("path", string())
.map_result(|path| Ok(Self { path }), |args| args.path.clone()),
@@ -256,7 +256,7 @@ impl SlashCommandInlineArgs for FeedbackArgs {
"/feedback <bad-result|good-result|bug|safety-check|other>",
];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(
positional(enum_choice(FEEDBACK_CATEGORY_CHOICES))
.map_result(|category| Ok(Self { category }), |args| args.category),
@@ -276,7 +276,7 @@ pub(crate) struct StatuslineArgs {
impl SlashCommandInlineArgs for StatuslineArgs {
const USAGE_LINES: &'static [&'static str] = &["/statusline", "/statusline <item>..."];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(list(from_str_value::<StatusLineItem>()).map_result(
|items| {
if items.is_empty() {

View File

@@ -424,7 +424,7 @@ impl SlashArgsSerializer {
}
}
pub(crate) trait SlashArgsCodec<T> {
pub(crate) trait SlashArgsSchema<T> {
fn parse<'a>(&self, parser: &mut SlashArgsParser<'a>) -> Result<T, SlashCommandUsageErrorKind>;
fn serialize(&self, value: &T, serializer: &mut SlashArgsSerializer);
@@ -437,13 +437,13 @@ pub(crate) trait SlashArgsCodec<T> {
self,
parse_map: P,
serialize_map: S,
) -> SlashMapResultCodec<Self, P, S, T, U>
) -> SlashMapResultSchema<Self, P, S, T, U>
where
Self: Sized,
P: Fn(T) -> Result<U, SlashCommandUsageErrorKind>,
S: Fn(&U) -> T,
{
SlashMapResultCodec {
SlashMapResultSchema {
inner: self,
parse_map,
serialize_map,
@@ -452,16 +452,16 @@ pub(crate) trait SlashArgsCodec<T> {
}
}
pub(crate) struct SlashMapResultCodec<C, P, S, T, U> {
pub(crate) struct SlashMapResultSchema<C, P, S, T, U> {
inner: C,
parse_map: P,
serialize_map: S,
_phantom: PhantomData<fn(T) -> U>,
}
impl<C, P, S, T, U> SlashArgsCodec<U> for SlashMapResultCodec<C, P, S, T, U>
impl<C, P, S, T, U> SlashArgsSchema<U> for SlashMapResultSchema<C, P, S, T, U>
where
C: SlashArgsCodec<T>,
C: SlashArgsSchema<T>,
P: Fn(T) -> Result<U, SlashCommandUsageErrorKind>,
S: Fn(&U) -> T,
{
@@ -480,15 +480,15 @@ where
}
}
pub(crate) struct SlashPositionalCodec<S> {
pub(crate) struct SlashPositionalSchema<S> {
spec: S,
}
pub(crate) fn positional<S>(spec: S) -> SlashPositionalCodec<S> {
SlashPositionalCodec { spec }
pub(crate) fn positional<S>(spec: S) -> SlashPositionalSchema<S> {
SlashPositionalSchema { spec }
}
impl<T, S> SlashArgsCodec<T> for SlashPositionalCodec<S>
impl<T, S> SlashArgsSchema<T> for SlashPositionalSchema<S>
where
S: SlashTokenValueSpec<T>,
{
@@ -501,15 +501,15 @@ where
}
}
pub(crate) struct SlashListCodec<S> {
pub(crate) struct SlashListSchema<S> {
spec: S,
}
pub(crate) fn list<S>(spec: S) -> SlashListCodec<S> {
SlashListCodec { spec }
pub(crate) fn list<S>(spec: S) -> SlashListSchema<S> {
SlashListSchema { spec }
}
impl<T, S> SlashArgsCodec<Vec<T>> for SlashListCodec<S>
impl<T, S> SlashArgsSchema<Vec<T>> for SlashListSchema<S>
where
T: Clone,
S: SlashTokenValueSpec<T>,
@@ -527,17 +527,17 @@ where
}
#[allow(dead_code)]
pub(crate) struct SlashNamedCodec<S> {
pub(crate) struct SlashNamedSchema<S> {
key: &'static str,
spec: S,
}
#[allow(dead_code)]
pub(crate) fn named<S>(key: &'static str, spec: S) -> SlashNamedCodec<S> {
SlashNamedCodec { key, spec }
pub(crate) fn named<S>(key: &'static str, spec: S) -> SlashNamedSchema<S> {
SlashNamedSchema { key, spec }
}
impl<T, S> SlashArgsCodec<Option<T>> for SlashNamedCodec<S>
impl<T, S> SlashArgsSchema<Option<T>> for SlashNamedSchema<S>
where
S: SlashTokenValueSpec<T>,
{
@@ -555,16 +555,19 @@ where
}
}
pub(crate) struct SlashNamedOrPositionalCodec<S> {
pub(crate) struct SlashNamedOrPositionalSchema<S> {
key: &'static str,
spec: S,
}
pub(crate) fn named_or_positional<S>(key: &'static str, spec: S) -> SlashNamedOrPositionalCodec<S> {
SlashNamedOrPositionalCodec { key, spec }
pub(crate) fn named_or_positional<S>(
key: &'static str,
spec: S,
) -> SlashNamedOrPositionalSchema<S> {
SlashNamedOrPositionalSchema { key, spec }
}
impl<T, S> SlashArgsCodec<T> for SlashNamedOrPositionalCodec<S>
impl<T, S> SlashArgsSchema<T> for SlashNamedOrPositionalSchema<S>
where
S: SlashTokenValueSpec<T>,
{
@@ -580,15 +583,15 @@ where
}
}
pub(crate) struct SlashRemainderCodec<S> {
pub(crate) struct SlashRemainderSchema<S> {
spec: S,
}
pub(crate) fn remainder<S>(spec: S) -> SlashRemainderCodec<S> {
SlashRemainderCodec { spec }
pub(crate) fn remainder<S>(spec: S) -> SlashRemainderSchema<S> {
SlashRemainderSchema { spec }
}
impl<T, S> SlashArgsCodec<T> for SlashRemainderCodec<S>
impl<T, S> SlashArgsSchema<T> for SlashRemainderSchema<S>
where
S: SlashTextValueSpec<T>,
{

View File

@@ -4,7 +4,7 @@ use strum_macros::IntoStaticStr;
use crate::app_event::FeedbackCategory;
use crate::bottom_pane::StatusLineItem;
use crate::slash_command_protocol::SlashArgsCodec;
use crate::slash_command_protocol::SlashArgsSchema;
use crate::slash_command_protocol::SlashCommandParseInput;
use crate::slash_command_protocol::SlashCommandUsageErrorKind;
use crate::slash_command_protocol::SlashSerializedText;
@@ -123,22 +123,22 @@ const FEEDBACK_CATEGORY_CHOICES: &[(&str, FeedbackCategory)] = &[
pub(crate) trait SlashCommandInlineArgs: Sized {
const USAGE_LINES: &'static [&'static str];
fn codec() -> Box<dyn SlashArgsCodec<Self>>;
fn args_schema() -> Box<dyn SlashArgsSchema<Self>>;
fn into_invocation(self) -> SlashCommandInvocation;
fn parse_inline(input: SlashCommandParseInput<'_>) -> Result<Self, SlashCommandUsageErrorKind> {
let codec = Self::codec();
let args_schema = Self::args_schema();
let mut parser = crate::slash_command_protocol::SlashArgsParser::new(input)?;
let value = codec.parse(&mut parser)?;
codec.finish(parser)?;
let value = args_schema.parse(&mut parser)?;
args_schema.finish(parser)?;
Ok(value)
}
fn serialize_inline(&self) -> SlashSerializedText {
let codec = Self::codec();
let args_schema = Self::args_schema();
let mut serializer = crate::slash_command_protocol::SlashArgsSerializer::default();
codec.serialize(self, &mut serializer);
args_schema.serialize(self, &mut serializer);
serializer.finish()
}
}
@@ -151,7 +151,7 @@ pub(crate) struct FastArgs {
impl SlashCommandInlineArgs for FastArgs {
const USAGE_LINES: &'static [&'static str] = &["/fast", "/fast [on|off|status]"];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(
positional(enum_choice(FAST_MODE_CHOICES).ascii_case_insensitive())
.map_result(|mode| Ok(Self { mode }), |args| args.mode),
@@ -171,7 +171,7 @@ pub(crate) struct RenameArgs {
impl SlashCommandInlineArgs for RenameArgs {
const USAGE_LINES: &'static [&'static str] = &["/rename", "/rename <title>"];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(
remainder(text()).map_result(|title| Ok(Self { title }), |args| args.title.clone()),
)
@@ -190,7 +190,7 @@ pub(crate) struct PlanArgs {
impl SlashCommandInlineArgs for PlanArgs {
const USAGE_LINES: &'static [&'static str] = &["/plan", "/plan <prompt>"];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(
remainder(text()).map_result(|prompt| Ok(Self { prompt }), |args| args.prompt.clone()),
)
@@ -209,7 +209,7 @@ pub(crate) struct ReviewArgs {
impl SlashCommandInlineArgs for ReviewArgs {
const USAGE_LINES: &'static [&'static str] = &["/review", "/review <instructions>"];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(remainder(text()).map_result(
|instructions| Ok(Self { instructions }),
|args| args.instructions.clone(),
@@ -232,7 +232,7 @@ impl SlashCommandInlineArgs for SandboxReadRootArgs {
"/sandbox-add-read-dir --path=<absolute-path>",
];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(
named_or_positional("path", string())
.map_result(|path| Ok(Self { path }), |args| args.path.clone()),
@@ -255,7 +255,7 @@ impl SlashCommandInlineArgs for FeedbackArgs {
"/feedback <bad-result|good-result|bug|safety-check|other>",
];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(
positional(enum_choice(FEEDBACK_CATEGORY_CHOICES))
.map_result(|category| Ok(Self { category }), |args| args.category),
@@ -275,7 +275,7 @@ pub(crate) struct StatuslineArgs {
impl SlashCommandInlineArgs for StatuslineArgs {
const USAGE_LINES: &'static [&'static str] = &["/statusline", "/statusline <item>..."];
fn codec() -> Box<dyn SlashArgsCodec<Self>> {
fn args_schema() -> Box<dyn SlashArgsSchema<Self>> {
Box::new(list(from_str_value::<StatusLineItem>()).map_result(
|items| {
if items.is_empty() {

View File

@@ -424,7 +424,7 @@ impl SlashArgsSerializer {
}
}
pub(crate) trait SlashArgsCodec<T> {
pub(crate) trait SlashArgsSchema<T> {
fn parse<'a>(&self, parser: &mut SlashArgsParser<'a>) -> Result<T, SlashCommandUsageErrorKind>;
fn serialize(&self, value: &T, serializer: &mut SlashArgsSerializer);
@@ -437,13 +437,13 @@ pub(crate) trait SlashArgsCodec<T> {
self,
parse_map: P,
serialize_map: S,
) -> SlashMapResultCodec<Self, P, S, T, U>
) -> SlashMapResultSchema<Self, P, S, T, U>
where
Self: Sized,
P: Fn(T) -> Result<U, SlashCommandUsageErrorKind>,
S: Fn(&U) -> T,
{
SlashMapResultCodec {
SlashMapResultSchema {
inner: self,
parse_map,
serialize_map,
@@ -452,16 +452,16 @@ pub(crate) trait SlashArgsCodec<T> {
}
}
pub(crate) struct SlashMapResultCodec<C, P, S, T, U> {
pub(crate) struct SlashMapResultSchema<C, P, S, T, U> {
inner: C,
parse_map: P,
serialize_map: S,
_phantom: PhantomData<fn(T) -> U>,
}
impl<C, P, S, T, U> SlashArgsCodec<U> for SlashMapResultCodec<C, P, S, T, U>
impl<C, P, S, T, U> SlashArgsSchema<U> for SlashMapResultSchema<C, P, S, T, U>
where
C: SlashArgsCodec<T>,
C: SlashArgsSchema<T>,
P: Fn(T) -> Result<U, SlashCommandUsageErrorKind>,
S: Fn(&U) -> T,
{
@@ -480,15 +480,15 @@ where
}
}
pub(crate) struct SlashPositionalCodec<S> {
pub(crate) struct SlashPositionalSchema<S> {
spec: S,
}
pub(crate) fn positional<S>(spec: S) -> SlashPositionalCodec<S> {
SlashPositionalCodec { spec }
pub(crate) fn positional<S>(spec: S) -> SlashPositionalSchema<S> {
SlashPositionalSchema { spec }
}
impl<T, S> SlashArgsCodec<T> for SlashPositionalCodec<S>
impl<T, S> SlashArgsSchema<T> for SlashPositionalSchema<S>
where
S: SlashTokenValueSpec<T>,
{
@@ -501,15 +501,15 @@ where
}
}
pub(crate) struct SlashListCodec<S> {
pub(crate) struct SlashListSchema<S> {
spec: S,
}
pub(crate) fn list<S>(spec: S) -> SlashListCodec<S> {
SlashListCodec { spec }
pub(crate) fn list<S>(spec: S) -> SlashListSchema<S> {
SlashListSchema { spec }
}
impl<T, S> SlashArgsCodec<Vec<T>> for SlashListCodec<S>
impl<T, S> SlashArgsSchema<Vec<T>> for SlashListSchema<S>
where
T: Clone,
S: SlashTokenValueSpec<T>,
@@ -527,17 +527,17 @@ where
}
#[allow(dead_code)]
pub(crate) struct SlashNamedCodec<S> {
pub(crate) struct SlashNamedSchema<S> {
key: &'static str,
spec: S,
}
#[allow(dead_code)]
pub(crate) fn named<S>(key: &'static str, spec: S) -> SlashNamedCodec<S> {
SlashNamedCodec { key, spec }
pub(crate) fn named<S>(key: &'static str, spec: S) -> SlashNamedSchema<S> {
SlashNamedSchema { key, spec }
}
impl<T, S> SlashArgsCodec<Option<T>> for SlashNamedCodec<S>
impl<T, S> SlashArgsSchema<Option<T>> for SlashNamedSchema<S>
where
S: SlashTokenValueSpec<T>,
{
@@ -555,16 +555,19 @@ where
}
}
pub(crate) struct SlashNamedOrPositionalCodec<S> {
pub(crate) struct SlashNamedOrPositionalSchema<S> {
key: &'static str,
spec: S,
}
pub(crate) fn named_or_positional<S>(key: &'static str, spec: S) -> SlashNamedOrPositionalCodec<S> {
SlashNamedOrPositionalCodec { key, spec }
pub(crate) fn named_or_positional<S>(
key: &'static str,
spec: S,
) -> SlashNamedOrPositionalSchema<S> {
SlashNamedOrPositionalSchema { key, spec }
}
impl<T, S> SlashArgsCodec<T> for SlashNamedOrPositionalCodec<S>
impl<T, S> SlashArgsSchema<T> for SlashNamedOrPositionalSchema<S>
where
S: SlashTokenValueSpec<T>,
{
@@ -580,15 +583,15 @@ where
}
}
pub(crate) struct SlashRemainderCodec<S> {
pub(crate) struct SlashRemainderSchema<S> {
spec: S,
}
pub(crate) fn remainder<S>(spec: S) -> SlashRemainderCodec<S> {
SlashRemainderCodec { spec }
pub(crate) fn remainder<S>(spec: S) -> SlashRemainderSchema<S> {
SlashRemainderSchema { spec }
}
impl<T, S> SlashArgsCodec<T> for SlashRemainderCodec<S>
impl<T, S> SlashArgsSchema<T> for SlashRemainderSchema<S>
where
S: SlashTextValueSpec<T>,
{