use typst_syntax::Spanned;
use crate::diag::{At, HintedString, SourceResult, error};
use crate::engine::Engine;
use crate::foundations::{
Cast, Content, Derived, Label, Packed, Smart, StyleChain, Synthesize, cast, elem,
};
use crate::introspection::Locatable;
use crate::model::bibliography::Works;
use crate::model::{CslSource, CslStyle};
use crate::text::{Lang, Region, TextElem};
#[elem(Locatable, Synthesize)]
pub struct CiteElem {
#[required]
pub key: Label,
pub supplement: Option<Content>,
#[default(Some(CitationForm::Normal))]
pub form: Option<CitationForm>,
#[parse(match args.named::<Spanned<Smart<CslSource>>>("style")? {
Some(Spanned { v: Smart::Custom(source), span }) => Some(Smart::Custom(
CslStyle::load(engine, Spanned::new(source, span))?
)),
Some(Spanned { v: Smart::Auto, .. }) => Some(Smart::Auto),
None => None,
})]
pub style: Smart<Derived<CslSource, CslStyle>>,
#[internal]
#[synthesized]
pub lang: Lang,
#[internal]
#[synthesized]
pub region: Option<Region>,
}
impl Synthesize for Packed<CiteElem> {
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
let elem = self.as_mut();
elem.lang = Some(styles.get(TextElem::lang));
elem.region = Some(styles.get(TextElem::region));
Ok(())
}
}
cast! {
CiteElem,
v: Content => v.unpack::<Self>().map_err(|_| "expected citation")?,
}
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Cast)]
pub enum CitationForm {
#[default]
Normal,
Prose,
Full,
Author,
Year,
}
#[elem(Locatable)]
pub struct CiteGroup {
#[required]
pub children: Vec<Packed<CiteElem>>,
}
impl Packed<CiteGroup> {
pub fn realize(&self, engine: &mut Engine) -> SourceResult<Content> {
let location = self.location().unwrap();
let span = self.span();
Works::generate(engine)
.at(span)?
.citations
.get(&location)
.cloned()
.ok_or_else(failed_to_format_citation)
.at(span)?
}
}
#[cold]
fn failed_to_format_citation() -> HintedString {
error!(
"cannot format citation in isolation";
hint: "check whether this citation is measured \
without being inserted into the document"
)
}