1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use clap::{Parser, Subcommand};
use std::time::Duration;
use v4_cli::commands;
#[derive(Parser)]
#[command(name = "v4")]
#[command(version, about = "CLI tool for V4 VM bytecode deployment", long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Deploy bytecode to device
Push {
/// Bytecode file path
file: String,
/// Serial port path (e.g., /dev/ttyACM0)
#[arg(short, long)]
port: String,
/// Don't wait for response
#[arg(long)]
detach: bool,
/// Timeout in seconds
#[arg(long, default_value = "5")]
timeout: u64,
},
/// Check connection to device
Ping {
/// Serial port path
#[arg(short, long)]
port: String,
/// Timeout in seconds
#[arg(long, default_value = "5")]
timeout: u64,
},
/// Reset VM
Reset {
/// Serial port path
#[arg(short, long)]
port: String,
/// Timeout in seconds
#[arg(long, default_value = "5")]
timeout: u64,
},
/// Start interactive REPL session
Repl {
/// Serial port path (e.g., /dev/ttyACM0)
#[arg(short, long)]
port: String,
/// Skip VM reset on startup (preserves existing words)
#[arg(long)]
no_reset: bool,
},
/// Execute Forth source file on device
Exec {
/// Forth source file path
file: String,
/// Serial port path (e.g., /dev/ttyACM0)
#[arg(short, long)]
port: String,
/// Timeout in seconds
#[arg(long, default_value = "5")]
timeout: u64,
/// Enter REPL after execution
#[arg(long)]
repl: bool,
},
}
fn main() {
let cli = Cli::parse();
let result = match cli.command {
Commands::Push {
file,
port,
detach,
timeout,
} => commands::push(&file, &port, detach, Duration::from_secs(timeout)),
Commands::Ping { port, timeout } => commands::ping(&port, Duration::from_secs(timeout)),
Commands::Reset { port, timeout } => commands::reset(&port, Duration::from_secs(timeout)),
Commands::Repl { port, no_reset } => commands::run_repl(&port, no_reset),
Commands::Exec {
file,
port,
timeout,
repl,
} => commands::exec(&file, &port, Duration::from_secs(timeout), repl),
};
if let Err(e) = result {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}