use std::io::{self, Write};
use std::path::{Path, PathBuf};
use anyhow::{Result, anyhow};
use shuck_formatter::{FormatError, FormattedSource, format_source, source_is_formatted};
use crate::ExitStatus;
use crate::args::FormatCommand;
use crate::commands::format::{FormatMode, unified_diff, write_parse_error_line};
use crate::config::{
ConfigArguments, resolve_project_root_for_file, resolve_project_root_for_input,
};
use crate::format_settings::resolve_project_format_settings;
use crate::stdin::read_from_stdin;
pub(crate) fn format_stdin(
args: FormatCommand,
config_arguments: &ConfigArguments,
) -> Result<ExitStatus> {
let mode = FormatMode::from_cli(&args);
let source = read_from_stdin()?;
let path = args.stdin_filename.as_deref();
let display_path = display_path(path);
let cwd = std::env::current_dir()?;
let project_root = stdin_project_root(path, &cwd, config_arguments.use_config_roots())?;
let options = resolve_project_format_settings(
&project_root,
config_arguments,
args.format_settings_patch(),
)?
.to_shell_format_options();
if matches!(mode, FormatMode::Check) {
return match source_is_formatted(&source, path, &options) {
Ok(true) => Ok(ExitStatus::Success),
Ok(false) => Ok(ExitStatus::Failure),
Err(FormatError::Parse {
message,
line,
column,
}) => {
let mut stdout = io::stdout().lock();
write_parse_error_line(&mut stdout, &display_path, line, column, &message)?;
Ok(ExitStatus::Error)
}
Err(FormatError::Internal(message)) => Err(anyhow!(message)),
};
}
match format_source(&source, path, &options) {
Ok(FormattedSource::Unchanged) => {
if mode.is_write() {
let mut stdout = io::stdout().lock();
stdout.write_all(source.as_bytes())?;
}
Ok(ExitStatus::Success)
}
Ok(FormattedSource::Formatted(formatted)) => match mode {
FormatMode::Write => {
let mut stdout = io::stdout().lock();
stdout.write_all(formatted.as_bytes())?;
Ok(ExitStatus::Success)
}
FormatMode::Check => Ok(ExitStatus::Failure),
FormatMode::Diff => {
let mut stdout = io::stdout().lock();
write!(
&mut stdout,
"{}",
unified_diff(&display_path, &source, &formatted)
)?;
Ok(ExitStatus::Failure)
}
},
Err(FormatError::Parse {
message,
line,
column,
}) => {
let mut stdout = io::stdout().lock();
write_parse_error_line(&mut stdout, &display_path, line, column, &message)?;
Ok(ExitStatus::Error)
}
Err(FormatError::Internal(message)) => Err(anyhow!(message)),
}
}
fn display_path(path: Option<&Path>) -> PathBuf {
path.map(Path::to_path_buf)
.unwrap_or_else(|| PathBuf::from("<stdin>"))
}
fn stdin_project_root(path: Option<&Path>, cwd: &Path, use_config_roots: bool) -> Result<PathBuf> {
match path {
Some(path) => {
let path = if path.is_absolute() {
path.to_path_buf()
} else {
cwd.join(path)
};
Ok(resolve_project_root_for_file(&path, cwd, use_config_roots)?)
}
None => Ok(resolve_project_root_for_input(cwd, use_config_roots)?),
}
}