Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 53 additions & 17 deletions src/csl/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::any::Any;
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::hash_map::Entry as HmEntry;
use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Write};
use std::hash::Hash;
use std::num::{NonZeroI16, NonZeroUsize};
use std::sync::Arc;
use std::{mem, vec};

use citationberg::taxonomy::{
Expand Down Expand Up @@ -54,7 +56,7 @@ impl<T: EntryLike> Default for BibliographyDriver<'_, T> {
}
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq)]
struct SpeculativeItemRender<'a, T: EntryLike> {
rendered: ElemChildren,
entry: &'a T,
Expand All @@ -70,7 +72,7 @@ struct SpeculativeItemRender<'a, T: EntryLike> {
collapse_verdict: Option<CollapseVerdict>,
}

#[derive(Debug, Hash, PartialEq, Eq)]
#[derive(Debug, PartialEq)]
struct SpeculativeCiteRender<'a, 'b, T: EntryLike> {
items: Vec<SpeculativeItemRender<'a, T>>,
request: &'b CitationRequest<'a, T>,
Expand Down Expand Up @@ -195,7 +197,7 @@ impl<T: EntryLike + Hash + PartialEq + Eq + Debug> BibliographyDriver<'_, T> {
first_name: ctx.writing.first_name.clone(),
delim_override: None,
group_idx: None,
locator: item.locator,
locator: item.locator.clone(),
rendered: ctx.flush(),
hidden: item.hidden,
locale: item.locale.clone(),
Expand Down Expand Up @@ -342,18 +344,19 @@ impl<T: EntryLike + Hash + PartialEq + Eq + Debug> BibliographyDriver<'_, T> {
} else {
Some(&res[i].items[j - 1])
}
.map(|l| CitationItem::with_locator(l.entry, l.locator));
.map(|l| CitationItem::with_locator(l.entry, l.locator.clone()));

res[i].items[j].cite_props.speculative.ibid = IbidState::with_last(
&CitationItem::with_locator(
res[i].items[j].entry,
res[i].items[j].locator,
res[i].items[j].locator.clone(),
),
last.as_ref(),
);

// - Add final locator
res[i].items[j].cite_props.speculative.locator = res[i].items[j].locator;
res[i].items[j].cite_props.speculative.locator =
res[i].items[j].locator.clone();
}
}

Expand Down Expand Up @@ -1137,7 +1140,7 @@ impl<'a, T: EntryLike> InstanceContext<'a, T> {
fn variable_sort_instance(item: &CitationItem<'a, T>, idx: usize) -> Self {
Self::new(
item.entry,
CiteProperties::for_sorting(item.locator, idx),
CiteProperties::for_sorting(item.locator.clone(), idx),
Some(Sorting::Variable),
None,
None,
Expand Down Expand Up @@ -1201,7 +1204,7 @@ impl<'a> StyleContext<'a> {
Context {
instance: InstanceContext::new(
item.entry,
CiteProperties::for_sorting(item.locator, idx),
CiteProperties::for_sorting(item.locator.clone(), idx),
Some(Sorting::Macro),
locale,
term_locale,
Expand Down Expand Up @@ -1451,7 +1454,7 @@ impl<'a> StyleContext<'a> {
}

/// A citation request. A citation can contain references to multiple items.
#[derive(Debug, PartialEq, Eq, Hash)]
#[derive(Debug, PartialEq)]
pub struct CitationRequest<'a, T: EntryLike> {
/// The items to format.
pub items: Vec<CitationItem<'a, T>>,
Expand Down Expand Up @@ -1559,7 +1562,7 @@ impl<'a> BibliographyRequest<'a> {
}

/// A reference to an [`crate::Entry`] within a [`CitationRequest`].
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq)]
pub struct CitationItem<'a, T: EntryLike> {
/// The entry to format.
pub entry: &'a T,
Expand Down Expand Up @@ -2055,7 +2058,7 @@ pub(crate) struct Context<'a, T: EntryLike> {
bibliography: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq)]
struct CiteProperties<'a> {
pub certain: CertainCiteProperties,
pub speculative: SpeculativeCiteProperties<'a>,
Expand Down Expand Up @@ -2105,7 +2108,7 @@ impl CertainCiteProperties {

/// Item properties that can only be determined after one or multiple renders.
/// These require validation.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq)]
struct SpeculativeCiteProperties<'a> {
/// Locator with its type.
///
Expand Down Expand Up @@ -2268,17 +2271,50 @@ impl IbidState {

/// This struct allows to add context to where the information in a cite is
/// found in the source.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq)]
pub struct SpecificLocator<'a>(pub Locator, pub LocatorPayload<'a>);

/// The type of content a `cs:text` element should yield for a locator.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq)]
pub enum LocatorPayload<'a> {
/// Just print the string.
Str(&'a str),
/// An element with the original index of the locator will be yielded. The
/// consumer can then recognize this and replace it with their own content.
Transparent,
Transparent(TransparentLocator),
}

/// Wraps an object that is used to determine whether two locators are the same.
#[derive(Clone)]
pub struct TransparentLocator(Arc<dyn TransparentLocatorPayload>);

impl TransparentLocator {
/// Creates a new [`TransparentLocator`] which wraps the given payload.
pub fn new<T: PartialEq + 'static>(payload: T) -> Self {
Self(Arc::new(payload))
}
}

impl std::fmt::Debug for TransparentLocator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("TransparentLocator").finish_non_exhaustive()
}
}

impl PartialEq for TransparentLocator {
fn eq(&self, other: &Self) -> bool {
TransparentLocatorPayload::dyn_eq(self.0.as_ref(), other.0.as_ref())
}
}

trait TransparentLocatorPayload: Any {
fn dyn_eq(&self, other: &dyn TransparentLocatorPayload) -> bool;
}

impl<T: PartialEq + 'static> TransparentLocatorPayload for T {
fn dyn_eq(&self, other: &dyn TransparentLocatorPayload) -> bool {
Some(self) == (other as &dyn Any).downcast_ref::<Self>()
}
}

impl<'a, T: EntryLike> Context<'a, T> {
Expand Down Expand Up @@ -2537,7 +2573,7 @@ impl<'a, T: EntryLike> Context<'a, T> {
/// Get a term from the style.
fn term(&self, mut term: Term, form: TermForm, plural: bool) -> Option<&'a str> {
if term == Term::NumberVariable(csl_taxonomy::NumberVariable::Locator)
&& let Some(locator) = self.instance.cite_props.speculative.locator
&& let Some(locator) = &self.instance.cite_props.speculative.locator
{
term = locator.0.into();
}
Expand Down Expand Up @@ -3159,7 +3195,7 @@ mod tests {
let locator = SpecificLocator(Locator::Custom, LocatorPayload::Str("12"));
for entry in library.iter() {
driver.citation(CitationRequest::new(
vec![CitationItem::with_locator(entry, Some(locator))],
vec![CitationItem::with_locator(entry, Some(locator.clone()))],
&alphanumeric,
None,
&[],
Expand Down
10 changes: 8 additions & 2 deletions src/csl/rendering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ impl RenderCsl for citationberg::Label {
.cite_props
.speculative
.locator
.as_ref()
.is_some_and(|l| l.0 == Locator::Custom)
{
return (false, UsageInfo::default());
Expand Down Expand Up @@ -1101,8 +1102,13 @@ impl<T: EntryLike> Iterator for BranchConditionIter<'_, '_, T> {
self.idx += 1;

Some(
self.ctx.instance.cite_props.speculative.locator.map(|l| l.0)
== Some(loc),
self.ctx
.instance
.cite_props
.speculative
.locator
.as_ref()
.is_some_and(|l| l.0 == loc),
)
} else {
self.next_case();
Expand Down
24 changes: 14 additions & 10 deletions src/csl/taxonomy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,20 @@ impl<'a, T: EntryLike> InstanceContext<'a, T> {
)))
})
}
NumberVariable::Locator => match self.cite_props.speculative.locator?.1 {
LocatorPayload::Str(l) => Some(NumberVariableResult::from_regular(
Numeric::from_str(l)
.map(|n| MaybeTyped::Typed(Cow::Owned(n)))
.unwrap_or_else(|_| MaybeTyped::String(l.to_owned())),
)),
LocatorPayload::Transparent => Some(NumberVariableResult::Transparent(
self.cite_props.certain.initial_idx,
)),
},
NumberVariable::Locator => {
match &self.cite_props.speculative.locator.as_ref()?.1 {
&LocatorPayload::Str(l) => Some(NumberVariableResult::from_regular(
Numeric::from_str(l)
.map(|n| MaybeTyped::Typed(Cow::Owned(n)))
.unwrap_or_else(|_| MaybeTyped::String(l.to_owned())),
)),
LocatorPayload::Transparent(_) => {
Some(NumberVariableResult::Transparent(
self.cite_props.certain.initial_idx,
))
}
}
}
_ => self
.entry
.resolve_number_variable(variable)
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ pub use csl::{
BibliographyDriver, BibliographyItem, BibliographyRequest, Brackets, BufWriteFormat,
CitationItem, CitationRequest, CitePurpose, Elem, ElemChild, ElemChildren, ElemMeta,
Formatted, Formatting, LocatorPayload, Rendered, RenderedBibliography,
RenderedCitation, SpecificLocator, standalone_citation,
RenderedCitation, SpecificLocator, TransparentLocator, standalone_citation,
};
pub use selectors::{Selector, SelectorError};

Expand Down