use indoc::formatdoc;
use itertools::Itertools;
use nom::AsChar;
use crate::parsing::parse_config;
use crate::prelude::PluginConfig;
use crate::processing::{ProcessingError, convert_nodes_into_plugin_source};
pub(crate) const REQUIRED_DERIVES: &[&str] =
&["Hash", "Default", "Debug", "Clone", "PartialEq", "Eq"];
pub(crate) fn get_package_info() -> String {
let pkg = env!("CARGO_PKG_NAME");
#[cfg(not(test))]
let version = env!("CARGO_PKG_VERSION");
#[cfg(test)]
let version = "[CARGO_PKG_VERSION]";
format!("{pkg} v{version}")
}
pub(crate) fn generate_debug_info(src_path: &str, source: &str) -> String {
let lines = source.lines().map(|line| format!("// {line}")).join("\n");
let pkg_info = get_package_info();
formatdoc! {"
// generated by {pkg_info}
// src: {src_path}
{lines}
"}
}
#[cfg(feature = "rustfmt")]
fn try_format_source(source: &str) -> std::io::Result<String> {
duct::cmd!("rustfmt")
.stdin_bytes(source)
.stderr_to_stdout()
.read()
}
pub(crate) fn format_source<S: AsRef<str>>(source: S) -> String {
let source = source.as_ref();
#[cfg(feature = "rustfmt")]
let source = try_format_source(source).unwrap_or_else(|_| source.to_owned());
#[cfg(not(feature = "rustfmt"))]
let source = source.to_owned();
if source.ends_with(|c: char| c.is_newline()) {
source
} else {
source + "\n"
}
}
pub(crate) fn generate_state_plugin_source(
input_source: &str,
plugin_config: PluginConfig,
src_path: Option<&str>,
) -> Result<String, ProcessingError> {
let (unparsed, nodes) = parse_config(input_source)?;
let mut output = convert_nodes_into_plugin_source(nodes, plugin_config)?;
#[cfg(test)]
{
use speculoos::assert_that;
use speculoos::prelude::VecAssertions;
assert_that!(output.matches(" mod ").collect_vec()).has_length(1);
}
output = if let Some(src_path) = src_path {
if !unparsed.trim().is_empty() {
return Err(ProcessingError::InvalidConfig(unparsed.to_string()));
}
let debug_info = generate_debug_info(src_path, input_source);
[debug_info, output].join("\n")
} else {
[unparsed, &output].join("\n")
};
Ok(format_source(output))
}