Files
typikon/src/rules/orthodox.rs
T

251 lines
10 KiB
Rust
Raw Normal View History

2026-09-18 11:13:10 -04:00
//! The Typikon's fast, as the Orthodox Church keeps it.
//!
//! This is the discipline printed in the Typikon and reproduced, with small
//! variations, by every Orthodox jurisdiction's published calendar. It binds no
//! one in the canonical sense: the Orthodox Church legislates fasting as
//! tradition rather than as precept, so every day here is reported as
//! customary. That is not a softening. It is the difference between a rule of
//! life and a law, and the Eastern Catholic churches, which do have law about
//! this, are handled in their own module.
//!
//! A saint of vigil or polyeleos rank lifts the weekday discipline of a fast:
//! to wine and oil inside the Great Fast, as far as fish in the three lesser
//! fasts. That is why the printed calendars mark so many Wednesdays in the
//! Nativity Fast for wine and oil, and it is the rank in the menologion that
//! decides it.
//!
//! Where usages differ, this follows the common published rule and says so in
//! the reason. The known divergences:
//!
//! - **Great Thursday** is given wine and oil here. Some typika keep xerophagy.
//! - **The Exaltation of the Cross** and the **Beheading of the Forerunner**
//! are given wine and oil. Several jurisdictions print them as strict.
//! - **Lazarus Saturday** is given wine and oil; caviar is the traditional
//! addition, which this vocabulary has no level for.
//! - **Great Week** is kept at xerophagy on its weekdays whatever saint falls
//! in it, because the days of the Passion outrank the menologion.
//! - **Monastic practice**, and the Holy Mountain in particular, is stricter
//! than any of this throughout.
//!
//! A calendar cannot settle those. Your bishop and your spiritual father can.
use super::{Context, Rule, Vocabulary};
use crate::calendars::Weekday;
use crate::fasting::{Fast, Level};
use crate::menologion::Rank;
use crate::seasons::Season;
fn label(level: Level) -> &'static str {
match level {
Level::Strict => "no food until evening",
Level::Xerophagy => "xerophagy \u{2014} no oil or wine",
Level::WineOil => "wine and oil permitted",
Level::Fish => "fish, wine and oil",
Level::Dairy => "dairy and eggs; no meat",
Level::None => "no fast",
}
}
fn emoji(level: Level) -> &'static str {
match level {
Level::Strict => "\u{26d4}",
Level::Xerophagy => "\u{1f96c}",
Level::WineOil => "\u{1fad2}",
Level::Fish => "\u{1f41f}",
Level::Dairy => "\u{1f9c0}",
Level::None => "\u{1f356}",
}
}
/// How far a feast lifts a fast day.
fn relaxed_by(rank: Option<Rank>) -> Option<Level> {
match rank {
Some(Rank::Great) => Some(Level::Fish),
Some(Rank::Vigil) | Some(Rank::Polyeleos) => Some(Level::WineOil),
None => None,
}
}
/// Relax `level` for the day's saint, never past `cap`, never tightening.
fn for_feast(level: Level, ctx: &Context, cap: Level, reason: &str) -> Fast {
let Some(target) = relaxed_by(ctx.rank) else {
return Fast::customary(level, reason);
};
let lifted = level.relaxed_to(target, cap);
if lifted == level {
return Fast::customary(level, reason);
}
Fast::customary(lifted, &format!("{} \u{2014} the fast is relaxed", ctx.feast))
}
pub fn fast_for(ctx: &Context) -> Fast {
let md = ctx.fixed;
let mv = ctx.mv;
let weekend = ctx.is_weekend();
// The fast-free stretches, settled before anything else can override them.
// A Friday inside Bright Week is not a fast day, and the ordinary Friday
// rule further down would say otherwise.
match ctx.season {
Some(Season::BrightWeek) => return Fast::free("Bright Week \u{2014} no fasting"),
Some(Season::PentecostWeek) => {
return Fast::free("the week after Pentecost \u{2014} no fasting")
}
Some(Season::Christmastide) => {
return Fast::free("the Nativity to the eve of Theophany")
}
_ => {}
}
if ctx.within(mv.publican_pharisee, 6) {
// Fast-free precisely because the Pharisee boasted of fasting twice a week.
return Fast::free("fast-free week (the Publican and the Pharisee)");
}
// The Great Fast and Great Week.
if matches!(ctx.season, Some(Season::GreatFast) | Some(Season::GreatWeek)) {
let season = ctx.season.unwrap();
// The Annunciation is tested before the strict days, not after. It can
// land on Great Friday or Great Saturday, and when it does the feast
// still lifts the total fast to wine and oil: it does not vanish.
if md == (3, 25) {
if ctx.civil == mv.great_friday || ctx.civil == mv.holy_saturday {
return Fast::customary(
Level::WineOil,
"the Annunciation, on a day of the total fast",
);
}
return Fast::customary(Level::Fish, "the Annunciation");
}
if ctx.civil == mv.palm_sunday {
return Fast::customary(Level::Fish, "Palm Sunday");
}
if ctx.civil == mv.lazarus_saturday {
return Fast::customary(Level::WineOil, "Lazarus Saturday");
}
if ctx.civil == mv.clean_monday {
return Fast::customary(Level::Strict, "the first day of the Great Fast");
}
if ctx.civil == mv.great_friday {
return Fast::customary(Level::Strict, "Great and Holy Friday");
}
if ctx.civil == mv.holy_saturday {
// The one Saturday of the year kept without oil.
return Fast::customary(Level::Xerophagy, "Great and Holy Saturday");
}
if ctx.civil == mv.great_thursday {
return Fast::customary(Level::WineOil, "Great and Holy Thursday");
}
if season == Season::GreatWeek {
return Fast::customary(
Level::Xerophagy,
&format!("a weekday of the {}", season.name()),
);
}
if weekend {
return Fast::customary(
Level::WineOil,
&format!("Saturday and Sunday of the {}", season.name()),
);
}
return for_feast(
Level::Xerophagy,
ctx,
Level::WineOil,
&format!("a weekday of the {}", season.name()),
);
}
// Cheesefare: meat is set aside for good, everything else stays,
// Wednesday and Friday included.
if ctx.season == Some(Season::CheesefareWeek) {
return Fast::customary(Level::Dairy, "Cheesefare Week");
}
if ctx.civil == mv.meatfare {
return Fast::free("Meatfare Sunday \u{2014} the last day of meat");
}
// The Nativity Fast, which tightens for its last five days.
if ctx.season == Some(Season::NativityFast) {
if md == (12, 24) {
// The eve is relaxed to wine and oil on a Saturday or Sunday,
// because the Liturgy of St Basil is served with Vespers.
if weekend {
return Fast::customary(Level::WineOil, "the eve of the Nativity, on a weekend");
}
return Fast::customary(Level::Xerophagy, "the eve of the Nativity");
}
if md == (11, 21) {
return Fast::customary(Level::Fish, "the Entrance of the Theotokos");
}
if md >= (12, 20) {
if matches!(ctx.weekday, Weekday::Tuesday | Weekday::Thursday) || weekend {
return Fast::customary(Level::WineOil, "the forefeast of the Nativity");
}
return for_feast(Level::Xerophagy, ctx, Level::WineOil, "the forefeast of the Nativity");
}
if weekend {
return Fast::customary(Level::Fish, "Saturday and Sunday of the Nativity Fast");
}
if matches!(ctx.weekday, Weekday::Tuesday | Weekday::Thursday) {
return for_feast(Level::WineOil, ctx, Level::Fish, "Tuesday and Thursday of the Nativity Fast");
}
return for_feast(Level::Xerophagy, ctx, Level::Fish, "Monday, Wednesday and Friday of the Nativity Fast");
}
// The Apostles' Fast, kept the same way.
if ctx.season == Some(Season::Apostles) {
if weekend {
return Fast::customary(Level::Fish, "Saturday and Sunday of the Apostles' Fast");
}
if matches!(ctx.weekday, Weekday::Tuesday | Weekday::Thursday) {
return for_feast(Level::WineOil, ctx, Level::Fish, "Tuesday and Thursday of the Apostles' Fast");
}
return for_feast(Level::Xerophagy, ctx, Level::Fish, "Monday, Wednesday and Friday of the Apostles' Fast");
}
// The Dormition Fast, kept like the Great Fast.
if ctx.season == Some(Season::Dormition) {
if md == (8, 6) {
return Fast::customary(Level::Fish, "the Transfiguration");
}
if weekend {
return Fast::customary(Level::WineOil, "Saturday and Sunday of the Dormition Fast");
}
return for_feast(Level::Xerophagy, ctx, Level::WineOil, "a weekday of the Dormition Fast");
}
// The three fixed fast days outside any season.
if md == (1, 5) {
if weekend {
return Fast::customary(Level::WineOil, "the eve of Theophany, on a weekend");
}
return Fast::customary(Level::Xerophagy, "the eve of Theophany");
}
if md == (9, 14) {
return Fast::customary(Level::WineOil, "the Exaltation of the Holy Cross");
}
if md == (8, 29) {
return Fast::customary(Level::WineOil, "the Beheading of the Forerunner");
}
// Ordinary Wednesdays and Fridays, which a feast can lift.
if matches!(ctx.weekday, Weekday::Wednesday | Weekday::Friday) {
if let Some(target) = relaxed_by(ctx.rank) {
return Fast::customary(target, &format!("{} \u{2014} the fast is relaxed", ctx.feast));
}
return Fast::customary(Level::Xerophagy, "Wednesday and Friday");
}
Fast::new(Level::None, "", None)
}
pub static RULE: Rule = Rule {
name: "orthodox",
title: "the Typikon",
vocabulary: Vocabulary { label, emoji },
fast_for,
// The Orthodox Church does not legislate the fast as precept, so nothing
// here is reported as binding. See the module docs.
has_obligation: false,
};