feat(minga): minga-vfs — proyecta el repo como filesystem FUSE
minga-vfs deja de ser un stub: monta el repositorio direccionado por contenido como un filesystem FUSE de sólo lectura. roots/<hash> da el código fuente reconstruido (formato normalizado) de cada raíz del MST; cas/<hash> resuelve cualquier hash bajo demanda como S-expression. Capas separadas: render (SemanticNode→texto, puro) + source (contrato NodeSource, backends sled/memoria) + fs (único módulo con fuser). Nuevo subcomando `minga mount <punto>`. Dep fuser 0.15 sin libfuse-dev (default-features = false). 14 tests nuevos, sin regresión en minga-cli. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@ version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
description = "CLI de Minga: init, status, ingest, listen, sync."
|
||||
description = "CLI de Minga: init, status, ingest, listen, sync, watch, mount."
|
||||
|
||||
[[bin]]
|
||||
name = "minga"
|
||||
@@ -14,6 +14,7 @@ path = "src/main.rs"
|
||||
minga-core = { path = "../minga-core" }
|
||||
minga-p2p = { path = "../minga-p2p" }
|
||||
minga-store = { path = "../minga-store" }
|
||||
minga-vfs = { path = "../minga-vfs" }
|
||||
clap = { workspace = true }
|
||||
rpassword = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
||||
@@ -97,6 +97,23 @@ pub fn cmd_ingest(
|
||||
})
|
||||
}
|
||||
|
||||
/// `minga mount <punto>`: monta el repositorio como un filesystem FUSE
|
||||
/// de sólo lectura. Cada hash del store se vuelve un archivo
|
||||
/// navegable con `ls`/`cat`. Bloquea hasta que se desmonte el punto
|
||||
/// (`fusermount -u <punto>` o una señal al proceso).
|
||||
pub fn cmd_mount(
|
||||
repo_path: &Path,
|
||||
passphrase: &str,
|
||||
mountpoint: &Path,
|
||||
) -> Result<(), CliError> {
|
||||
// Cargar el keypair valida la passphrase: montar es navegar el
|
||||
// repo, así que pedimos la misma credencial que `status`/`watch`.
|
||||
let _keypair = keypair_file::load(repo_path.join(KEYPAIR_FILENAME), passphrase)?;
|
||||
let repo = PersistentRepo::open(repo_path.join(REPO_DIRNAME))?;
|
||||
minga_vfs::mount(minga_vfs::RepoSource::new(repo), mountpoint)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Detecta el dialecto desde la extensión del archivo. Error si la
|
||||
/// extensión no corresponde a un lenguaje soportado.
|
||||
fn detect_dialect(file: &Path) -> Result<parse::Dialect, CliError> {
|
||||
|
||||
@@ -10,6 +10,7 @@ pub mod commands;
|
||||
pub mod error;
|
||||
|
||||
pub use commands::{
|
||||
cmd_ingest, cmd_init, cmd_listen, cmd_status, cmd_sync, cmd_watch, IngestResult, RepoStatus,
|
||||
cmd_ingest, cmd_init, cmd_listen, cmd_mount, cmd_status, cmd_sync, cmd_watch, IngestResult,
|
||||
RepoStatus,
|
||||
};
|
||||
pub use error::CliError;
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::process::ExitCode;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use minga_cli::{
|
||||
cmd_ingest, cmd_init, cmd_listen, cmd_status, cmd_sync, cmd_watch, CliError,
|
||||
cmd_ingest, cmd_init, cmd_listen, cmd_mount, cmd_status, cmd_sync, cmd_watch, CliError,
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
@@ -59,6 +59,14 @@ enum Command {
|
||||
/// Directorio a vigilar.
|
||||
dir: PathBuf,
|
||||
},
|
||||
|
||||
/// Monta el repositorio como filesystem FUSE de sólo lectura.
|
||||
/// Cada hash del store se vuelve un archivo navegable con
|
||||
/// `ls`/`cat`. Bloquea hasta `fusermount -u <punto>`.
|
||||
Mount {
|
||||
/// Punto de montaje: un directorio existente.
|
||||
point: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
@@ -116,6 +124,16 @@ fn run() -> Result<(), CliError> {
|
||||
.map_err(|e| CliError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?;
|
||||
rt.block_on(cmd_watch(&cli.repo, &pass, &dir))?;
|
||||
}
|
||||
Command::Mount { point } => {
|
||||
let pass = prompt_passphrase()?;
|
||||
println!(
|
||||
"Montando {} en {}. `fusermount -u {}` para desmontar.",
|
||||
cli.repo.display(),
|
||||
point.display(),
|
||||
point.display()
|
||||
);
|
||||
cmd_mount(&cli.repo, &pass, &point)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user