data: consolidate the YAML embed convention into an embed_data! macro
This commit is contained in:
+70
@@ -0,0 +1,70 @@
|
|||||||
|
//! Embedding convention for the static reference data files (`data/*.yml`)
|
||||||
|
//!
|
||||||
|
//! Reference data that changes independently of the code — distro series
|
||||||
|
//! pointers, pinned SSH host keys, package quirks — lives in YAML files
|
||||||
|
//! under `data/` at the repo root instead of hardcoded in the source, so
|
||||||
|
//! it is updatable in one reviewable place.
|
||||||
|
//!
|
||||||
|
//! This module is deliberately not a central registry: each file is
|
||||||
|
//! embedded by the module that owns it (distro_info.rs owns
|
||||||
|
//! data/distro_info.yml, put/ssh.rs owns data/host_keys.yml, quirks.rs
|
||||||
|
//! owns data/quirks.yml) through the [`embed_data!`] macro below, so data
|
||||||
|
//! and its accessors stay together and a diff touching one domain cannot
|
||||||
|
//! half-touch another. The macro embeds the file at compile time and
|
||||||
|
//! parses it once into a `lazy_static` on first use; since the data ships
|
||||||
|
//! inside the binary, a parse failure is a build-time bug that cannot be
|
||||||
|
//! recovered from at runtime, and the macro panics on it.
|
||||||
|
//!
|
||||||
|
//! Paths and URLs in the data files carry their variable parts as `{name}`
|
||||||
|
//! placeholders, substituted with `str::replace` at the use site — no
|
||||||
|
//! template engine.
|
||||||
|
|
||||||
|
/// Embed one YAML data file as a lazily-parsed static, following the
|
||||||
|
/// convention documented at the module level.
|
||||||
|
///
|
||||||
|
/// Takes the visibility of the generated static (none for private, `pub` or
|
||||||
|
/// `pub(crate)`-style), its name, its struct type (which stays defined in
|
||||||
|
/// the owning module, next to its accessors) and the file path relative to
|
||||||
|
/// the invoking source file (`"../data/distro_info.yml"` from
|
||||||
|
/// `src/distro_info.rs`, `"../../data/host_keys.yml"` from
|
||||||
|
/// `src/put/ssh.rs`, ...), and expands to the house `include_str!` →
|
||||||
|
/// `lazy_static` → parse pattern — only the embed+parse boilerplate is
|
||||||
|
/// generated.
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// embed_data! {
|
||||||
|
/// static ref MY_DATA: MyData = "../data/my_data.yml"
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
macro_rules! embed_data {
|
||||||
|
// Internal arm: the visibility arrives wrapped in parentheses (empty for
|
||||||
|
// private statics) because `lazy_static!` only re-matches literal
|
||||||
|
// `pub`/`pub(...)` token sequences, not an opaque forwarded `vis`.
|
||||||
|
(@expand ($($vis:tt)*) static ref $name:ident : $ty:ty = $path:literal) => {
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
// The YAML is include_str!'d at compile time and statically
|
||||||
|
// valid; if it ever failed to parse it would be a build-time bug
|
||||||
|
// that cannot be recovered from at runtime, so panicking here is
|
||||||
|
// acceptable.
|
||||||
|
$($vis)* static ref $name: $ty = serde_yaml::from_str(include_str!($path))
|
||||||
|
.expect(concat!(
|
||||||
|
"built-in ",
|
||||||
|
$path,
|
||||||
|
" data is statically valid and must parse"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(static ref $name:ident : $ty:ty = $path:literal) => {
|
||||||
|
$crate::data::embed_data!(@expand () static ref $name : $ty = $path);
|
||||||
|
};
|
||||||
|
(pub static ref $name:ident : $ty:ty = $path:literal) => {
|
||||||
|
$crate::data::embed_data!(@expand (pub) static ref $name : $ty = $path);
|
||||||
|
};
|
||||||
|
(pub ($($vis:tt)+) static ref $name:ident : $ty:ty = $path:literal) => {
|
||||||
|
$crate::data::embed_data!(@expand (pub ($($vis)+)) static ref $name : $ty = $path);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes the macro available through the module path
|
||||||
|
/// (`use crate::data::embed_data;`)
|
||||||
|
pub(crate) use embed_data;
|
||||||
+5
-7
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::data::embed_data;
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@@ -43,14 +44,11 @@ struct Data {
|
|||||||
dist: std::collections::HashMap<String, DistData>,
|
dist: std::collections::HashMap<String, DistData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const DATA_YAML: &str = include_str!("../data/distro_info.yml");
|
embed_data! {
|
||||||
lazy_static! {
|
static ref DATA: Data = "../data/distro_info.yml"
|
||||||
// The YAML is include_str!'d at compile time and statically valid; if it
|
}
|
||||||
// ever failed to parse it would be a build-time bug that cannot be
|
|
||||||
// recovered from at runtime, so panicking here is acceptable.
|
|
||||||
static ref DATA: Data = serde_yaml::from_str(DATA_YAML)
|
|
||||||
.expect("built-in distro_info.yml data is statically valid and must parse");
|
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
// Shared HTTP client used for all outgoing plain requests: timeouts keep
|
// Shared HTTP client used for all outgoing plain requests: timeouts keep
|
||||||
// a hanging remote (connect or transfer) from stalling pkh indefinitely.
|
// a hanging remote (connect or transfer) from stalling pkh indefinitely.
|
||||||
// The short pool idle timeout and TCP keepalive avoid reusing keep-alive
|
// The short pool idle timeout and TCP keepalive avoid reusing keep-alive
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ pub mod apt;
|
|||||||
pub mod build;
|
pub mod build;
|
||||||
/// Parse or edit a Debian changelog of a source package
|
/// Parse or edit a Debian changelog of a source package
|
||||||
pub mod changelog;
|
pub mod changelog;
|
||||||
|
/// Embedding convention for static reference data (`data/*.yml`), applied
|
||||||
|
/// by each owning module via the `embed_data!` macro
|
||||||
|
pub(crate) mod data;
|
||||||
/// Build a Debian package into a binary (.deb)
|
/// Build a Debian package into a binary (.deb)
|
||||||
pub mod deb;
|
pub mod deb;
|
||||||
/// Reusable Debian format primitives (control/deb822, checksums, versions,
|
/// Reusable Debian format primitives (control/deb822, checksums, versions,
|
||||||
|
|||||||
+3
-9
@@ -21,16 +21,14 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use indicatif::ProgressBar;
|
use indicatif::ProgressBar;
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use ssh2::{CheckResult, HostKeyType, KnownHostFileKind, KnownHosts, Session};
|
use ssh2::{CheckResult, HostKeyType, KnownHostFileKind, KnownHosts, Session};
|
||||||
|
|
||||||
|
use crate::data::embed_data;
|
||||||
use crate::ui::prompt;
|
use crate::ui::prompt;
|
||||||
|
|
||||||
const HOST_KEYS_YAML: &str = include_str!("../../data/host_keys.yml");
|
|
||||||
|
|
||||||
/// Pinned SSH host key fingerprints, loaded from the bundled
|
/// Pinned SSH host key fingerprints, loaded from the bundled
|
||||||
/// `host_keys.yml` data file (same pattern as `distro_info.yml`): data
|
/// `host_keys.yml` data file (same pattern as `distro_info.yml`): data
|
||||||
/// rather than code, so trust anchors are updatable without touching the
|
/// rather than code, so trust anchors are updatable without touching the
|
||||||
@@ -42,12 +40,8 @@ struct PinnedHostKeys {
|
|||||||
fingerprints: HashMap<String, Vec<String>>,
|
fingerprints: HashMap<String, Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
embed_data! {
|
||||||
// The YAML is include_str!'d at compile time and statically valid; if it
|
static ref PINNED_HOST_KEYS: PinnedHostKeys = "../../data/host_keys.yml"
|
||||||
// ever failed to parse it would be a build-time bug that cannot be
|
|
||||||
// recovered from at runtime, so panicking here is acceptable.
|
|
||||||
static ref PINNED_HOST_KEYS: PinnedHostKeys = serde_yaml::from_str(HOST_KEYS_YAML)
|
|
||||||
.expect("built-in host_keys.yml data is statically valid and must parse");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether `fingerprint` is one of the pinned (published) fingerprints of
|
/// Whether `fingerprint` is one of the pinned (published) fingerprints of
|
||||||
|
|||||||
+3
-4
@@ -3,7 +3,7 @@
|
|||||||
//! This module provides functionality to read quirks from a YAML file
|
//! This module provides functionality to read quirks from a YAML file
|
||||||
//! and apply them during pull and deb operations.
|
//! and apply them during pull and deb operations.
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use crate::data::embed_data;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@@ -44,9 +44,8 @@ pub struct QuirksConfig {
|
|||||||
pub quirks: HashMap<String, PackageQuirks>,
|
pub quirks: HashMap<String, PackageQuirks>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const QUIRKS_YAML: &str = include_str!("../data/quirks.yml");
|
embed_data! {
|
||||||
lazy_static! {
|
static ref QUIRKS_DATA: QuirksConfig = "../data/quirks.yml"
|
||||||
static ref QUIRKS_DATA: QuirksConfig = serde_yaml::from_str(QUIRKS_YAML).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get quirks for a specific package
|
/// Get quirks for a specific package
|
||||||
|
|||||||
Reference in New Issue
Block a user