Refactor command line format enumerator (#1045)

This commit is contained in:
Luni-4 2023-05-23 00:05:13 +02:00 коммит произвёл GitHub
Родитель 25212e8a59
Коммит cf53c6a7a8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 198 добавлений и 91 удалений

Просмотреть файл

@ -1,6 +1,5 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};
use std::str::FromStr;
@ -21,97 +20,24 @@ impl Format {
pub fn dump_formats<T: Serialize>(
&self,
space: &T,
path: &Path,
output_path: &Option<PathBuf>,
space: T,
path: PathBuf,
output_path: Option<&PathBuf>,
pretty: bool,
) -> std::io::Result<()> {
if output_path.is_none() {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
) {
if let Some(output_path) = output_path {
match self {
Format::Cbor => Err(Error::new(
ErrorKind::Other,
"Cbor format cannot be printed to stdout",
)),
Format::Json => {
let json_data = if pretty {
serde_json::to_string_pretty(&space).unwrap()
} else {
serde_json::to_string(&space).unwrap()
};
writeln!(stdout, "{json_data}")
}
Format::Toml => {
let toml_data = if pretty {
toml::to_string_pretty(&space).unwrap()
} else {
toml::to_string(&space).unwrap()
};
writeln!(stdout, "{toml_data}")
}
Format::Yaml => writeln!(stdout, "{}", serde_yaml::to_string(&space).unwrap()),
Self::Cbor => Cbor::with_writer(space, path, output_path),
Self::Json => Json::with_pretty_writer(space, path, output_path, pretty),
Self::Toml => Toml::with_pretty_writer(space, path, output_path, pretty),
Self::Yaml => Yaml::with_writer(space, path, output_path),
}
} else {
let format_ext = match self {
Format::Cbor => ".cbor",
Format::Json => ".json",
Format::Toml => ".toml",
Format::Yaml => ".yml",
};
// Remove root /
let path = path.strip_prefix("/").unwrap_or(path);
// Remove root ./
let path = path.strip_prefix("./").unwrap_or(path);
// Replace .. with . to keep files inside the output folder
let cleaned_path: Vec<&str> = path
.iter()
.map(|os_str| {
let s_str = os_str.to_str().unwrap();
if s_str == ".." {
"."
} else {
s_str
}
})
.collect();
// Create the filename
let filename = cleaned_path.join("/") + format_ext;
// Build the file path
let format_path = output_path.as_ref().unwrap().join(filename);
// Create directories
create_dir_all(format_path.parent().unwrap()).unwrap();
let mut format_file = File::create(format_path)?;
match self {
Format::Cbor => serde_cbor::to_writer(format_file, &space)
.map_err(|e| Error::new(ErrorKind::Other, e.to_string())),
Format::Json => {
if pretty {
serde_json::to_writer_pretty(format_file, &space)
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
} else {
serde_json::to_writer(format_file, &space)
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
}
}
Format::Toml => {
let toml_data = if pretty {
toml::to_string_pretty(&space).unwrap()
} else {
toml::to_string(&space).unwrap()
};
format_file.write_all(toml_data.as_bytes())
}
Format::Yaml => serde_yaml::to_writer(format_file, &space)
.map_err(|e| Error::new(ErrorKind::Other, e.to_string())),
Self::Json => Json::write_on_stdout_pretty(space, pretty),
Self::Toml => Toml::write_on_stdout_pretty(space, pretty),
Self::Yaml => Yaml::write_on_stdout(space),
Self::Cbor => panic!("Cbor format cannot be printed to stdout"),
}
}
}
@ -130,3 +56,184 @@ impl FromStr for Format {
}
}
}
#[inline(always)]
fn print_on_stdout(content: String) {
writeln!(std::io::stdout().lock(), "{content}").unwrap();
}
trait WriteOnStdout {
#[inline(always)]
fn write_on_stdout<T: Serialize>(content: T) {
print_on_stdout(Self::format(content));
}
fn format<T: Serialize>(content: T) -> String;
}
trait WritePrettyOnStdout: WriteOnStdout {
fn write_on_stdout_pretty<T: Serialize>(content: T, pretty: bool) {
print_on_stdout(if pretty {
Self::format_pretty(content)
} else {
Self::format(content)
});
}
fn format_pretty<T: Serialize>(content: T) -> String;
}
fn handle_path(path: PathBuf, output_path: &Path, extension: &str) -> PathBuf {
// Remove root /
let path = path.as_path().strip_prefix("/").unwrap_or(path.as_path());
// Remove root ./
let path = path.strip_prefix("./").unwrap_or(path);
// Replace .. with . to keep files inside the output folder
let cleaned_path: Vec<&str> = path
.iter()
.map(|os_str| {
let s_str = os_str.to_str().unwrap();
if s_str == ".." {
"."
} else {
s_str
}
})
.collect();
// Create the filename
let filename = cleaned_path.join("/") + extension;
// Build the file path
output_path.join(filename)
}
trait WriteFile {
const EXTENSION: &'static str;
fn open_file(path: PathBuf, output_path: &Path) -> File {
// Handle output path
let format_path = handle_path(path, output_path, Self::EXTENSION);
// Create directories
create_dir_all(format_path.parent().unwrap()).unwrap();
File::create(format_path).unwrap()
}
fn with_writer<T: Serialize>(content: T, path: PathBuf, output_path: &Path);
}
trait WritePrettyFile: WriteFile {
fn with_pretty_writer<T: Serialize>(
content: T,
path: PathBuf,
output_path: &Path,
pretty: bool,
);
}
struct Json;
impl WriteOnStdout for Json {
fn format<T: Serialize>(content: T) -> String {
serde_json::to_string(&content).unwrap()
}
}
impl WritePrettyOnStdout for Json {
fn format_pretty<T: Serialize>(content: T) -> String {
serde_json::to_string_pretty(&content).unwrap()
}
}
impl WriteFile for Json {
const EXTENSION: &'static str = ".json";
fn with_writer<T: Serialize>(content: T, path: PathBuf, output_path: &Path) {
serde_json::to_writer(Self::open_file(path, output_path), &content).unwrap()
}
}
impl WritePrettyFile for Json {
fn with_pretty_writer<T: Serialize>(
content: T,
path: PathBuf,
output_path: &Path,
pretty: bool,
) {
if pretty {
serde_json::to_writer_pretty(Self::open_file(path, output_path), &content).unwrap();
} else {
Self::with_writer(content, path, output_path);
}
}
}
struct Toml;
impl WriteOnStdout for Toml {
fn format<T: Serialize>(content: T) -> String {
toml::to_string(&content).unwrap()
}
}
impl WritePrettyOnStdout for Toml {
fn format_pretty<T: Serialize>(content: T) -> String {
toml::to_string_pretty(&content).unwrap()
}
}
impl WriteFile for Toml {
const EXTENSION: &'static str = ".toml";
fn with_writer<T: Serialize>(content: T, path: PathBuf, output_path: &Path) {
Self::open_file(path, output_path)
.write_all(Self::format(content).as_bytes())
.unwrap();
}
}
impl WritePrettyFile for Toml {
fn with_pretty_writer<T: Serialize>(
content: T,
path: PathBuf,
output_path: &Path,
pretty: bool,
) {
if pretty {
Self::open_file(path, output_path)
.write_all(Self::format_pretty(&content).as_bytes())
.unwrap();
} else {
Self::with_writer(content, path, output_path);
}
}
}
struct Yaml;
impl WriteOnStdout for Yaml {
fn format<T: Serialize>(content: T) -> String {
serde_yaml::to_string(&content).unwrap()
}
}
impl WriteFile for Yaml {
const EXTENSION: &'static str = ".yml";
fn with_writer<T: Serialize>(content: T, path: PathBuf, output_path: &Path) {
serde_yaml::to_writer(Self::open_file(path, output_path), &content).unwrap()
}
}
struct Cbor;
impl WriteFile for Cbor {
const EXTENSION: &'static str = ".cbor";
fn with_writer<T: Serialize>(content: T, path: PathBuf, output_path: &Path) {
serde_cbor::to_writer(Self::open_file(path, output_path), &content).unwrap()
}
}

Просмотреть файл

@ -92,10 +92,9 @@ fn act_on_file(path: PathBuf, cfg: &Config) -> std::io::Result<()> {
} else if cfg.metrics {
if let Some(output_format) = &cfg.output_format {
if let Some(space) = get_function_spaces(&language, source, &path, pr) {
output_format.dump_formats(&space, &path, &cfg.output, cfg.pretty)
} else {
Ok(())
output_format.dump_formats(space, path, cfg.output.as_ref(), cfg.pretty);
}
Ok(())
} else {
let cfg = MetricsCfg { path };
let path = cfg.path.clone();
@ -104,7 +103,8 @@ fn act_on_file(path: PathBuf, cfg: &Config) -> std::io::Result<()> {
} else if cfg.ops {
if let Some(output_format) = &cfg.output_format {
let ops = get_ops(&language, source, &path, pr).unwrap();
output_format.dump_formats(&ops, &path, &cfg.output, cfg.pretty)
output_format.dump_formats(ops, path, cfg.output.as_ref(), cfg.pretty);
Ok(())
} else {
let cfg = OpsCfg { path };
let path = cfg.path.clone();