Skip to main content

bynk/
cli.rs

1//! The `bynk` driver command-line interface.
2//!
3//! Kept thin: the driver shells `bynkc` and the Node toolchain. v0.46 ships a
4//! single subcommand, `doctor`; `new` and `dev` join it in later slices.
5
6use std::path::PathBuf;
7
8use clap::{Parser, Subcommand, ValueEnum};
9
10use crate::doctor::{Capability, DoctorOptions};
11use crate::report::Format;
12
13#[derive(Parser, Debug)]
14#[command(name = "bynk", version, about = "The Bynk driver", long_about = None)]
15pub struct Cli {
16    #[command(subcommand)]
17    pub command: Command,
18}
19
20#[derive(Subcommand, Debug)]
21pub enum Command {
22    /// Check whether your machine is ready to compile, test, and deploy Bynk —
23    /// and print the exact remedy for anything missing.
24    ///
25    /// Bare `bynk doctor` is informational: it surveys every capability and
26    /// exits 0 unless `bynkc` itself is unusable. `--only <capability>` gates on
27    /// one capability (exits non-zero if its tools are missing); `--strict`
28    /// turns every warning into a failure, for CI.
29    Doctor {
30        /// Project directory to inspect (for project-local `node_modules/.bin`
31        /// resolution). Defaults to the current directory.
32        #[arg(default_value = ".")]
33        input: PathBuf,
34        /// Scope the check — and the exit code — to one capability.
35        #[arg(long, value_enum)]
36        only: Option<CapabilityArg>,
37        /// Treat every warning (optional gaps, npx provisionability, minor
38        /// version skew) as a failure. For an all-green CI gate.
39        #[arg(long)]
40        strict: bool,
41        /// Output format. `human` (default) is a grouped table; `short` and
42        /// `json` are the stable scriptable surface.
43        #[arg(long, value_enum, default_value = "human")]
44        format: FormatArg,
45    },
46    /// Build the project and serve it locally with `wrangler dev` — one step in
47    /// place of the manual compile + `cd` + `wrangler dev` recipe.
48    ///
49    /// Compiles into a managed `.bynk/dev/` build dir, picks the worker to serve
50    /// (one context → served; `--context` to choose; ambiguous → lists them),
51    /// and runs `wrangler dev` from inside it in local mode (Miniflare) — no
52    /// namespace provisioning needed. Everything after `--` is forwarded to
53    /// `wrangler dev` verbatim.
54    Dev {
55        /// Project directory to serve from (anywhere inside the project; the
56        /// root is found by walking up for `bynk.toml`). Defaults to `.`.
57        #[arg(default_value = ".")]
58        path: PathBuf,
59        /// Which context's worker to serve, for multi-context projects.
60        #[arg(long)]
61        context: Option<String>,
62        /// Serve with the V8 inspector enabled (slice 3, ADR 0104): `wrangler dev`
63        /// starts with `--inspector-port` so a JavaScript debugger can attach.
64        /// Breakpoints set in `.bynk` sources resolve through the emitted source
65        /// maps, composed into the worker bundle. Prints the inspector URL on start.
66        #[arg(long)]
67        inspect: bool,
68        /// Inspector port for `--inspect` (default 9229).
69        #[arg(long, default_value_t = 9229)]
70        inspect_port: u16,
71        /// Arguments after `--`, forwarded to `wrangler dev` (e.g. `-- --port 8788`).
72        #[arg(last = true)]
73        wrangler_args: Vec<String>,
74    },
75    /// Scaffold a new project: a complete, runnable single-context HTTP service
76    /// you can serve immediately with `bynk dev`.
77    ///
78    /// Writes a `bynk.toml`, a `.gitignore`, and `src/<name>.bynk` into a new
79    /// directory. Pure offline file-writing — it shells nothing and needs no
80    /// toolchain, so you can run it before `bynkc`, Node, or `wrangler` are
81    /// installed. The project name defaults to the target directory's final
82    /// component; `--name` overrides it and must be a legal Bynk identifier.
83    New {
84        /// Directory to create for the new project (e.g. `hello` or `./hello`).
85        path: PathBuf,
86        /// Project name / context identifier. Defaults to PATH's final
87        /// component; must be a legal Bynk identifier (a letter followed by
88        /// letters, digits, or underscores).
89        #[arg(long)]
90        name: Option<String>,
91    },
92}
93
94/// `--only` selector. Mirrors [`Capability`] minus the internal distinctions.
95#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
96pub enum CapabilityArg {
97    /// `bynkc` compile / check / fmt.
98    Compile,
99    /// `bynk test` — Node + tsc|tsx.
100    Test,
101    /// dev / deploy to Cloudflare — Node + wrangler.
102    Deploy,
103    /// Editor support — bynkc-lsp.
104    Editor,
105    /// Build Bynk from source — a Rust toolchain.
106    Build,
107}
108
109impl From<CapabilityArg> for Capability {
110    fn from(a: CapabilityArg) -> Self {
111        match a {
112            CapabilityArg::Compile => Capability::Compile,
113            CapabilityArg::Test => Capability::Test,
114            CapabilityArg::Deploy => Capability::Deploy,
115            CapabilityArg::Editor => Capability::Editor,
116            CapabilityArg::Build => Capability::BuildFromSource,
117        }
118    }
119}
120
121#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, ValueEnum)]
122pub enum FormatArg {
123    #[default]
124    Human,
125    Short,
126    Json,
127}
128
129impl From<FormatArg> for Format {
130    fn from(f: FormatArg) -> Self {
131        match f {
132            FormatArg::Human => Format::Human,
133            FormatArg::Short => Format::Short,
134            FormatArg::Json => Format::Json,
135        }
136    }
137}
138
139/// Build the [`DoctorOptions`] from the parsed flags.
140pub fn doctor_options(only: Option<CapabilityArg>, strict: bool) -> DoctorOptions {
141    DoctorOptions {
142        only: only.map(Into::into),
143        strict,
144    }
145}