use crate::reference::Reference;
use crate::values::ProcHints;
use citum_schema::options::{
BibliographyLabelMode, BibliographyLabelWrap, CitationLabelMode, Config, LabelWrap,
bibliography::BibliographyConfig,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum MarkerKind {
Numeric,
Alphabetic,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum MarkerValue {
Number {
number: usize,
sub_label: Option<String>,
},
Token(String),
Range(String),
}
impl MarkerValue {
pub(crate) fn as_localized_text(
&self,
digit_system: &citum_schema::locale::DigitSystem,
) -> String {
crate::values::number::localize_digits(self.as_text(), digit_system)
}
pub(crate) fn as_text(&self) -> String {
match self {
MarkerValue::Number { number, sub_label } => match sub_label {
Some(sub_label) => format!("{number}{sub_label}"),
None => number.to_string(),
},
MarkerValue::Token(text) | MarkerValue::Range(text) => text.clone(),
}
}
pub(crate) fn number_parts(&self) -> Option<(usize, Option<&str>)> {
match self {
MarkerValue::Number { number, sub_label } => Some((*number, sub_label.as_deref())),
_ => None,
}
}
pub(crate) fn collapsible_number(&self) -> Option<usize> {
match self.number_parts() {
Some((number, None)) => Some(number),
_ => None,
}
}
pub(crate) fn is_numeric(&self) -> bool {
self.number_parts().is_some()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum MarkerPlacement {
Leading,
Trailing,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct CitationMarkerSpec {
pub kind: MarkerKind,
pub label_wrap: Option<LabelWrap>,
pub item_wrap: Option<LabelWrap>,
pub placement: MarkerPlacement,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct BibliographyMarkerSpec {
pub kind: MarkerKind,
pub wrap: Option<BibliographyLabelWrap>,
pub separator: String,
}
pub(crate) fn citation_label_mode(
config: &Config,
spec: &citum_schema::CitationSpec,
) -> Option<CitationLabelMode> {
spec.options
.as_ref()
.and_then(|options| options.label_mode)
.or_else(|| {
matches!(
config.effective_processing(),
citum_schema::options::Processing::Numeric
)
.then_some(CitationLabelMode::Numeric)
})
}
pub(crate) fn resolve_citation_marker(
config: &Config,
spec: &citum_schema::CitationSpec,
mode: &citum_schema::citation::CitationMode,
) -> Option<CitationMarkerSpec> {
let kind = match citation_label_mode(config, spec)? {
CitationLabelMode::Numeric => MarkerKind::Numeric,
CitationLabelMode::Alphabetic => MarkerKind::Alphabetic,
CitationLabelMode::None => return None,
};
let integral = matches!(mode, citum_schema::citation::CitationMode::Integral);
let options = spec.options.as_ref();
Some(CitationMarkerSpec {
kind,
label_wrap: options.and_then(|options| options.label_wrap),
item_wrap: if integral {
None
} else {
options.and_then(|options| options.item_wrap)
},
placement: if integral {
MarkerPlacement::Trailing
} else {
MarkerPlacement::Leading
},
})
}
pub(crate) fn resolve_bibliography_marker(
config: Option<&BibliographyConfig>,
) -> Option<BibliographyMarkerSpec> {
let config = config?;
let kind = match config.label_mode? {
BibliographyLabelMode::Numeric => MarkerKind::Numeric,
BibliographyLabelMode::Alphabetic => MarkerKind::Alphabetic,
BibliographyLabelMode::None | BibliographyLabelMode::AuthorDate => return None,
};
Some(BibliographyMarkerSpec {
kind,
wrap: config.label_wrap,
separator: config.label_separator.clone().unwrap_or_default(),
})
}
pub(crate) fn marker_value(
kind: MarkerKind,
config: &Config,
reference: &Reference,
citation_number: Option<usize>,
sub_label: Option<String>,
hints: Option<&ProcHints>,
) -> Option<MarkerValue> {
match kind {
MarkerKind::Numeric => {
citation_number.map(|number| MarkerValue::Number { number, sub_label })
}
MarkerKind::Alphabetic => {
let citum_schema::options::Processing::Label(label_config) =
config.processing.as_ref()?
else {
return None;
};
let base = crate::processor::labels::generate_base_label(
reference,
&label_config.effective_params(),
);
if base.is_empty() {
return None;
}
let suffix = hints
.filter(|hints| hints.disamb_condition && hints.group_index > 0)
.and_then(|hints| crate::values::int_to_letter(hints.group_index as u32))
.unwrap_or_default();
Some(MarkerValue::Token(format!("{base}{suffix}")))
}
}
}