Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Bug, Put Page into its own Variable category #14

Merged
merged 6 commits into from
Sep 2, 2024
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
25 changes: 23 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ use std::num::{NonZeroI16, NonZeroUsize};
use quick_xml::de::{Deserializer, SliceReader};
use serde::{Deserialize, Serialize};
use taxonomy::{
DateVariable, Kind, Locator, NameVariable, NumberVariable, OtherTerm, Term, Variable,
DateVariable, Kind, Locator, NameVariable, NumberOrPageVariable, NumberVariable,
OtherTerm, Term, Variable,
};

use self::util::*;
Expand Down Expand Up @@ -742,6 +743,11 @@ fn minimal(
x: &str,
y: &str,
) -> Result<(), fmt::Error> {
if y.len() > x.len() {
// y is no abbrev. write it
return write!(buf, "{y}");
}

let mut xs = String::new();
let mut ys = String::new();
for (c, d) in x.chars().zip(y.chars()).skip_while(|(c, d)| c == d) {
Expand Down Expand Up @@ -2635,7 +2641,7 @@ pub struct Substitute {
pub struct Label {
/// The variable for which to print the label.
#[serde(rename = "@variable")]
pub variable: NumberVariable,
pub variable: NumberOrPageVariable,
/// The form of the label.
#[serde(flatten)]
pub label: VariablelessLabel,
Expand Down Expand Up @@ -3729,6 +3735,7 @@ mod test {
assert_eq!("13792–803", run(c15, "13792", "13803"));
assert_eq!("1496–1504", run(c15, "1496", "1504"));
assert_eq!("2787–2816", run(c15, "2787", "2816"));
assert_eq!("101–8", run(c15, "101", "108"));

assert_eq!("3–10", run(c16, "3", "10"));
assert_eq!("71–72", run(c16, "71", "72"));
Expand All @@ -3746,6 +3753,7 @@ mod test {
assert_eq!("11564–68", run(c16, "11564", "11568"));
assert_eq!("13792–803", run(c16, "13792", "13803"));
assert_eq!("12991–3001", run(c16, "12991", "13001"));
assert_eq!("12991–123001", run(c16, "12991", "123001"));

assert_eq!("42–45", run(exp, "42", "45"));
assert_eq!("321–328", run(exp, "321", "328"));
Expand All @@ -3761,6 +3769,19 @@ mod test {
assert_eq!("2787–816", run(mi2, "2787", "2816"));
}

/// Tests the bug from PR typst/hayagriva#155
#[test]
fn test_bug_hayagriva_115() {
fn run(format: PageRangeFormat, start: &str, end: &str) -> String {
let mut buf = String::new();
format.format(&mut buf, start, end, None).unwrap();
buf
}
let c16 = PageRangeFormat::Chicago16;

assert_eq!("12991–123001", run(c16, "12991", "123001"));
}

#[test]
fn page_range_prefix() {
fn run(format: PageRangeFormat, start: &str, end: &str) -> String {
Expand Down
82 changes: 67 additions & 15 deletions src/taxonomy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ use serde::{de, Deserialize, Deserializer, Serialize};
pub enum Variable {
/// The set of variables with no other attributes.
Standard(StandardVariable),
/// Variables that can be formatted as numbers.
/// The page variable. Can be formatted as a page range.
///
/// CSL does not distinguish between page ranges and other number variables.
/// See the [`NumberOrPageVariable`] enum for a CSL-like view on the
/// variants.
Page(PageVariable),
/// Variables that can be formatted as numbers and are not page ranges.
Number(NumberVariable),
/// Variables that can be formatted as dates.
Date(DateVariable),
Expand All @@ -39,6 +45,7 @@ impl fmt::Display for Variable {
Self::Number(v) => v.fmt(f),
Self::Date(v) => v.fmt(f),
Self::Name(v) => v.fmt(f),
Self::Page(v) => v.fmt(f),
}
}
}
Expand Down Expand Up @@ -250,6 +257,53 @@ impl fmt::Display for StandardVariable {
}
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
/// Number variables as the CSL spec knows them, though we separate between
/// number variables and page ranges.
#[serde(untagged)]
pub enum NumberOrPageVariable {
/// The page variable.
Page(PageVariable),
/// Variables formattable as numbers.
Number(NumberVariable),
}

impl From<NumberOrPageVariable> for Term {
fn from(value: NumberOrPageVariable) -> Self {
match value {
NumberOrPageVariable::Page(p) => p.into(),
NumberOrPageVariable::Number(n) => n.into(),
}
}
}

/// Variable that can be formatted as page numbers
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
pub enum PageVariable {
#[serde(rename = "page")]
/// Range of pages the item (e.g. a journal article) covers in a container
/// (e.g. a journal issue).
Page,
}

impl From<PageVariable> for Term {
fn from(_: PageVariable) -> Self {
Self::PageVariable
}
}

impl From<PageVariable> for Variable {
fn from(value: PageVariable) -> Self {
Self::Page(value)
}
}

impl fmt::Display for PageVariable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "page")
}
}

/// Variables that can be formatted as numbers.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
Expand Down Expand Up @@ -286,9 +340,6 @@ pub enum NumberVariable {
NumberOfPages,
/// Total number of volumes, used when citing multi-volume books and such.
NumberOfVolumes,
/// Range of pages the item (e.g. a journal article) covers in a container
/// (e.g. a journal issue).
Page,
/// First page of the range of pages the item (e.g. a journal article)
/// covers in a container (e.g. a journal issue).
PageFirst,
Expand Down Expand Up @@ -326,7 +377,6 @@ impl fmt::Display for NumberVariable {
Self::Number => write!(f, "number"),
Self::NumberOfPages => write!(f, "number-of-pages"),
Self::NumberOfVolumes => write!(f, "number-of-volumes"),
Self::Page => write!(f, "page"),
Self::PageFirst => write!(f, "page-first"),
Self::PartNumber => write!(f, "part-number"),
Self::PrintingNumber => write!(f, "printing-number"),
Expand Down Expand Up @@ -526,6 +576,8 @@ pub enum Term {
NameVariable(NameVariable),
/// Variables that can be formatted as numbers.
NumberVariable(NumberVariable),
/// The Page variable.
PageVariable,
/// A locator.
Locator(Locator),
/// Terms that are not defined via types, name or number variables.
Expand Down Expand Up @@ -595,16 +647,16 @@ impl Term {
(
Self::Locator(Locator::Issue),
Self::NumberVariable(NumberVariable::Issue),
) | (
Self::Locator(Locator::Page),
Self::NumberVariable(NumberVariable::Page),
) | (
Self::Locator(Locator::Section),
Self::NumberVariable(NumberVariable::Section),
) | (
Self::Locator(Locator::Volume),
Self::NumberVariable(NumberVariable::Volume),
) | (Self::Locator(Locator::Book), Self::Kind(Kind::Book))
) | (Self::Locator(Locator::Page), Self::PageVariable,)
| (
Self::Locator(Locator::Section),
Self::NumberVariable(NumberVariable::Section),
)
| (
Self::Locator(Locator::Volume),
Self::NumberVariable(NumberVariable::Volume),
)
| (Self::Locator(Locator::Book), Self::Kind(Kind::Book))
| (Self::Locator(Locator::Chapter), Self::Kind(Kind::Chapter))
| (Self::Locator(Locator::Figure), Self::Kind(Kind::Figure))
)
Expand Down
Loading