use crate::{AppendAsLine, PatternWriter};
use aeruginous_io::OptionReader;
use std::path::PathBuf;
use sysexits::Result;
#[derive(clap::Parser, Clone)]
#[command(visible_aliases = ["cffref", "cff-ref", "cff-reference"])]
pub struct Cffreference {
#[arg(long = "input", short)]
input_file: Option<PathBuf>,
#[arg(long = "output", short)]
output_file: Option<PathBuf>,
}
impl Cffreference {
pub fn main(&self) -> Result<()> {
self.wrap().main()
}
#[must_use]
pub const fn new(
input_file: Option<PathBuf>,
output_file: Option<PathBuf>,
) -> Self {
Self {
input_file,
output_file,
}
}
fn wrap(&self) -> Logic {
Logic {
cff_data: String::new(),
cff_reference: String::new(),
cli: self.clone(),
preferred_citation_reached: false,
properties: Properties::default(),
references_reached: false,
}
}
}
struct Logic {
cff_data: String,
cff_reference: String,
cli: Cffreference,
preferred_citation_reached: bool,
properties: Properties,
references_reached: bool,
}
impl Logic {
fn extract(&mut self) -> String {
if self.properties.has_preferred_citation() {
for line in self.cff_data.lines() {
if self.preferred_citation_reached && line.starts_with(' ') {
self.cff_reference
.push_str(&(" ".to_string() + line + "\n"));
} else if self.preferred_citation_reached {
self.preferred_citation_reached = false;
}
if line.starts_with("preferred-citation:") {
self.preferred_citation_reached = true;
}
}
let mut lines = self.cff_reference.lines();
lines
.next()
.map_or_else(String::new, |l| format!(" - {}\n", l.trim()))
+ &lines.fold(String::new(), |s, l| s + l + "\n")
} else {
let mut lines = self.cff_data.lines();
(if self.properties.has_type() {
lines
.next()
.map_or_else(String::new, |l| format!(" - {}\n", l.trim()))
} else {
" - type: software\n".to_string()
}) + &lines.fold(String::new(), |s, l| s + " " + l + "\n")
}
}
fn main(&mut self) -> Result<()> {
self.read()?;
self.cli
.output_file
.clone()
.append(Box::new(self.extract()))
}
fn read(&mut self) -> Result<()> {
for line in self
.cli
.input_file
.read_loudly(std::io::stdin().lock())?
.lines()
{
if self.references_reached
&& !matches!(line.chars().next(), Some(' ' | '-'))
{
self.references_reached = false;
}
if !line.is_empty()
&& !line.starts_with('#')
&& !line.starts_with("---")
&& !line.starts_with("...")
&& !line.starts_with("cff-version:")
&& !line.starts_with("message:")
&& !line.starts_with("references:")
&& !self.references_reached
{
if line.starts_with("preferred-citation:") {
self.properties.find_preferred_citation();
} else if line.starts_with("type:") {
self.properties.find_type();
}
self.cff_data.append_as_line(line);
} else if line.starts_with("references:") {
self.references_reached = true;
}
}
Ok(())
}
}
#[derive(Default)]
enum Properties {
BothPreferredCitationAndType,
#[default]
NeitherPreferredCitationNorType,
PreferredCitation,
Type,
}
impl Properties {
fn find_preferred_citation(&mut self) {
if matches!(self, Self::NeitherPreferredCitationNorType) {
*self = Self::PreferredCitation;
} else if matches!(self, Self::Type) {
*self = Self::BothPreferredCitationAndType;
}
}
fn find_type(&mut self) {
if matches!(self, Self::NeitherPreferredCitationNorType) {
*self = Self::Type;
} else if matches!(self, Self::PreferredCitation) {
*self = Self::BothPreferredCitationAndType;
}
}
const fn has_preferred_citation(&self) -> bool {
matches!(
self,
Self::PreferredCitation | Self::BothPreferredCitationAndType
)
}
const fn has_type(&self) -> bool {
matches!(self, Self::Type | Self::BothPreferredCitationAndType)
}
}