Skip to content

Commit 78f1c3b

Browse files
committed
[Feat] enhance routes parser
1 parent 9fb1d45 commit 78f1c3b

File tree

4 files changed

+50
-20
lines changed

4 files changed

+50
-20
lines changed

App.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ prompt_placeholder = "Type a command and press Enter ⏎"
66
prompt_symbol = ""
77
routes = [
88
{ path = "/", command = 'clear && ls -p'},
9-
{ path = "/posts/{file_path}", command = 'clear && render {file_path}'},
9+
{ path = "/posts/{*file_path}", command = 'clear && render {*file_path}'},
1010
{ path = "*", command = 'clear && echo "Not found!"' }
1111
]
1212

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ generate-css.sh # TailwindCSS generation script
110110
- **Build System**: Trunk for WASM bundling with TailwindCSS integration
111111
- **State Management**: Yew hooks (use_state, use_effect) for component state
112112

113+
## Routing
114+
115+
Routes are defined in `App.toml` and executed via the terminal command runner:
116+
117+
- Static match: `/` runs its command exactly.
118+
- Named segment: `/posts/{name}/foo` binds `{name}` to the segment between `posts` and `foo` (e.g., `/posts/bar/foo` sets `name = "bar"`).
119+
- Wildcard tail: `/posts/{*file_path}` captures everything after `/posts/` (e.g., `/posts/a/b/c` sets `file_path = "/a/b/c"`, and visiting `/posts/` sets `file_path = "/"`).
120+
- Fallback: `*` matches anything not caught earlier.
121+
122+
Captured values can be interpolated into commands using `{name}` or `{*name}`.
123+
113124
## Adding New Commands
114125

115126
To add a new command:
@@ -138,4 +149,4 @@ impl Command for MyCommand {
138149

139150
## License
140151

141-
MIT License
152+
MIT License

app/src/router.rs

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -62,27 +62,32 @@ fn match_route(path: &str, route: &RouteSection) -> Option<String> {
6262
let mut params = HashMap::new();
6363
let mut path_idx = 0usize;
6464

65-
for (idx, part) in pattern_segments.iter().enumerate() {
66-
let is_last = idx == pattern_segments.len() - 1;
67-
if let Some(name) = placeholder(part) {
68-
if is_last {
65+
for part in pattern_segments.iter() {
66+
match placeholder_kind(part) {
67+
Some(Placeholder::Wildcard(name)) => {
6968
let remaining = &path_segments[path_idx..];
70-
params.insert(name.to_string(), remaining.join("/"));
69+
let joined = if remaining.is_empty() {
70+
"/".to_string()
71+
} else {
72+
format!("/{}", remaining.join("/"))
73+
};
74+
params.insert(name.to_string(), joined);
7175
path_idx = path_segments.len();
7276
break;
7377
}
74-
75-
let value = path_segments.get(path_idx)?;
76-
params.insert(name.to_string(), (*value).to_string());
77-
path_idx += 1;
78-
continue;
79-
}
80-
81-
let value = path_segments.get(path_idx)?;
82-
if part != value {
83-
return None;
78+
Some(Placeholder::Single(name)) => {
79+
let value = path_segments.get(path_idx)?;
80+
params.insert(name.to_string(), (*value).to_string());
81+
path_idx += 1;
82+
}
83+
None => {
84+
let value = path_segments.get(path_idx)?;
85+
if part != value {
86+
return None;
87+
}
88+
path_idx += 1;
89+
}
8490
}
85-
path_idx += 1;
8691
}
8792

8893
if path_idx != path_segments.len() {
@@ -96,15 +101,30 @@ fn apply_params(template: &str, params: &HashMap<String, String>) -> String {
96101
let mut resolved = template.to_string();
97102
for (key, value) in params {
98103
resolved = resolved.replace(&format!("{{{key}}}"), value);
104+
resolved = resolved.replace(&format!("{{*{key}}}"), value);
99105
}
100106
resolved
101107
}
102108

103-
fn placeholder(segment: &str) -> Option<&str> {
109+
enum Placeholder<'a> {
110+
Single(&'a str),
111+
Wildcard(&'a str),
112+
}
113+
114+
fn placeholder_kind(segment: &str) -> Option<Placeholder<'_>> {
115+
if let Some(name) = segment
116+
.strip_prefix("{*")
117+
.and_then(|s| s.strip_suffix('}'))
118+
.filter(|s| !s.is_empty())
119+
{
120+
return Some(Placeholder::Wildcard(name));
121+
}
122+
104123
segment
105124
.strip_prefix('{')
106125
.and_then(|s| s.strip_suffix('}'))
107126
.filter(|s| !s.is_empty())
127+
.map(Placeholder::Single)
108128
}
109129

110130
fn normalize_path(path: &str) -> String {

app/src/terminal.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use crate::config_service::ConfigService;
55
use crate::terminal_state::{TerminalAction, TerminalState};
66
use crate::types::{OutputKind, TermLine};
77
use crate::vfs_data::{load_vfs, VfsNode};
8-
use shell_parser::integration::ExecutableCommand;
98
use shell_parser::{with_cli, ShellParseError};
109
use std::cell::RefCell;
1110
use std::rc::Rc;

0 commit comments

Comments
 (0)