use crate::cli_outbound::{Chunk, Cli, ExitInfo};
use crate::config_edit::apply::EDITOR_APPLY_FLAG;
use crate::opslog::{self, OpEntry, Origin};
use std::path::Path;
const SUBCOMMAND_CONFIG: &str = "config";
const ENV_EDITOR: &str = "EDITOR";
const ENV_EDIT_SRC: &str = "YOG_EDIT_SRC";
mod staging;
pub use staging::{DraftFile, next_nonce, stage_files, stale_staging, sweep_staging};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EditOrigin {
Advance,
Fork { source: String },
Orphan,
}
impl EditOrigin {
fn flags(&self) -> Vec<String> {
match self {
EditOrigin::Advance => Vec::new(),
EditOrigin::Fork { source } => vec!["--from".to_string(), source.clone()],
EditOrigin::Orphan => vec!["--orphan".to_string()],
}
}
}
fn sh_quote(s: &str) -> String {
format!("'{}'", s.replace('\'', "'\\''"))
}
pub fn editor_env_value(yog_binary: &Path) -> String {
format!(
"{} {EDITOR_APPLY_FLAG}",
sh_quote(&yog_binary.display().to_string())
)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EditPlan {
argv: Vec<String>,
editor: String,
staging: String,
}
impl EditPlan {
pub fn compose(
yog_binary: &Path,
workspace: &Path,
name: &str,
origin: &EditOrigin,
staging_dir: &Path,
) -> Self {
let mut argv = vec![
SUBCOMMAND_CONFIG.to_string(),
workspace.display().to_string(),
name.to_string(),
];
argv.extend(origin.flags());
Self {
argv,
editor: editor_env_value(yog_binary),
staging: staging_dir.display().to_string(),
}
}
#[cfg(test)]
pub(crate) fn argv(&self) -> &[String] {
&self.argv
}
pub fn env(&self) -> [(&str, &str); 2] {
[(ENV_EDITOR, &self.editor), (ENV_EDIT_SRC, &self.staging)]
}
}
pub fn drive(
cli: &Cli,
workspace: &Path,
plan: &EditPlan,
ts: &str,
state_root: &Path,
origin: Origin,
) -> OpEntry {
let args: Vec<&str> = plan.argv.iter().map(String::as_str).collect();
let (stdout, stderr, exit) = match cli.run_env(&plan.env(), &args) {
Ok(stream) => collect(stream),
Err(e) => (String::new(), e.to_string(), -1),
};
let mut argv = vec![cli.binary().display().to_string()];
argv.extend(plan.argv.iter().cloned());
let entry = OpEntry {
ts: ts.to_string(),
argv,
cwd: workspace.display().to_string(),
exit,
stdout,
stderr,
origin,
};
let _ = opslog::append(state_root, &entry);
entry
}
fn collect(stream: crate::cli_outbound::Stream) -> (String, String, i32) {
let (mut out, mut err, mut code) = (Vec::new(), Vec::new(), -1);
for chunk in stream {
match chunk {
Chunk::Stdout(b) => out.extend(b),
Chunk::Stderr(b) => err.extend(b),
Chunk::Exited(ExitInfo::Code(c)) => code = c,
Chunk::Exited(_) => code = -1,
}
}
(
String::from_utf8_lossy(&out).into_owned(),
String::from_utf8_lossy(&err).into_owned(),
code,
)
}
#[cfg(test)]
mod tests;