mirror of
https://git.pumpin.online/funkpower/typikon.git
synced 2026-10-23 03:07:12 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93b597302a |
@@ -4,6 +4,17 @@ Notable changes, in the terms [docs/versioning.md](docs/versioning.md) sets out.
|
||||
**Calendar** entries change what a day reports, whatever the version number
|
||||
does.
|
||||
|
||||
## 1.1.2
|
||||
|
||||
### Fixed
|
||||
|
||||
- The published description says what the package covers, and says the same
|
||||
thing in both registries. PyPI takes its summary from `pyproject.toml`, which
|
||||
1.1.1 did not update, so it went out with the old one-line text while npm got
|
||||
the new one. The two READMEs had drifted apart as well: npm's opened without
|
||||
mentioning the Byzantine Catholic churches at all. All four now carry one
|
||||
sentence, and a test fails if any of them moves without the others.
|
||||
|
||||
## 1.1.1
|
||||
|
||||
### Fixed
|
||||
|
||||
Generated
+1
-1
@@ -404,7 +404,7 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
||||
|
||||
[[package]]
|
||||
name = "typikon"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"js-sys",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "typikon"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
edition = "2021"
|
||||
rust-version = "1.85"
|
||||
description = "The Orthodox liturgical day: feasts, seasons and the fast, computed rather than fetched. Twenty Orthodox jurisdictions and four Byzantine Catholic churches, across the Julian, Revised Julian and Gregorian reckonings."
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# typikon
|
||||
|
||||
The Orthodox liturgical day: feasts, seasons and the fast, computed rather
|
||||
than fetched. The Byzantine Catholic churches are here too, on their own
|
||||
calendars and, where the law has been sourced, under their own particular law.
|
||||
than fetched. Twenty Orthodox jurisdictions and four Byzantine Catholic
|
||||
churches, across the Julian, Revised Julian and Gregorian reckonings.
|
||||
|
||||
Everything derives from the paschalion and the fixed calendar, so it needs no
|
||||
network, cannot go stale, and answers for any year you ask about. No API key,
|
||||
|
||||
+4
-1
@@ -1,7 +1,10 @@
|
||||
# typikon
|
||||
|
||||
The Orthodox liturgical day: feasts, seasons and the fast, computed rather
|
||||
than fetched. No network, no data files, no service to be up.
|
||||
than fetched. Twenty Orthodox jurisdictions and four Byzantine Catholic
|
||||
churches, across the Julian, Revised Julian and Gregorian reckonings.
|
||||
|
||||
No network, no data files, no service to be up.
|
||||
|
||||
```sh
|
||||
npm install typikon
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "typikon"
|
||||
description = "The Orthodox liturgical day: feasts, seasons and the fast, computed rather than fetched"
|
||||
description = "The Orthodox liturgical day: feasts, seasons and the fast, computed rather than fetched. Twenty Orthodox jurisdictions and four Byzantine Catholic churches, across the Julian, Revised Julian and Gregorian reckonings."
|
||||
readme = "README.md"
|
||||
license = "GPL-2.0-only"
|
||||
license-files = ["LICENSE"]
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
//! The packaging metadata, which lives in three files and is published to two
|
||||
//! registries.
|
||||
//!
|
||||
//! `Cargo.toml` describes the crate, `pyproject.toml` is what maturin puts on
|
||||
//! PyPI, and `npm/package.json.in` is what npm shows. Nothing makes them agree
|
||||
//! and nothing notices when they stop: a description edited in one of the three
|
||||
//! reaches one registry and not the other, and the wrong one is then frozen
|
||||
//! into a release, because neither registry lets published metadata be edited.
|
||||
//! That has happened once already.
|
||||
//!
|
||||
//! The two READMEs open with the same sentence, because that paragraph is the
|
||||
//! first thing shown on both registry pages and is what most people will read
|
||||
//! instead of the description field.
|
||||
|
||||
use std::fs;
|
||||
|
||||
/// The value of a `key = "…"` line, from the first section that has one.
|
||||
fn toml_string(path: &str, key: &str) -> String {
|
||||
let text = fs::read_to_string(path).unwrap_or_else(|e| panic!("{path}: {e}"));
|
||||
for line in text.lines() {
|
||||
let line = line.trim();
|
||||
let Some(rest) = line.strip_prefix(key) else { continue };
|
||||
let Some(rest) = rest.trim_start().strip_prefix('=') else { continue };
|
||||
let rest = rest.trim();
|
||||
if let Some(v) = rest.strip_prefix('"').and_then(|v| v.strip_suffix('"')) {
|
||||
return v.to_string();
|
||||
}
|
||||
}
|
||||
panic!("{path}: no `{key} = \"…\"` line");
|
||||
}
|
||||
|
||||
/// The value of a `"key": "…"` line. Enough for a hand-written file with no
|
||||
/// escapes in it, and the test below fails loudly rather than quietly if that
|
||||
/// ever stops being true.
|
||||
fn json_string(path: &str, key: &str) -> String {
|
||||
let text = fs::read_to_string(path).unwrap_or_else(|e| panic!("{path}: {e}"));
|
||||
let needle = format!("\"{key}\":");
|
||||
for line in text.lines() {
|
||||
let Some(rest) = line.trim().strip_prefix(&needle) else { continue };
|
||||
let rest = rest.trim().trim_end_matches(',').trim();
|
||||
if let Some(v) = rest.strip_prefix('"').and_then(|v| v.strip_suffix('"')) {
|
||||
assert!(!v.contains('\\'), "{path}: {key} has escapes this reader cannot handle");
|
||||
return v.to_string();
|
||||
}
|
||||
}
|
||||
panic!("{path}: no `\"{key}\": \"…\"` line");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_three_descriptions_are_the_same_sentence() {
|
||||
let cargo = toml_string("Cargo.toml", "description");
|
||||
let pyproject = toml_string("pyproject.toml", "description");
|
||||
let npm = json_string("npm/package.json.in", "description");
|
||||
|
||||
assert_eq!(
|
||||
cargo, pyproject,
|
||||
"Cargo.toml and pyproject.toml disagree; pyproject.toml is the one PyPI shows"
|
||||
);
|
||||
assert_eq!(
|
||||
cargo, npm,
|
||||
"Cargo.toml and npm/package.json.in disagree; the latter is the one npm shows"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_description_says_what_the_crate_is_called() {
|
||||
// Cheap guard against a description edited into something that no longer
|
||||
// describes this package at all.
|
||||
let d = toml_string("Cargo.toml", "description");
|
||||
assert!(d.len() > 40, "description is too short to be one: {d:?}");
|
||||
assert!(!d.ends_with(' '), "description has a trailing space");
|
||||
assert!(d.contains("Orthodox"), "description no longer mentions Orthodox: {d:?}");
|
||||
}
|
||||
|
||||
/// The opening paragraph of a README, unwrapped to one line.
|
||||
fn lede(path: &str) -> String {
|
||||
let text = fs::read_to_string(path).unwrap_or_else(|e| panic!("{path}: {e}"));
|
||||
let body = text
|
||||
.split("\n\n")
|
||||
.find(|p| !p.trim().is_empty() && !p.trim_start().starts_with('#'))
|
||||
.unwrap_or_else(|| panic!("{path}: no opening paragraph"));
|
||||
body.split_whitespace().collect::<Vec<_>>().join(" ")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn both_readmes_open_with_the_description() {
|
||||
// A registry page leads with the README, not with the description field,
|
||||
// so the sentence a reader actually meets is this one. npm's README is a
|
||||
// separate file from the crate's and had gone its own way: it described
|
||||
// the package without mentioning that it covers Byzantine Catholic
|
||||
// churches at all.
|
||||
let description = toml_string("Cargo.toml", "description");
|
||||
assert_eq!(
|
||||
lede("README.md"),
|
||||
description,
|
||||
"README.md does not open with the published description"
|
||||
);
|
||||
assert_eq!(
|
||||
lede("npm/README.md"),
|
||||
description,
|
||||
"npm/README.md does not open with the published description"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user