tawasuyu

tawasuyu/brahman

Rust 582TOML 222Markdown 83JSON 198635 Definitions

⎇ e77a32f4d6a09fe29701811f4d2e60dd2cb678db crates/modules/semantic_dht/minga-cli/src/main.rs Rust · 159 lines · 5.1 KB History Who touched what raw

feat(minga): minga-vfs — proyecta el repo como filesystem FUSE e77a32f4d · sergio ·

1//! Binario `minga`: argument parsing y formateo de salida.
2
3use std::path::PathBuf;
4use std::process::ExitCode;
5
6use clap::{Parser, Subcommand};
7use minga_cli::{
8    cmd_ingest, cmd_init, cmd_listen, cmd_mount, cmd_status, cmd_sync, cmd_watch, CliError,
9};
10
11#[derive(Parser)]
12#[command(
13    name = "minga",
14    version,
15    about = "Minga: VCS semántico P2P. Versiona AST, no líneas."
16)]
17struct Cli {
18    /// Ruta del repositorio Minga. Por defecto: `.minga` en el cwd.
19    #[arg(short, long, default_value = ".minga", global = true)]
20    repo: PathBuf,
21
22    #[command(subcommand)]
23    command: Command,
24}
25
26#[derive(Subcommand)]
27enum Command {
28    /// Inicializa un nuevo repo: genera keypair Ed25519, lo cifra con
29    /// passphrase, y crea el almacén persistente vacío.
30    Init,
31
32    /// Muestra DID, tamaño del MST, nodos almacenados y atestaciones.
33    Status,
34
35    /// Parsea un archivo Rust, lo añade al MST y firma una atestación
36    /// de autoría con la identidad del repo.
37    Ingest {
38        /// Ruta del archivo .rs a ingerir.
39        file: PathBuf,
40    },
41
42    /// Escucha conexiones de peers en una multiaddr libp2p y acepta
43    /// sincronizaciones entrantes hasta Ctrl+C.
44    Listen {
45        /// Multiaddr libp2p, ej. `/ip4/0.0.0.0/tcp/4001`.
46        addr: String,
47    },
48
49    /// Sincroniza una vez con un peer remoto (multiaddr con `/p2p/<id>`).
50    Sync {
51        /// Multiaddr completo, ej. `/ip4/1.2.3.4/tcp/4001/p2p/12D3KooW...`.
52        peer: String,
53    },
54
55    /// Vigila un directorio y re-ingiere automáticamente cualquier
56    /// archivo `.rs` que se cree o modifique. Minga como VCS de fondo:
57    /// el usuario escribe en su editor y el código queda versionado.
58    Watch {
59        /// Directorio a vigilar.
60        dir: PathBuf,
61    },
62
63    /// Monta el repositorio como filesystem FUSE de sólo lectura.
64    /// Cada hash del store se vuelve un archivo navegable con
65    /// `ls`/`cat`. Bloquea hasta `fusermount -u <punto>`.
66    Mount {
67        /// Punto de montaje: un directorio existente.
68        point: PathBuf,
69    },
70}
71
72fn main() -> ExitCode {
73    match run() {
74        Ok(()) => ExitCode::SUCCESS,
75        Err(e) => {
76            eprintln!("error: {}", e);
77            ExitCode::FAILURE
78        }
79    }
80}
81
82fn run() -> Result<(), CliError> {
83    let cli = Cli::parse();
84
85    match cli.command {
86        Command::Init => {
87            let pass = prompt_passphrase_with_confirm()?;
88            let did = cmd_init(&cli.repo, &pass)?;
89            println!("Repo inicializado en {}", cli.repo.display());
90            println!("DID: {}", did);
91        }
92        Command::Status => {
93            let pass = prompt_passphrase()?;
94            let s = cmd_status(&cli.repo, &pass)?;
95            println!("DID: {}", s.did);
96            println!("MST: {} claves", s.mst_len);
97            println!("Nodos almacenados: {}", s.nodes_len);
98            println!("Atestaciones: {}", s.attestations_len);
99        }
100        Command::Ingest { file } => {
101            let pass = prompt_passphrase()?;
102            let r = cmd_ingest(&cli.repo, &pass, &file)?;
103            println!("Ingerido: {}", file.display());
104            println!("Hash: {}", r.hash);
105            println!("Firmado por: {}", r.did);
106        }
107        Command::Listen { addr } => {
108            let pass = prompt_passphrase()?;
109            let rt = tokio::runtime::Runtime::new()
110                .map_err(|e| CliError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?;
111            rt.block_on(cmd_listen(&cli.repo, &pass, &addr))?;
112        }
113        Command::Sync { peer } => {
114            let pass = prompt_passphrase()?;
115            let rt = tokio::runtime::Runtime::new()
116                .map_err(|e| CliError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?;
117            rt.block_on(cmd_sync(&cli.repo, &pass, &peer))?;
118            println!("Sync completo.");
119        }
120        Command::Watch { dir } => {
121            let pass = prompt_passphrase()?;
122            println!("Vigilando {}. Ctrl+C para parar.", dir.display());
123            let rt = tokio::runtime::Runtime::new()
124                .map_err(|e| CliError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?;
125            rt.block_on(cmd_watch(&cli.repo, &pass, &dir))?;
126        }
127        Command::Mount { point } => {
128            let pass = prompt_passphrase()?;
129            println!(
130                "Montando {} en {}. `fusermount -u {}` para desmontar.",
131                cli.repo.display(),
132                point.display(),
133                point.display()
134            );
135            cmd_mount(&cli.repo, &pass, &point)?;
136        }
137    }
138    Ok(())
139}
140
141fn prompt_passphrase() -> Result<String, CliError> {
142    let pass = rpassword::prompt_password("Passphrase: ")
143        .map_err(CliError::Io)?;
144    Ok(pass)
145}
146
147fn prompt_passphrase_with_confirm() -> Result<String, CliError> {
148    let pass = rpassword::prompt_password("Passphrase nueva: ")
149        .map_err(CliError::Io)?;
150    let conf = rpassword::prompt_password("Confirma: ")
151        .map_err(CliError::Io)?;
152    if pass != conf {
153        return Err(CliError::Io(std::io::Error::new(
154            std::io::ErrorKind::InvalidInput,
155            "passphrases no coinciden",
156        )));
157    }
158    Ok(pass)
159}