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
|
**Calendar** entries change what a day reports, whatever the version number
|
||||||
does.
|
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
|
## 1.1.1
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
Generated
+1
-1
@@ -404,7 +404,7 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typikon"
|
name = "typikon"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "typikon"
|
name = "typikon"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.85"
|
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."
|
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
|
# typikon
|
||||||
|
|
||||||
The Orthodox liturgical day: feasts, seasons and the fast, computed rather
|
The Orthodox liturgical day: feasts, seasons and the fast, computed rather
|
||||||
than fetched. The Byzantine Catholic churches are here too, on their own
|
than fetched. Twenty Orthodox jurisdictions and four Byzantine Catholic
|
||||||
calendars and, where the law has been sourced, under their own particular law.
|
churches, across the Julian, Revised Julian and Gregorian reckonings.
|
||||||
|
|
||||||
Everything derives from the paschalion and the fixed calendar, so it needs no
|
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,
|
network, cannot go stale, and answers for any year you ask about. No API key,
|
||||||
|
|||||||
+4
-1
@@ -1,7 +1,10 @@
|
|||||||
# typikon
|
# typikon
|
||||||
|
|
||||||
The Orthodox liturgical day: feasts, seasons and the fast, computed rather
|
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
|
```sh
|
||||||
npm install typikon
|
npm install typikon
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "typikon"
|
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"
|
readme = "README.md"
|
||||||
license = "GPL-2.0-only"
|
license = "GPL-2.0-only"
|
||||||
license-files = ["LICENSE"]
|
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