OXYGENHUB
# 2b5ee371060e oxygen / src / cli / mod.rs
5.8 KiB · Rust · last changed TLS: oxy serve --tls-cert/--tls-key via rustls; remote.<name>.ca and OXY_CA for self-hosted roots 1 month ago source blame history raw
fce44ae9 Andy McCall ↶ 1
//! Command-line definitions and dispatch.
2
3
mod format;
4
mod identity;
5
mod sync;
6
mod workspace;
7
8
use anyhow::{Context, Result};
9
use clap::{Parser, Subcommand};
10
use oxy_core::repo::Repo;
11
use std::path::PathBuf;
12
13
#[derive(Parser)]
14
#[command(name = "oxy", version, about = "Oxygen: breathing fresh air into version control")]
15
struct Cli {
16
    #[command(subcommand)]
17
    cmd: Cmd,
18
}
19
20
#[derive(Subcommand)]
21
enum Cmd {
22
    /// Create a repository in PATH (default: current directory)
23
    Init { path: Option<PathBuf> },
24
    /// Clone a repository from a local path or an http(s) URL
25
    Clone { url: String, dest: Option<PathBuf> },
26
    /// Show the working copy: changed paths and conflicts
27
    Status,
28
    /// Record the working copy as a change on the current bookmark
29
    Commit {
30
        #[arg(short, long)]
31
        message: String,
32
        /// Extra explicit dependencies (ChangeId prefix, #hash prefix)
33
        #[arg(long = "depends-on")]
34
        depends_on: Vec<String>,
35
    },
36
    /// List changes reachable from the bookmark and the working copy
37
    Log,
38
    /// Show what the working copy changes against its base
39
    Diff,
40
    /// Show one change (ChangeId prefix, #hash prefix, or @)
41
    Show { change: String },
42
    /// Move the working copy to the state formed by CHANGE... (default: the bookmark heads)
43
    Checkout {
44
        changes: Vec<String>,
45
        /// Throw away uncommitted working-copy changes
46
        #[arg(long)]
47
        discard: bool,
48
    },
49
    /// Report which conflicts of the working copy's base are resolved by the current files
50
    Resolve,
51
    /// Undo the last operation
52
    Undo,
53
    /// List operations
54
    #[command(name = "op")]
55
    Op {
56
        #[command(subcommand)]
57
        cmd: OpCmd,
58
    },
59
    /// Manage remotes
60
    Remote {
61
        #[command(subcommand)]
62
        cmd: RemoteCmd,
63
    },
64
    /// Fetch every bookmark of a remote into its tracking references
65
    Fetch { remote: Option<String> },
66
    /// Fetch, then union the remote bookmark into the local one (no merge change)
67
    Pull { remote: Option<String> },
68
    /// Push the current bookmark to a remote
69
    Push {
70
        remote: Option<String>,
71
        /// Allow dropping heads the remote has
72
        #[arg(long)]
73
        force: bool,
74
    },
2b5ee371 Andy McCall ↶ 75
    /// Serve repositories under ROOT over HTTPS (with --tls-cert/--tls-key) or plain HTTP
fce44ae9 Andy McCall ↶ 76
    Serve {
77
        /// Directory with one repository per subdirectory
78
        #[arg(long, default_value = ".")]
79
        root: PathBuf,
80
        #[arg(long, default_value = "127.0.0.1:8890")]
81
        bind: String,
82
        /// Bearer token allowed to push (repeatable); none means open
83
        #[arg(long = "token")]
84
        tokens: Vec<String>,
2b5ee371 Andy McCall ↶ 85
        /// PEM certificate chain; enables HTTPS together with --tls-key
86
        #[arg(long, requires = "tls_key")]
87
        tls_cert: Option<PathBuf>,
88
        /// PEM private key
89
        #[arg(long, requires = "tls_cert")]
90
        tls_key: Option<PathBuf>,
fce44ae9 Andy McCall ↶ 91
    },
92
    /// Manage the signing identity
93
    Identity {
94
        #[command(subcommand)]
95
        cmd: IdentityCmd,
96
    },
97
    /// Verify the signatures on a change (default: every change in the log)
98
    Verify { change: Option<String> },
2b5ee371 Andy McCall ↶ 99
    /// Set a configuration key (author.*, core.sign, workspace.bookmark, remote.<name>.url|token|ca)
fce44ae9 Andy McCall ↶ 100
    Config {
101
        key: String,
102
        value: String,
103
        /// Write to the user file (~/.config/oxy/config.toml) instead of this repository
104
        #[arg(long)]
105
        user: bool,
106
    },
107
}
108
109
#[derive(Subcommand)]
110
enum OpCmd {
111
    Log,
112
}
113
114
#[derive(Subcommand)]
115
enum RemoteCmd {
116
    Add { name: String, url: String },
117
    Remove { name: String },
118
    List,
119
}
120
121
#[derive(Subcommand)]
122
enum IdentityCmd {
123
    /// Generate a composite Ed25519 + ML-DSA-65 key pair and make it your author identity
124
    Create {
125
        #[arg(long)]
126
        name: Option<String>,
127
        #[arg(long)]
128
        email: Option<String>,
129
        /// Record the author in this repository only, not in the user config
130
        #[arg(long)]
131
        local: bool,
132
    },
133
    /// Show the configured identity
134
    Show,
135
}
136
137
/// Open the repository containing the current directory.
138
fn open() -> Result<Repo> {
139
    Repo::open(&std::env::current_dir()?).context("opening repository")
140
}
141
142
/// Parse arguments and run; returns the process exit code.
143
pub fn run() -> Result<i32> {
144
    let cli = Cli::parse();
145
    match cli.cmd {
146
        Cmd::Init { path } => workspace::init(path),
147
        Cmd::Status => workspace::status(),
148
        Cmd::Commit { message, depends_on } => workspace::commit(&message, &depends_on),
149
        Cmd::Log => workspace::log(),
150
        Cmd::Diff => workspace::diff(),
151
        Cmd::Show { change } => workspace::show(&change),
152
        Cmd::Checkout { changes, discard } => workspace::checkout(&changes, discard),
153
        Cmd::Resolve => workspace::resolve(),
154
        Cmd::Undo => workspace::undo(),
155
        Cmd::Op { cmd: OpCmd::Log } => workspace::op_log(),
156
        Cmd::Clone { url, dest } => sync::clone(&url, dest),
157
        Cmd::Remote { cmd } => match cmd {
158
            RemoteCmd::Add { name, url } => sync::remote_add(&name, &url),
159
            RemoteCmd::Remove { name } => sync::remote_remove(&name),
160
            RemoteCmd::List => sync::remote_list(),
161
        },
162
        Cmd::Fetch { remote } => sync::fetch(remote.as_deref()),
163
        Cmd::Pull { remote } => sync::pull(remote.as_deref()),
164
        Cmd::Push { remote, force } => sync::push(remote.as_deref(), force),
2b5ee371 Andy McCall ↶ 165
        Cmd::Serve { root, bind, tokens, tls_cert, tls_key } => sync::serve(root, &bind, tokens, tls_cert.zip(tls_key)),
fce44ae9 Andy McCall ↶ 166
        Cmd::Identity { cmd } => match cmd {
167
            IdentityCmd::Create { name, email, local } => identity::create(name, email, local),
168
            IdentityCmd::Show => identity::show(),
169
        },
170
        Cmd::Verify { change } => identity::verify(change.as_deref()),
171
        Cmd::Config { key, value, user } => identity::config(&key, &value, user),
172
    }
173
}