use crate::commit_command::effective_directory;
use crate::forwarding::git_command;
use crate::lfs_pre_push::plan_failed_message;
use crate::lfs_push_precheck::lfs_refusal;
use crate::lfs_object_plan::narrow_plan;
use crate::lfs_upload::{run_upload_interactive, submodule_notice};
use crate::git_system_config::with_real_git;
use crate::repo_read_layout::read_layout_after_success_for;
use crate::lfs_upload_plan::{GitQuery, PlanError, Settings, UploadPlan, plan_upload, read_settings};
use std::ffi::OsString;
use std::io::Write;
use std::path::Path;
pub struct InteractiveUpload<'a> {
pub prefix: &'a [OsString],
pub region: &'a [OsString],
pub real_git: &'a Path,
pub environment: &'a [(OsString, OsString)],
pub overlay: &'a [(OsString, OsString)],
pub settings: Option<&'a [(String, String)]>,
}
struct InteractiveQuery<'a> {
request: &'a InteractiveUpload<'a>,
}
impl GitQuery for InteractiveQuery<'_> {
fn ask(&mut self, query_arguments: &[&str]) -> Result<String, String> {
let mut arguments: Vec<OsString> = self.request.prefix.to_vec();
arguments.extend(query_arguments.iter().map(OsString::from));
let output = match git_command(self.request.real_git, arguments.as_slice(), self.request.overlay).output() {
Ok(done) => done,
Err(error) => return Err(error.to_string()),
};
let text: String = String::from_utf8_lossy(output.stdout.as_slice()).trim().to_owned();
return match output.status.code() {
Some(0) => Ok(text),
Some(1) if text.is_empty() => Ok(text),
code => Err(format!("exited with {code:?}: {}", String::from_utf8_lossy(output.stderr.as_slice()).trim())),
};
}
}
pub fn upload_first(request: &InteractiveUpload<'_>) -> Option<i32> {
if let Some(refusal) = lfs_refusal(request.prefix, request.region, request.environment) {
let _written = std::io::stderr().write_all(refusal.as_bytes());
return Some(1);
}
let mut query: InteractiveQuery<'_> = InteractiveQuery { request };
let directory: std::path::PathBuf =
effective_directory(std::env::current_dir().unwrap_or_default().as_path(), request.prefix);
let read: Result<Settings, PlanError> = match request.settings {
Some(held) => Ok(held.to_vec()),
None => read_settings(&mut query),
};
let planned: Result<Option<UploadPlan>, PlanError> = match read {
Ok(settings) => plan_upload(request.prefix, request.region, request.environment, directory.as_path(), settings.as_slice(), &mut query),
Err(error) => Err(error),
};
match planned {
Ok(Some(plan)) => {
let layout = read_layout_after_success_for(request.prefix, with_real_git(request.overlay, request.real_git).as_slice());
let submodules: bool = plan.submodules;
let Some(narrowed) = narrow_plan(plan, layout.as_ref()) else {
if submodules {
let _written = std::io::stderr().write_all(submodule_notice().as_bytes());
}
return None;
};
return run_upload_interactive(
&narrowed,
request.prefix,
directory.as_path(),
request.real_git,
request.environment,
request.overlay,
&mut std::io::stderr(),
);
}
Ok(None) => return None,
Err(error) => {
let _written = std::io::stderr().write_all(plan_failed_message(error.0.as_str()).as_bytes());
return Some(1);
}
}
}