feat: add session name template support#357
Conversation
e143a6f to
eaa1fbd
Compare
|
@GeoffChurch this is the session name template thing we were talking about in case you want to review or join in the syntax bikeshedding. |
eaa1fbd to
4a40633
Compare
|
|
|
Yeah that would work and I think it would look nice. I don't really have intuition pushing in either direction between |
|
That makes sense. Support for |
|
I don't see how That's a good point about reuse, though since sigils are not reserved any new sigil would technically be a breaking change. I suppose we could take this opportunity to make it a hard error to include a set of other sigils for forwards compatibility. I'm not really sure what other sigils we would ever want though. The only extension to this scheme I can think of would be introducing some sort of string munging dsl that would allow people to compute things in terms of variables. I can't really think of any reasonable usecases for that for session name templates, but I was planning to follow this up by added a templated |
I just meant that it would still do the replacement. I also can't think of any compelling use cases for different sigils. Especially since with a single sigil / simple string replacement you can already e.g. set up an external process to call |
|
Ok, I'll strip out the |
4a40633 to
473433d
Compare
|
Switch to |
| let mut var_start = None; | ||
| let mut last_var_end = 0; | ||
| let mut last_idx = 0; | ||
| for (i, c) in src.char_indices() { |
There was a problem hiding this comment.
The manual index tracking here feels pretty hard to follow here. What do you think using find.('{') to try and process one chunk at a time? e.g.
pub fn new(src: &str) -> anyhow::Result<Template> {
let mut chunks = vec![];
let mut rest = src;
while let Some(start) = rest.find('{') {
// Push any raw text before the '{'
if start > 0 {
chunks.push(Chunk::Raw(rest[..start].to_string()));
}
rest = &rest[start + 1..];
// Find the closing '}'
let end = rest.find('}').ok_or_else(|| anyhow!("unclosed var substitution"))?;
let var = &rest[..end];
// Validate the identifier
let valid_ident = var.chars().next().is_some_and(|c| !c.is_numeric())
&& var.chars().all(|c| c.is_alphanumeric() || c == '_');
if !valid_ident {
return Err(anyhow!("invalid var name: '{}'", var));
}
chunks.push(Chunk::Var(var.to_string()));
rest = &rest[end + 1..];
}
// Push any remaining text
if !rest.is_empty() {
chunks.push(Chunk::Raw(rest.to_string()));
}
// More idiomatic way to calculate the size guess
let instantiated_size_guess = chunks
.iter()
.map(|c| match c {
Chunk::Raw(text) => text.len(),
Chunk::Var(_) => VAR_SIZE_GUESS,
})
.sum();
Ok(Template { chunks, instantiated_size_guess })
}
There was a problem hiding this comment.
Good idea, switched.
I kept the size guess computation because I don't think functional style is always better.
| let maybe_switch: MaybeSwitch = client.read_reply().context("reading reply")?; | ||
| if json { | ||
| let mut obj = serde_json::json!({}); | ||
| for (var, val) in maybe_switch.vars.into_iter() { |
There was a problem hiding this comment.
would using collect() be cleaner?
let map: HashMap<String, String> = maybe_switch.vars.into_iter().collect();
println!("{}", serde_json::to_string_pretty(&map)?);
There was a problem hiding this comment.
I want to collect into a serde_json::Object directly since I feel like I know what I'm getting in the output.
| let cases = vec![ | ||
| ("{var}", vec![("other", "other")], ""), | ||
| ("{var}", vec![("var", "val")], "val"), | ||
| ("{var}-foo", vec![("var", "val")], "val-foo"), |
There was a problem hiding this comment.
("{var}-{var}", vec![("var", "val")], "val-val" ),?
There was a problem hiding this comment.
Good point, added
| ("{$}", "invalid var name"), | ||
| ("{.}", "invalid var name"), | ||
| ("{1foo}", "invalid var name"), | ||
| ("{foo-bar}", "invalid var name"), |
There was a problem hiding this comment.
("{}", "invalid var name"),
("{var name}", "invalid var name"),
maybe?
There was a problem hiding this comment.
Good point, added
|
I want to hold off on merging until #366 goes in then rebase on top of that. |
9e902ef to
b49522e
Compare
This patch adds support for session name templates so
that you can switch multiple shpool sessions all at once.
In some sense this is a super-set of the 'shpool switch' FR.
I did lay some groundwork for implementing support for that
in this change, though I'm starting to wonder if templates are
good enough on their own. The only extra thing that switch would
bring is the ability to switch sessions you don't pre-declare as
switchable with a dedicated variable up front.
One thing worth bikeshedding: At first I was thinking '${var}' for
the substitution syntax, then realized that would be weird about
nesting when it comes to shells, so I switched to '#{var}' syntax,
but then I realized that's the comment char in shells, and wound up
on '@{var}'. I'm open to other symbol/syntax ideas.
BREAKING: this breaks shpool-protocol since we have a new chunk kind.
b49522e to
304b047
Compare
Issue Link
#345
AI Policy Ack
Please ack that you have read the AI Policy
and explain your use of AI to generate this PR.
This PR was:
I vibed the tests, but meat coded all the application code.
Description
This patch adds support for session name templates so that you can switch multiple shpool sessions all at once.
In some sense this is a super-set of the 'shpool switch' FR. I did lay some groundwork for implementing support for that in this change, though I'm starting to wonder if templates are good enough on their own. The only extra thing that switch would bring is the ability to switch sessions you don't pre-declare as switchable with a dedicated variable up front.
One thing worth bikeshedding: At first I was thinking '${var}' for
the substitution syntax, then realized that would be weird about
nesting when it comes to shells, so I switched to '#{var}' syntax,
but then I realized that's the comment char in shells, and wound up
on '@{var}'. I'm open to other symbol/syntax ideas.