% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/01_labels.R
\name{label_scheme}
\alias{label_scheme}
\alias{linear_labels}
\alias{cyclical_labels}
\title{Describe a granule's label scheme}
\usage{
label_scheme(
  start = 0L,
  vocab = NULL,
  transform = NULL,
  width = NULL,
  locale = NULL
)

linear_labels(granule, ...)

cyclical_labels(granule, cycle, ...)
}
\arguments{
\item{start}{Author-fixed. Raw index 0 displays as \code{start}, shared by
numeric rendering and vocab indexing (e.g. \code{start = 1L} makes January,
raw index 0, display as \code{"1"} and index \code{vocab()[[1]]}). A caller can't
override this: get it wrong and dates are wrong, not just styled
differently.}

\item{vocab}{\code{NULL} (numeric only), or a function \verb{function(type = NULL, locale = NULL)} returning either the full named list of renderings for
one locale (\code{type = NULL}), or one rendering (e.g.
\code{vocab("abbreviated")}). \code{\link[=vocab_table]{vocab_table()}} builds this from a plain
lookup table; see "Locale specific labels" below for how to specify labels.}

\item{transform}{\code{NULL} (plain \code{start} shift), or \verb{list(encode = function(i, at = NULL) -> vocab index, decode = function(d, at = NULL) -> raw i)}, overriding the indexing used for named rendering only. Both
functions are authored scalar (\code{if}/\code{return}) and vectorized
internally. See "Irregular cycles" below.}

\item{width, locale}{Caller-overridable defaults for \code{cyc(granule, cycle, width = ...)}/\code{locale = ...} at format/parse time, not calendar facts.
\code{width} zero-pads numeric rendering (e.g. \code{width = 2L} for \code{"02"});
\code{locale} selects which \code{vocab()} entry to use by default.}

\item{granule}{A time granule instance (e.g. \code{month(1L)}).}

\item{...}{Unused; only present for S7 dispatch.}

\item{cycle}{For \code{cyclical_labels()}, the coarser granule instance this
cycle repeats within (e.g. \code{year(1L)}).}
}
\value{
A function \code{function(granule, cycle) list(start =, vocab =, transform =, width =, locale =)}, assignable directly as a
\code{\link[=linear_labels]{linear_labels()}}/\code{\link[=cyclical_labels]{cyclical_labels()}} method. \code{granule}/\code{cycle} are only
used for dispatch: a scheme describes a granule class, not the instance
a method happens to be called with.

A plain list, as constructed by \code{\link[=label_scheme]{label_scheme()}}.
}
\description{
Builds the function a \code{\link[=linear_labels]{linear_labels()}}/\code{\link[=cyclical_labels]{cyclical_labels()}} method is:
assign its result directly, e.g. \code{method(cyclical_labels, list(granule, cycle)) <- label_scheme(...)}. Three levels, from plain to most irregular:

\code{linear_labels()} and \code{cyclical_labels()} are S7 generics returning a
granule's label scheme: a plain list describing how its internal position
renders as text and parses back (\code{start}, \code{vocab}, \code{transform}, \code{width},
\code{locale}; see \code{\link[=label_scheme]{label_scheme()}}).
}
\details{
\enumerate{
\item Plain numeric. No \code{vocab}: labels are \code{i + start}, optionally
zero-padded to \code{width}. The default for most granules (minutes,
seconds).
\item Named, regular. \code{vocab} supplies the names (see \code{\link[=vocab_table]{vocab_table()}});
\code{start} positions raw index 0 in both the numeric and vocab-indexed
rendering. Covers most named calendar units (months, weekdays).
\item Named, irregular. \code{transform} overrides the plain \code{start} shift with
hand-written \code{encode}/\code{decode}, for cycles where raw index and name
aren't a constant offset apart (e.g. a leap month splitting one name
into two). See "Irregular cycles" below.
}

A scheme is only consulted for named (\code{label = TRUE}) rendering and its
inverse; numeric rendering is always the plain \code{i + start} shift (see
\code{\link[=linear_labels_format]{linear_labels_format()}}). Labels that aren't index-shaped at all (e.g.
"1BC") override the generics directly, documented in \code{\link[=linear_labels]{linear_labels()}}
instead.

\code{\link[=linear_labels_format]{linear_labels_format()}}/\code{\link[=linear_labels_parse]{linear_labels_parse()}} (and their cyclical
counterparts) do the actual rendering/parsing, with the default method
looking up the scheme and applying it. A granule with labels that are
incompatible with label schemes (e.g. 2BC, 1BC, 1, 2, ...) registers a method
directly on those generics instead, skipping the scheme system. See
"Overriding the generics directly" below.
}
\section{Locale specific labels}{

\code{vocab} is a plain closure, not a shared registry, so two calendar
packages can't collide on it. To add locale support to an existing
granule, redefine the \code{linear_labels()}/\code{cyclical_labels()} method and
call the previous one for anything you're not changing: the normal S7
rule that the last \verb{method(...) <-} wins. That needs a hand-written
wrapper rather than assigning \code{label_scheme()}'s result directly, since
overriding one field means fetching the old scheme and changing it:

\if{html}{\out{<div class="sourceCode r">}}\preformatted{scheme <- cyclical_labels(cal_gregorian$month(1L), cal_gregorian$year(1L))
method(cyclical_labels, list(cal_gregorian$month, cal_gregorian$year)) <-
  function(granule, cycle) \{
    scheme$vocab <- function(type = NULL, locale = NULL) \{
      if (identical(locale \%||\% "en-GB", "fr-FR")) \{
        fr <- list(
          wide = c("janvier", "février", "mars", "avril", "mai", "juin",
                   "juillet", "août", "septembre", "octobre", "novembre", "décembre"),
          abbreviated = c("janv.", "févr.", "mars", "avr.", "mai", "juin",
                          "juil.", "août", "sept.", "oct.", "nov.", "déc.")
        )
        return(if (is.null(type)) fr else fr[[type]])
      \}
      scheme$vocab(type, locale)
    \}
    scheme
  \}
}\if{html}{\out{</div>}}

Locale tags follow BCP 47 (\code{"en-GB"}, \code{"fr-FR"}), like ICU, \code{stringi} and
\code{clock}. Type names follow CLDR's wide/abbreviated/narrow, so CLDR data
(e.g. \code{stringi::stri_datetime_symbols()}) works with \code{\link[=vocab_table]{vocab_table()}}
unchanged. Other \code{type} values are allowed too, e.g. \code{"emoji"} for lunar
phases, picked at render time via \code{\link[=cyclical_labels_format]{cyclical_labels_format()}}'s \code{type}
argument.
}

\section{Irregular cycles}{

\code{at} (in \code{transform}'s \code{encode}/\code{decode}, and in
\code{\link[=cyclical_labels_format]{cyclical_labels_format()}}/\code{\link[=cyclical_labels_parse]{cyclical_labels_parse()}}) is the coarser
granule instance's raw position, not epoch-shifted, with \code{n = 1}. It
follows the same convention as \code{\link[=chronon_cardinality]{chronon_cardinality()}}. It lets
\code{transform} pick the right name when that name depends on which cycle
instance a raw index falls in, e.g. the Hebrew calendar's leap year
splitting "Adar" into "Adar I"/"Adar II":

\if{html}{\out{<div class="sourceCode r">}}\preformatted{month_names <- c("Tishrei", "Cheshvan", "Kislev", "Tevet", "Shevat",
                  "Adar", "Adar I", "Adar II",
                  "Nisan", "Iyar", "Sivan", "Tammuz", "Av", "Elul")

method(cyclical_labels, list(cal_hebrew$month, cal_hebrew$year)) <- label_scheme(
  start = 1L,
  vocab = vocab_table(`en-GB` = list(wide = month_names, abbreviated = month_names)),
  transform = list(
    encode = function(i, at) \{
      leap <- is_hebrew_leap_year(at)
      if (i <= 4L) return(i + 1L)
      if (!leap) return(if (i == 5L) 6L else i + 3L)
      if (i == 5L) return(7L)
      if (i == 6L) return(8L)
      i + 2L
    \},
    decode = function(d, at) \{
      leap <- is_hebrew_leap_year(at)
      if (d <= 5L) return(d - 1L)
      if (!leap) \{
        if (d == 6L) return(5L)
        if (d \%in\% c(7L, 8L)) cli::cli_abort("'Adar I'/'Adar II' are not valid outside a leap year.")
        return(d - 3L)
      \}
      if (d == 6L) cli::cli_abort("'Adar' is ambiguous in a leap year, use 'Adar I' or 'Adar II'.")
      if (d == 7L) return(5L)
      if (d == 8L) return(6L)
      d - 2L
    \}
  )
)
}\if{html}{\out{</div>}}

The aborts in \code{decode()} matter: without them, "Adar II" typed in a
common year would fall through to the same offset as plain "Adar" and
silently parse to the wrong month. Only the label layer, which knows
both the text and \code{at}, can catch that. \code{transform}'s leap predicate must
also be the same one \code{\link[=chronon_cardinality]{chronon_cardinality()}} uses for this pair; put it
in one shared helper.
}

\section{Overriding the format and parsing methods}{

\code{linear_labels_format()}/\code{linear_labels_parse()} are ordinary S7
generics with a default method, not the only place formatting logic can
live. So escaping the scheme system for one granule is just registering
a method directly:

\if{html}{\out{<div class="sourceCode r">}}\preformatted{method(linear_labels_format, cal_gregorian$year) <- function(granule, i, ...) \{
  ifelse(i <= 0L, paste0(-i + 1L, "BC"), i)
\}
method(linear_labels_parse, cal_gregorian$year) <- function(granule, ...) \{
  list(
    pattern = "\\\\d+(?:BC)?",
    decode = function(text, at = NULL) \{
      bc <- grepl("BC$", text)
      n <- as.integer(sub("BC$", "", text))
      ifelse(bc, 1L - n, n) - chronon_epoch(granule)
    \}
  )
\}
}\if{html}{\out{</div>}}

\code{decode()} needs \code{- chronon_epoch(granule)} because this method bypasses
the default, which normally does that step (see \code{\link[=linear_labels_format]{linear_labels_format()}}'s
"Epoch shift" section).

The \code{...} on \verb{_format()} is not just style: \code{format()} forwards whatever
\link{component_helpers}' \code{lin()}/\code{cyc()} were called with (\code{label},
\code{abbreviate}, \code{type}, \code{width}, \code{locale}, ...) as named arguments. A
method without \code{...} errors with "unused argument" as soon as a caller
passes something it doesn't declare.

Registering only \code{linear_labels_format()} is fine: that's the same "no
parsing yet" state a fresh granule starts in. But registering it without
\code{linear_labels_parse()} leaves parsing silently wrong instead of just
missing: text like \code{"1BC"} falls through to whatever the granule's
scheme says, usually plain numeric decoding, which doesn't understand
"BC" at all.
}

\examples{
# A scheme for months of the year, using R's localised month names.
S7::method(cyclical_labels, list(cal_gregorian$month, cal_gregorian$year)) <- label_scheme(
  start = 1L, width = 2L,
  vocab = vocab_table(`en-GB` = list(wide = month.name, abbreviated = month.abb))
)

}
\seealso{
\code{\link[=linear_labels]{linear_labels()}}/\code{\link[=cyclical_labels]{cyclical_labels()}}, the generics a scheme is
returned from; \code{\link[=vocab_table]{vocab_table()}} for the common hand-listed name table
case.

\code{\link[=label_scheme]{label_scheme()}} for the fields a method returns;
\code{\link[=linear_labels_format]{linear_labels_format()}}/\code{\link[=cyclical_labels_format]{cyclical_labels_format()}} for the generics
that interpret it; \code{\link[=vocab_table]{vocab_table()}} for the common hand-listed name
table case.
}
