use crate::diag::{error, At, HintedString, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, Cast, Content, Label, Packed, Show, Smart, StyleChain, Synthesize,
};
use crate::introspection::Locatable;
use crate::model::bibliography::Works;
use crate::model::CslStyle;
use crate::text::{Lang, Region, TextElem};
#[elem(Synthesize)]
pub struct CiteElem {
#[required]
pub key: Label,
pub supplement: Option<Content>,
#[default(Some(CitationForm::Normal))]
pub form: Option<CitationForm>,
#[parse(CslStyle::parse_smart(engine, args)?)]
pub style: Smart<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.push_lang(TextElem::lang_in(styles));
elem.push_region(TextElem::region_in(styles));
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, Show)]
pub struct CiteGroup {
#[required]
pub children: Vec<Packed<CiteElem>>,
}
impl Show for Packed<CiteGroup> {
#[typst_macros::time(name = "cite", span = self.span())]
fn show(&self, engine: &mut Engine, _: StyleChain) -> SourceResult<Content> {
let location = self.location().unwrap();
let span = self.span();
Works::generate(engine.world, engine.introspector)
.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"
)
}