package_boundary.rs (71157B)
1 use std::collections::BTreeSet; 2 3 const MANIFEST: &str = include_str!("../Cargo.toml"); 4 const README: &str = include_str!("../README.md"); 5 const PUBLIC_API: &str = 6 include_str!("../../../contracts/api_baselines/radroots_service_sqlite.txt"); 7 const API_SEMVER: &str = include_str!("../../../contracts/releases/api_semver.toml"); 8 const PACKAGE_CATALOG: &str = include_str!("../../../contracts/crates/catalog.v2.toml"); 9 const PUBLISH_POLICY: &str = include_str!("../../../contracts/releases/publish_policy.toml"); 10 const ROOT: &str = include_str!("../src/lib.rs"); 11 const AUTHORITY_SOURCE: &str = include_str!("../src/authority.rs"); 12 const BACKUP_SOURCE: &str = include_str!("../src/backup/manifest.rs"); 13 const BACKUP_CAPTURE_SOURCE: &str = include_str!("../src/backup/capture.rs"); 14 const BACKUP_VERIFY_SOURCE: &str = include_str!("../src/backup/verify.rs"); 15 const CONFIG_SOURCE: &str = include_str!("../src/config.rs"); 16 const CONNECTION_SOURCE: &str = include_str!("../src/connection.rs"); 17 const ERROR_SOURCE: &str = include_str!("../src/error.rs"); 18 const FAILPOINT_SOURCE: &str = include_str!("../src/failpoint.rs"); 19 const INITIALIZE_SOURCE: &str = include_str!("../src/initialize.rs"); 20 const INTEGRITY_SOURCE: &str = include_str!("../src/integrity/mod.rs"); 21 const INTEGRITY_CATALOG_SOURCE: &str = include_str!("../src/integrity/catalog.rs"); 22 const INTEGRITY_INSPECTION_SOURCE: &str = include_str!("../src/integrity/inspection.rs"); 23 const METADATA_SOURCE: &str = include_str!("../src/metadata.rs"); 24 const MIGRATION_SOURCE: &str = include_str!("../src/migration.rs"); 25 const NATIVE_METADATA_SOURCE: &str = include_str!("../src/native_metadata.rs"); 26 const OPEN_SOURCE: &str = include_str!("../src/open.rs"); 27 const PERSISTED_VALUE_SOURCE: &str = include_str!("../src/persisted_value.rs"); 28 const RESTORE_MARKER_SOURCE: &str = include_str!("../src/restore/marker.rs"); 29 const RESTORE_FINALIZE_SOURCE: &str = include_str!("../src/restore/finalize.rs"); 30 const RESTORE_RECOVER_SOURCE: &str = include_str!("../src/restore/recover.rs"); 31 const RESTORE_ROOT_SOURCE: &str = include_str!("../src/restore/mod.rs"); 32 const RESTORE_PROCESS_TEST_SOURCE: &str = include_str!("../src/restore/process_tests.rs"); 33 const RESTORE_STAGE_SOURCE: &str = include_str!("../src/restore/stage.rs"); 34 const SQLITE_NATIVE_BACKUP_SOURCE: &str = include_str!("../src/sqlite_native_backup.rs"); 35 const STATUS_SOURCE: &str = include_str!("../src/status/mod.rs"); 36 const DISK_SOURCE: &str = include_str!("../src/status/disk.rs"); 37 const STATEMENT_POLICY_SOURCE: &str = include_str!("../src/statement_policy.rs"); 38 const TRANSACTION_CONTROL_SOURCE: &str = include_str!("../src/transaction_control.rs"); 39 const WRITER_PROCESS_TEST_SOURCE: &str = include_str!("writer_authority.rs"); 40 41 #[test] 42 fn service_sqlite_is_unpublished_lint_governed_and_dependency_bounded() { 43 let readme_words = README.split_whitespace().collect::<Vec<_>>().join(" "); 44 for required in [ 45 "name = \"radroots_service_sqlite\"", 46 "publish = false", 47 "version = \"0.1.0-alpha\"", 48 "[lints]\nworkspace = true", 49 ] { 50 assert!( 51 MANIFEST.contains(required), 52 "manifest is missing `{required}`" 53 ); 54 } 55 56 assert_eq!( 57 dependency_keys(MANIFEST, "[dependencies]"), 58 BTreeSet::from([ 59 "fs2", 60 "futures", 61 "libsqlite3-sys", 62 "radroots_runtime_paths", 63 "radroots_storage", 64 "rustix", 65 "serde", 66 "serde_json", 67 "sha2", 68 "sqlx", 69 "tokio" 70 ]) 71 ); 72 assert_eq!( 73 dependency_keys(MANIFEST, "[dev-dependencies]"), 74 BTreeSet::from(["tempfile", "tokio"]) 75 ); 76 let catalog_entry = package_catalog_entry(PACKAGE_CATALOG, "radroots_service_sqlite"); 77 for required in [ 78 "path = \"crates/service_sqlite\"", 79 "state = \"active\"", 80 "tier = \"runtime\"", 81 "visibility = \"private_runtime\"", 82 "publish = false", 83 "compatibility = [\"package_private\"]", 84 ] { 85 assert!( 86 catalog_entry.contains(required), 87 "package catalog entry is missing `{required}`" 88 ); 89 } 90 let publication = toml_section( 91 PUBLISH_POLICY, 92 "[publication]", 93 "[workspace_classification]", 94 ); 95 let workspace_classification = toml_section( 96 PUBLISH_POLICY, 97 "[workspace_classification]", 98 "[publish_order]", 99 ); 100 let private_packages = toml_array(workspace_classification, "private"); 101 let publish_order = PUBLISH_POLICY 102 .split_once("[publish_order]") 103 .map(|(_, section)| section) 104 .expect("publish policy must contain publish_order"); 105 assert!(!publication.contains("\"radroots_service_sqlite\"")); 106 assert!(private_packages.contains("\"radroots_service_sqlite\"")); 107 assert_eq!( 108 PUBLISH_POLICY 109 .matches("\"radroots_service_sqlite\"") 110 .count(), 111 1, 112 "service SQLite must appear only in the private workspace classification" 113 ); 114 assert!(!publish_order.contains("\"radroots_service_sqlite\"")); 115 assert!(!API_SEMVER.contains("\"radroots_service_sqlite\"")); 116 assert_eq!( 117 private_modules(ROOT), 118 BTreeSet::from([ 119 "authority", 120 "backup", 121 "config", 122 "connection", 123 "error", 124 "failpoint", 125 "initialize", 126 "integrity", 127 "metadata", 128 "migration", 129 "native_metadata", 130 "open", 131 "persisted_value", 132 "restore", 133 "sqlite_native_backup", 134 "status", 135 "statement_policy", 136 "transaction_control" 137 ]) 138 ); 139 assert!(public_modules(ROOT).is_empty()); 140 for required in [ 141 "pub(crate) const INTEGRITY_CHECK_SQL", 142 "PRAGMA integrity_check(1)", 143 "pub(crate) const MAX_INTEGRITY_RESULT_BYTES: usize = 64", 144 "pub(crate) fn bounded_integrity_bytes", 145 "pub(crate) fn integrity_result_failed", 146 "row.try_get::<&[u8], _>(0)", 147 "pub(crate) fn bounded_bytes", 148 "pub(crate) fn bounded_utf8", 149 ] { 150 assert!( 151 PERSISTED_VALUE_SOURCE.contains(required), 152 "persisted-value boundary is missing `{required}`" 153 ); 154 } 155 for forbidden in ["pub mod persisted_value", "pub use persisted_value"] { 156 assert!( 157 !ROOT.contains(forbidden), 158 "persisted-value boundary leaked through `{forbidden}`" 159 ); 160 } 161 for required in [ 162 "`ServiceSqliteHost` is the only public connection host", 163 "borrowed `ServiceSqliteTransaction` executor", 164 "transaction begin, commit, rollback, policy", 165 "attached-database exclusion", 166 "Service-controlled SQL is screened before SQLite compilation", 167 "statement-control inventory is `PRAGMA`, `ATTACH`, `DETACH`, `BEGIN`, `COMMIT`,", 168 "`END`, `ROLLBACK`, `SAVEPOINT`, and `RELEASE`", 169 "sticky for the transaction", 170 "is revalidated before commit and before a connection can return to the pool", 171 "Writable host opening finishes every pending governed migration", 172 "read-only inspection opens only current migration and", 173 "Existing databases can be admitted without a caller guessing their stored source generation", 174 "`ExistingServiceDatabaseIntent` seals the canonical service and instance, supported schema ceiling, and SQLite application ID", 175 "discover and verify the actual immutable metadata while retaining the corresponding writer or inspection authority", 176 "Success returns an `OpenedServiceDatabase`", 177 "keeps the host and verified metadata inseparable until the caller consumes them together", 178 "Recovery remains fail closed", 179 "with raw database authority", 180 "before the runner enables outer commit", 181 "leaves no authoritative transaction effect", 182 "only after rollback is confirmed", 183 "unconfirmed rollback is reported as `RollbackFailed`", 184 "Cancelling once outer", 185 "must be treated as an unknown commit outcome", 186 "require rereading authoritative state", 187 "before an idempotent retry", 188 "Every host must be closed explicitly with `ServiceSqliteHost::close`", 189 "permanently stops new transaction admission", 190 "drains transactions that were", 191 "safe to call sequentially or concurrently", 192 "fixed `PRAGMA wal_checkpoint(TRUNCATE)` policy", 193 "requires an unblocked checkpoint", 194 "releases its shared inspection guard without checkpointing or mutating", 195 "Cancelling close before terminal completion", 196 "private connect, checkpoint, and explicit connection-close driver remains host-owned", 197 "without losing the SQLite handle or its close proof", 198 "a later call resumes close", 199 "stable outer result is cached", 200 "Dropping a host performs no asynchronous close work", 201 "`ServiceBackupManifest` is the stable model-only v1 backup identity", 202 "compact canonical UTF-8 JSON in the frozen field order, capped at 1,024 bytes", 203 "external manifest SHA-256 over those exact bytes", 204 "exactly one `state.sqlite` member", 205 "integrity are exactly `ok`, and protected material is always excluded", 206 "Parsing proves only the strict structural and canonical contract", 207 "unknown, duplicate, null, reordered, whitespace-altered, or version-drifted input", 208 "Constructing or parsing the manifest model performs no filesystem or SQLite work", 209 "Writable hosts provide `ServiceSqliteHost::capture_online_backup`", 210 "one incremental, point-in-time SQLite capture at a time", 211 "exact new absolute staging-directory path", 212 "directory with mode `0700` and its sole `state.sqlite` member with mode `0600`", 213 "online-backup API without checkpointing or copying the live source file", 214 "requires exact service metadata, bounded `integrity_check`, an empty `foreign_key_check`", 215 "SHA-256, and file, staging, and parent synchronization", 216 "returning the canonical manifest in memory", 217 "No manifest file, bundle identifier, credential, or protected material", 218 "Dropping the capture future requests cancellation", 219 "retains its checked-out pool admission, writer authority, and exact staging identities", 220 "Host close therefore drains capture and cancellation cleanup", 221 "Capture has no hidden timeout", 222 "callers own any deadline by cancelling the future", 223 "does not provide restore or replacement behavior", 224 "`verify_backup_bundle` is the synchronous, task-free boundary", 225 "independently protected manifest SHA-256", 226 "positive maximum state-file size", 227 "restrictive owner-only directory containing only `state.sqlite`", 228 "performs no filesystem mutation and does not create a task or hidden deadline", 229 "non-forgeable `VerifiedServiceBackup`", 230 "exposing only the canonical manifest and actual database metadata", 231 "It is not restore or replacement authority", 232 "copy from the retained member and reverify the staged copy", 233 "pathname verification alone is insufficient", 234 "Restore crash recovery uses a private sealed v1 marker", 235 "state.restore-staged.sqlite", 236 "state.restore-backup.sqlite", 237 "state.restore-marker.v1.next", 238 "compact canonical JSON is capped at 2,048 bytes", 239 "domain-separated checksum binds the canonical fields and detects corruption", 240 "it is not an authenticity credential", 241 "only legal durable sequence is `prepared` to `live_retained` to `replacement_installed`", 242 "repeating the current phase is byte-idempotent", 243 "descriptor-relative, no-follow, single-link, owner-owned regular files with mode `0600`", 244 "compare-and-reloads the current bytes", 245 "create-new scratch, atomically replaces the marker", 246 "reads do not repair or remove it", 247 "does not stage, copy, open, rename, replace, or delete a database", 248 "No marker type, path, raw descriptor, or store operation is public API", 249 "`stage_verified_restore` is the offline boundary", 250 "acquires exclusive writer authority", 251 "fixed adjacent `state.restore-staged.sqlite`", 252 "A live writable or read-only host", 253 "opened create-new, no-follow, owner-only, and single-link with mode `0600`", 254 "copies the exact manifest-bound bytes from the verifier's retained member descriptor", 255 "opens SQLite only through the retained staged descriptor", 256 "exact applied migration prefix and schema-object catalog", 257 "bounded `integrity_check(1)`, and empty `foreign_key_check`", 258 "Live database bytes, identity, permissions, and timestamps remain untouched", 259 "sealed non-cloneable `StagedServiceRestore`", 260 "attempts an identity-checked unlink and state-directory synchronization", 261 "cleanup failure leaves staging or recovery evidence", 262 "detached work retains authority and exact cleanup ownership", 263 "does not create or advance a recovery marker", 264 "`finalize_staged_restore` consumes that sealed stage", 265 "bound the exact live inode, length, and digest", 266 "creates and synchronizes the `prepared` marker", 267 "descriptor-relative no-replace operations", 268 "marker advance to `live_retained` or `replacement_installed`", 269 "Cancellation observed before the worker's atomic commit-ownership handoff", 270 "interval before `prepared` becomes durable", 271 "Once `prepared` is durable, staged-artifact cleanup is disarmed", 272 "unknown immediate outcome", 273 "retains the old live database and final marker", 274 "Read-write-existing open is the sole recovery path", 275 "before opening SQLite", 276 "Read-only inspection, initialization, and an initialized open never recover", 277 "reject any stage, backup, marker, or marker scratch as `Recovery` without mutation", 278 "Recovery uses exact topology as the durable authority", 279 "rolls back by removing only the exact stage and then the marker", 280 "recovery advances and rolls forward", 281 "removes the exact old backup before retiring the marker", 282 "repeated recovery is idempotent", 283 "A marker scratch is admitted only when it is the canonical one-edge successor", 284 "Recovery removes only the exact bound scratch inode", 285 "then reproduces the transition through the governed marker-advance path", 286 "preserves the valid current marker if the scratch pathname was replaced", 287 "Recovery has no await point or hidden task", 288 "each synchronous filesystem step and its authority checks complete", 289 "Finalization itself does not reconcile or reopen the database", 290 "`ServiceSqliteHost::inspect_integrity` is the explicit active operator check", 291 "available on initialized, writable-existing, and read-only inspection hosts", 292 "admits at most one check per host", 293 "uses one deferred read transaction as the SQLite snapshot", 294 "caller injects a positive wall-clock `IntegrityCheckedAtUnixMs`", 295 "does not read an ambient clock or create a timer", 296 "only `verified` or `failed` for SQLite integrity and foreign keys", 297 "at most the fixed `sqlite_integrity_failed` and `foreign_key_violation` diagnostic codes", 298 "canonical order", 299 "projected to the passive `StorageIntegrity` vocabulary", 300 "does not persist or cache the report", 301 "never publishes raw SQLite diagnostics, table or row identity", 302 "Inability to execute, decode, or finish either bounded check is an `Integrity` error", 303 "Authority is revalidated after every await and has precedence", 304 "operation has no hidden timeout or task", 305 "Callers own a positive monotonic deadline by dropping the future", 306 "cancellation returns no report, writes nothing, quarantines the checked-out connection", 307 "leaves it in a host-owned close driver", 308 "Retry or host close explicitly awaits that retained close future", 309 "until the prior SQLite worker terminates", 310 "before any new check or authority release", 311 "retry uses a newly injected wall-clock time", 312 "strict backup and restore integrity verifier remains a separate fail-closed boundary", 313 "State-filesystem capacity inspection is an explicit synchronous input", 314 "`MinimumFreeBytes` must be supplied and is constrained to `1..=i64::MAX`; it has no default", 315 "`268435456` is the exact governed configuration and test vector, not an implicit universal threshold", 316 "owner-owned state directory that is not group/other writable", 317 "uses `fstatvfs` to measure bytes available to the unprivileged service user", 318 "current native qualification matrix is macOS aarch64 and Linux x86_64", 319 "successful compilation outside that matrix is not support evidence", 320 "successful immutable snapshot is `ready` when available bytes are greater than or equal", 321 "`low_disk` when they are below it", 322 "measurement failure is a typed unavailable error and is never fabricated as low-disk evidence", 323 "project low disk to the stable `database_low_disk` readiness reason", 324 "`/readyz` remains passive", 325 "measurement is advisory rather than a space reservation", 326 "performs no database open, pool operation, SQLite query, filesystem mutation, ambient time read, timer, task", 327 "Service configuration, threshold defaults, cache refresh, status persistence, admission wiring, and route projection remain consumer responsibilities", 328 "Durability fault injection is a private test-only mechanism", 329 "closed instance-scoped controller can arm exactly one named before/after boundary", 330 "returns one injected error the first time that boundary is reached; later hits are no-ops", 331 "complete inventory covers database initialization, runner-owned transaction begin and commit", 332 "online-backup creation/copy/synchronization", 333 "restore-marker creation and advancement", 334 "both restore rename/synchronization steps", 335 "explicit host drain/checkpoint/connection-close/authority-release", 336 "ordinary controller has zero behavior", 337 "no failpoint type or selector is exported from the crate root", 338 "no process-global failpoint state, environment or configuration selector, Cargo feature", 339 "hidden task, timer, panic, or process-exit behavior", 340 "Process-crash qualification remains test-only", 341 "one bounded temporary root over stdin", 342 "fixed stdout readiness token from an occurrence-aware failpoint barrier", 343 "orphan-stage refusal before a durable marker", 344 "interrupted marker-scratch promotion", 345 "permissive child umask cannot broaden", 346 "Linux execution on x86_64 is required for OS-level qualification", 347 "macOS aarch64 execution on the current machine is developer evidence", 348 "No other platform or architecture is an active qualification gate", 349 "do not claim abrupt power-loss or storage-device durability behavior", 350 ] { 351 assert!( 352 readme_words.contains(required), 353 "Step 061 README contract is missing `{required}`" 354 ); 355 } 356 let authority_production = AUTHORITY_SOURCE 357 .split_once("#[cfg(all(test") 358 .map(|(production, _)| production) 359 .expect("authority source must keep tests separated"); 360 let open_production = OPEN_SOURCE 361 .split_once("#[cfg(test)]\nmod tests") 362 .map(|(production, _)| production) 363 .expect("open source must keep tests separated"); 364 let migration_production = MIGRATION_SOURCE 365 .split_once("#[cfg(test)]") 366 .map(|(production, _)| production) 367 .expect("migration source must keep tests separated"); 368 let connection_production = CONNECTION_SOURCE 369 .split_once("#[cfg(test)]\nmod tests") 370 .map(|(production, _)| production) 371 .expect("connection source must keep tests separated"); 372 let backup_production = BACKUP_SOURCE 373 .split_once("#[cfg(test)]") 374 .map(|(production, _)| production) 375 .expect("backup source must keep tests separated"); 376 let backup_capture_production = BACKUP_CAPTURE_SOURCE 377 .split_once("#[cfg(test)]\nmod tests") 378 .map(|(production, _)| production) 379 .expect("backup capture source must keep tests separated"); 380 let backup_verify_production = BACKUP_VERIFY_SOURCE 381 .split_once("#[cfg(test)]") 382 .map_or(BACKUP_VERIFY_SOURCE, |(production, _)| production); 383 let integrity_inspection_production = INTEGRITY_INSPECTION_SOURCE 384 .split_once("#[cfg(all(test, any(target_os = \"linux\", target_os = \"macos\")))]") 385 .map(|(production, _)| production) 386 .expect("integrity inspection source must keep test seams separated"); 387 let integrity_catalog_production = INTEGRITY_CATALOG_SOURCE 388 .split_once("#[cfg(test)]") 389 .map(|(production, _)| production) 390 .expect("integrity catalog source must keep tests separated"); 391 let disk_production = DISK_SOURCE 392 .split_once("#[cfg(test)]\nmod tests") 393 .map(|(production, _)| production) 394 .expect("disk inspection source must keep tests separated"); 395 let restore_marker_production = RESTORE_MARKER_SOURCE 396 .split_once("#[cfg(test)]\nmod tests") 397 .map(|(production, _)| production) 398 .expect("restore marker source must keep tests separated"); 399 400 for required in [ 401 "pub struct radroots_service_sqlite::ServiceSqliteHost", 402 "pub struct radroots_service_sqlite::ServiceSqliteInitializer", 403 "pub struct radroots_service_sqlite::ServiceSqliteTransaction", 404 "pub struct radroots_service_sqlite::ServiceSqlitePaths", 405 "pub struct radroots_service_sqlite::ExistingServiceDatabaseIntent", 406 "pub struct radroots_service_sqlite::OpenedServiceDatabase", 407 "pub struct radroots_service_sqlite::MigrationCatalog", 408 "pub struct radroots_service_sqlite::SchemaCatalog", 409 "pub struct radroots_service_sqlite::VerifiedServiceBackup", 410 "pub struct radroots_service_sqlite::StagedServiceRestore", 411 "pub async fn radroots_service_sqlite::initialize_database", 412 "pub fn radroots_service_sqlite::verify_backup_bundle", 413 "pub async fn radroots_service_sqlite::finalize_staged_restore", 414 "pub async fn radroots_service_sqlite::stage_verified_restore", 415 "pub async fn radroots_service_sqlite::ServiceSqliteHost::open_read_write_existing_with_intent", 416 "pub async fn radroots_service_sqlite::ServiceSqliteHost::open_read_only_inspection_with_intent", 417 "pub async fn radroots_service_sqlite::ServiceSqliteHost::open_or_initialize", 418 "impl<'executor, 'connection> sqlx_core::executor::Executor<'executor> for &'executor mut radroots_service_sqlite::ServiceSqliteInitializer<'connection>", 419 "impl<'executor, 'connection> sqlx_core::executor::Executor<'executor> for &'executor mut radroots_service_sqlite::ServiceSqliteTransaction<'connection>", 420 ] { 421 assert!( 422 PUBLIC_API.contains(required), 423 "reviewed API baseline is missing `{required}`" 424 ); 425 } 426 for forbidden in [ 427 "pub mod radroots_service_sqlite::", 428 "SqlitePool", 429 "PoolConnection", 430 "sqlx_sqlite::connection::SqliteConnection", 431 "sqlx_core::pool::Pool", 432 "sqlx_core::transaction::Transaction", 433 "rusqlite::", 434 "rustix::", 435 "tokio::", 436 "fs2::", 437 "serde_json::", 438 "sha2::", 439 "pub use sqlx", 440 ] { 441 assert!( 442 !PUBLIC_API.contains(forbidden), 443 "reviewed API baseline exposes forbidden authority `{forbidden}`" 444 ); 445 } 446 for surface in [README, ROOT, PUBLIC_API, open_production] { 447 let lowercase = surface.to_ascii_lowercase(); 448 for forbidden in ["myc", "rhi"] { 449 assert!( 450 !lowercase.contains(forbidden), 451 "public or production boundary contains product identifier `{forbidden}`" 452 ); 453 } 454 } 455 for required in [ 456 "## Public API and package boundary", 457 "unpublished `private_runtime`, `package_private` crate", 458 "shared `radroots_service_metadata` and `schema_migrations` tables", 459 "Every service-owned table, index, trigger,", 460 "[service-SQLite API baseline](../../contracts/api_baselines/radroots_service_sqlite.txt)", 461 "Raw pools, pooled or direct connections, transaction-control handles, and", 462 "`sqlx::Executor` implementation for borrowed", 463 "`&mut ServiceSqliteInitializer<'_>` and `&mut ServiceSqliteTransaction<'_>`", 464 "values. They permit compile-time typed queries. The crate retains connection", 465 "ownership and sole begin, commit, rollback, policy, and cancellation authority.", 466 ] { 467 assert!(README.contains(required), "README is missing `{required}`"); 468 } 469 assert_eq!( 470 integrity_catalog_production 471 .matches("CREATE TABLE ") 472 .count(), 473 2, 474 "built-in persistent table inventory drifted" 475 ); 476 for required in [ 477 "CREATE TABLE radroots_service_metadata", 478 "CREATE TABLE schema_migrations", 479 "CREATE TRIGGER radroots_service_metadata_guard_update", 480 "CREATE TRIGGER radroots_service_metadata_no_delete", 481 "CREATE TRIGGER schema_migrations_no_update", 482 "CREATE TRIGGER schema_migrations_no_delete", 483 ] { 484 assert!( 485 integrity_catalog_production.contains(required), 486 "shared persistent-object inventory is missing `{required}`" 487 ); 488 } 489 assert_eq!( 490 integrity_catalog_production 491 .matches("CREATE TRIGGER ") 492 .count(), 493 4, 494 "built-in trigger inventory drifted" 495 ); 496 497 for required in [ 498 "ServiceSqliteErrorCode", 499 "ServiceSqliteErrorKind", 500 "SafeServiceSqliteError", 501 "ServiceSqliteError", 502 "WriterAuthority", 503 "BackupCreatedAtUnixMs", 504 "BackupManifestContractError", 505 "BackupManifestIntegrity", 506 "BackupManifestSha256", 507 "BackupMemberSha256", 508 "ServiceBackupManifest", 509 "ServiceBackupMember", 510 "VerifiedServiceBackup", 511 "verify_backup_bundle", 512 "BACKUP_MANIFEST_CANONICAL_MAX_BYTES", 513 "BACKUP_MANIFEST_SCHEMA", 514 "BACKUP_MANIFEST_SCHEMA_VERSION", 515 "BACKUP_STATE_MEMBER_NAME", 516 "ServiceSqliteConnectionOptions", 517 "ServiceSqliteConnectionOptionsError", 518 "initialize_database", 519 "ServiceSqliteInitializer", 520 "ServiceSqliteInitializerFuture", 521 "ServiceDatabaseIdentity", 522 "ServiceDatabaseMetadata", 523 "ServiceSqliteApplicationId", 524 "ServiceSqliteMetadataValueError", 525 "MigrationAppliedAtUnixSeconds", 526 "MigrationApplicationOutcome", 527 "MigrationBuildIdentity", 528 "MigrationCallback", 529 "MigrationCallbackBinding", 530 "MigrationCallbackFuture", 531 "MigrationCatalog", 532 "MigrationChecksum", 533 "MigrationContractError", 534 "MigrationDescriptor", 535 "MigrationEvidenceError", 536 "MigrationKind", 537 "MigrationName", 538 "MigrationTransactionExecutor", 539 "IntegrityCheckOutcome", 540 "IntegrityCheckedAtUnixMs", 541 "IntegrityDiagnosticCode", 542 "ServiceSqliteIntegrityReport", 543 "SchemaCatalog", 544 "SchemaCatalogContractError", 545 "SchemaDigest", 546 "SchemaObject", 547 "SchemaObjectKind", 548 "SchemaVersionCatalog", 549 "ServiceSqlitePathError", 550 "ServiceSqlitePaths", 551 "OpenMode", 552 "ServiceSqliteHost", 553 "ServiceSqliteTransaction", 554 "ServiceSqliteTransactionError", 555 "ServiceSqliteTransactionErrorKind", 556 "ServiceSqliteTransactionFuture", 557 "StorageHealth", 558 "StorageIntegrity", 559 "StorageStatus", 560 "MinimumFreeBytes", 561 "PlatformStateFilesystemCapacitySource", 562 "StateFilesystemCapacity", 563 "StateFilesystemCapacityError", 564 "StateFilesystemCapacityReadiness", 565 "StateFilesystemCapacitySource", 566 "inspect_state_filesystem_capacity", 567 ] { 568 assert!( 569 ROOT.contains(required), 570 "crate root is missing `{required}`" 571 ); 572 } 573 574 for required in [ 575 "MAXIMUM_MINIMUM_FREE_BYTES: u64 = i64::MAX as u64", 576 "pub const fn new(value: u64)", 577 "StateFilesystemCapacityReadiness::Ready", 578 "StateFilesystemCapacityReadiness::LowDisk", 579 "available_bytes >= minimum_free_bytes.get()", 580 "pub trait StateFilesystemCapacitySource", 581 "pub struct PlatformStateFilesystemCapacitySource", 582 "pub fn inspect_state_filesystem_capacity", 583 "OFlags::RDONLY | OFlags::DIRECTORY | OFlags::NOFOLLOW | OFlags::CLOEXEC", 584 "fstatvfs(&held)", 585 "capacity.f_bavail", 586 "capacity.f_frsize", 587 "crate::native_metadata::secure_directory(", 588 "UnsupportedPlatform", 589 ] { 590 assert!( 591 disk_production.contains(required), 592 "Step 071 disk inspection source is missing `{required}`" 593 ); 594 } 595 596 for required in [ 597 "pub(crate) fn mode<T>", 598 "T: Into<u32>", 599 "pub(crate) fn link_count<T>", 600 "T: Into<u64>", 601 "pub(crate) fn device<T>", 602 "T: TryInto<u64>", 603 "pub(crate) fn sqlite_wal_header", 604 "header[18] == 2,", 605 "header[19] == 2,", 606 "crate::all_constraints([", 607 "pub(crate) fn secure_directory", 608 "pub(crate) fn exact_regular_file", 609 "pub(crate) fn identity_pair_matches", 610 ] { 611 assert!( 612 NATIVE_METADATA_SOURCE.contains(required), 613 "native metadata normalization is missing `{required}`" 614 ); 615 } 616 for forbidden in ["pub fn mode", "pub fn link_count", "pub fn device"] { 617 assert!( 618 !NATIVE_METADATA_SOURCE.contains(forbidden), 619 "native metadata normalization exposes `{forbidden}`" 620 ); 621 } 622 for forbidden in [ 623 "ServiceSqliteHost", 624 "readyz", 625 "database_low_disk", 626 "sqlx", 627 "rusqlite", 628 "tokio", 629 "SystemTime", 630 "Instant", 631 "spawn", 632 "sleep", 633 "create_dir", 634 "write(", 635 "Default for MinimumFreeBytes", 636 ] { 637 assert!( 638 !disk_production.contains(forbidden), 639 "Step 071 disk inspection source contains deferred authority `{forbidden}`" 640 ); 641 } 642 for forbidden in ["rustix::", "RawFd", "OwnedFd", "BorrowedFd"] { 643 assert!( 644 !ROOT.contains(forbidden), 645 "Step 071 crate root exposes dependency or raw descriptor `{forbidden}`" 646 ); 647 } 648 649 let failpoint_production = FAILPOINT_SOURCE 650 .split_once("#[cfg(test)]\nmod tests") 651 .map(|(production, _)| production) 652 .expect("failpoint source must keep tests separated"); 653 for required in [ 654 "pub(crate) enum DurabilityFailpoint", 655 "pub(crate) struct DurabilityFailpoints", 656 "InitializeBeforeCreate", 657 "InitializeAfterReservationDirectorySync", 658 "InitializeAfterCommitDirectorySync", 659 "TransactionBeforeBegin", 660 "TransactionAfterCommit", 661 "BackupBeforeCreate", 662 "BackupAfterDirectorySync", 663 "MarkerBeforeCreate", 664 "MarkerAfterDirectorySync", 665 "MarkerAdvanceBeforeWriteAndFileSync", 666 "MarkerAdvanceAfterDirectorySync", 667 "RestoreBeforeRetainLiveRename", 668 "RestoreAfterInstallStageSync", 669 "CloseBeforeDrain", 670 "CloseAfterAuthorityRelease", 671 "pub(crate) fn hit", 672 "#[cfg(test)]\n pub(crate) fn armed", 673 "DurabilityFailpoints([redacted])", 674 ] { 675 assert!( 676 failpoint_production.contains(required), 677 "Step 072 failpoint source is missing `{required}`" 678 ); 679 } 680 for forbidden in [ 681 "pub enum DurabilityFailpoint", 682 "pub struct DurabilityFailpoints", 683 "static ", 684 "std::env", 685 "SystemTime", 686 "Instant", 687 "tokio::", 688 "spawn", 689 "sleep", 690 "panic!", 691 "process::exit", 692 ] { 693 assert!( 694 !failpoint_production.contains(forbidden), 695 "Step 072 failpoint source contains forbidden authority `{forbidden}`" 696 ); 697 } 698 assert!(!ROOT.contains("pub use failpoint")); 699 assert!(!MANIFEST.contains("[features]")); 700 for (source, required) in [ 701 (INITIALIZE_SOURCE, "InitializeBeforeCreate"), 702 (CONNECTION_SOURCE, "TransactionAfterCommit"), 703 (BACKUP_CAPTURE_SOURCE, "BackupAfterDirectorySync"), 704 (RESTORE_MARKER_SOURCE, "MarkerAdvanceAfterDirectorySync"), 705 (RESTORE_FINALIZE_SOURCE, "RestoreAfterInstallStageSync"), 706 (OPEN_SOURCE, "CloseAfterAuthorityRelease"), 707 ] { 708 assert!( 709 source.contains(required), 710 "Step 072 integration source is missing `{required}`" 711 ); 712 } 713 for required in [ 714 "pub(crate) fn process_barrier", 715 "occurrence: u8", 716 "RSHR_STEP073_READY", 717 "Condvar", 718 "wait_while", 719 ] { 720 assert!( 721 FAILPOINT_SOURCE.contains(required), 722 "Step 073 process barrier is missing `{required}`" 723 ); 724 } 725 for required in [ 726 "mod process_tests;", 727 "#[cfg(all(test, any(target_os = \"linux\", target_os = \"macos\")))]", 728 ] { 729 assert!( 730 RESTORE_ROOT_SOURCE.contains(required), 731 "Step 073 restore test boundary is missing `{required}`" 732 ); 733 } 734 for required in [ 735 "child_before_prepared_marker", 736 "child_after_prepared_marker", 737 "child_after_live_retained_scratch_sync", 738 "child_after_replacement_install_sync", 739 "child_after_terminal_marker_sync", 740 "MarkerBeforeCreate", 741 "MarkerAdvanceAfterWriteAndFileSync", 742 "MarkerAdvanceAfterDirectorySync, 2", 743 "--ignored", 744 "--exact", 745 "Stdio::piped()", 746 "Signal::KILL", 747 "status.signal(), Some(9)", 748 "assert_recovery_permissions", 749 "ServiceSqliteErrorKind::Recovery", 750 ] { 751 assert!( 752 RESTORE_PROCESS_TEST_SOURCE.contains(required), 753 "Step 073 restore process harness is missing `{required}`" 754 ); 755 } 756 for required in [ 757 "writer_authority_holder_child_probe", 758 "RSHR_STEP073_WRITER_READY", 759 "--ignored", 760 "Stdio::piped()", 761 "Signal::KILL", 762 "status.signal(), Some(9)", 763 "assert_lock_permissions", 764 ] { 765 assert!( 766 WRITER_PROCESS_TEST_SOURCE.contains(required), 767 "Step 073 writer process harness is missing `{required}`" 768 ); 769 } 770 for forbidden in [ 771 "std::env::var", 772 "env::var_os", 773 ".env(", 774 "ready.is_file", 775 "thread::sleep", 776 "remove_dir_all", 777 "process::exit", 778 ] { 779 assert!( 780 !RESTORE_PROCESS_TEST_SOURCE.contains(forbidden), 781 "Step 073 restore process harness contains forbidden `{forbidden}`" 782 ); 783 } 784 assert!(!ROOT.contains("process_tests")); 785 786 for required in [ 787 "radroots.service-backup", 788 "BACKUP_MANIFEST_SCHEMA_VERSION: u32 = 1", 789 "BACKUP_MANIFEST_CANONICAL_MAX_BYTES: usize = 1_024", 790 "BACKUP_STATE_MEMBER_NAME: &str = \"state.sqlite\"", 791 "pub fn from_canonical_bytes", 792 "manifest.canonical_bytes.as_ref() == bytes", 793 "BackupManifestContractError::NonCanonicalEncoding", 794 "serde(deny_unknown_fields)", 795 "Sha256::digest(&canonical_bytes)", 796 "pub(crate) fn from_capture", 797 "ServiceDatabaseMetadata", 798 "InvalidMemberInventory", 799 "ProtectedMaterialIncluded", 800 ] { 801 assert!( 802 backup_production.contains(required), 803 "Step 063 backup source is missing `{required}`" 804 ); 805 } 806 for forbidden in [ 807 "impl Serialize for ServiceBackupManifest", 808 "impl<'de> Deserialize<'de> for ServiceBackupManifest", 809 "pub fn from_capture", 810 "std::fs", 811 "sqlx", 812 "tokio", 813 "SystemTime", 814 "WallClock", 815 "PathBuf", 816 "OpenOptions", 817 ] { 818 assert!( 819 !backup_production.contains(forbidden), 820 "Step 063 backup source contains deferred or forgeable surface `{forbidden}`" 821 ); 822 } 823 824 for required in [ 825 "radroots.service_sqlite.migration_content.v1\\0", 826 "radroots.service_sqlite.migration_catalog.v1\\0", 827 "MAX_MIGRATION_NAME_UTF8_BYTES", 828 "MAX_MIGRATION_CONTENT_BYTES", 829 "MAX_MIGRATION_COUNT", 830 "pub const fn from_bytes", 831 "pub fn for_sql", 832 "pub fn for_callback", 833 ".take(MAX_MIGRATION_COUNT + 1)", 834 ".begin_with(\"BEGIN IMMEDIATE\")", 835 "pub(crate) async fn verify_migration_history", 836 "pub(crate) async fn apply_governed_migrations", 837 "validate_callback_bindings", 838 "advance_schema_version", 839 "pub struct MigrationTransactionExecutor", 840 "contains_forbidden_statement_control", 841 "statement_control_rejected", 842 "SAVEPOINT radroots_migration_transaction_probe", 843 "FROM pragma_database_list", 844 "typeof(name) = 'text' AS name_type_ok", 845 "substr(CAST(name AS BLOB), 1, 129) AS name_prefix", 846 "typeof(checksum) = 'blob' AS checksum_type_ok", 847 "substr(checksum, 1, 33) AS checksum_prefix", 848 "crate::persisted_value::bounded_utf8", 849 "crate::persisted_value::bounded_bytes", 850 ] { 851 assert!( 852 migration_production.contains(required), 853 "Step 059 migration source is missing `{required}`" 854 ); 855 } 856 857 for required in [ 858 "pub struct ServiceSqliteHost", 859 "pub struct ServiceSqliteTransaction<'connection>", 860 "impl<'executor, 'connection> Executor<'executor>", 861 "pub enum ServiceSqliteTransactionErrorKind", 862 "pub struct ServiceSqliteTransactionError<E>", 863 "pub type ServiceSqliteTransactionFuture", 864 "BEGIN IMMEDIATE", 865 "OpenMode::ReadOnlyInspection => connection.begin().await", 866 "verify_before_commit", 867 "connection.close_on_drop()", 868 "connection.trust()", 869 "RestrictedExecute", 870 "contains_forbidden_statement_control", 871 "RADROOTS_FORBIDDEN_STATEMENT_CONTROL", 872 "OperationRolledBack", 873 "RollbackFailed", 874 "CommitOutcomeUnknown", 875 "cancelling the future yields no result", 876 "must reread authoritative state before any idempotent retry", 877 "pub async fn close(&self)", 878 "pub async fn capture_online_backup(", 879 "pub async fn inspect_integrity(", 880 "closing.store(true, Ordering::Release)", 881 "close_state.lock().await", 882 "ServiceSqliteHostCloseState::Complete", 883 ] { 884 assert!( 885 CONNECTION_SOURCE.contains(required), 886 "Step 061 connection source is missing `{required}`" 887 ); 888 } 889 890 for required in [ 891 "pub(crate) fn contains_forbidden_statement_control", 892 "b\"pragma\".as_slice()", 893 "b\"attach\"", 894 "b\"detach\"", 895 "b\"begin\"", 896 "b\"commit\"", 897 "b\"end\"", 898 "b\"rollback\"", 899 "b\"savepoint\"", 900 "b\"release\"", 901 "trigger_definition", 902 "trigger_case_depth", 903 ] { 904 assert!( 905 STATEMENT_POLICY_SOURCE.contains(required), 906 "statement-policy source is missing `{required}`" 907 ); 908 } 909 910 for required in [ 911 "set_commit_hook", 912 "set_rollback_hook", 913 "permit_outer_commit", 914 "permit_runner_rollback", 915 "control_violation_observed", 916 "rejected_commit", 917 "rejected_commit_rolled_back", 918 "remove_commit_hook", 919 "remove_rollback_hook", 920 ] { 921 assert!( 922 TRANSACTION_CONTROL_SOURCE.contains(required), 923 "Step 061 transaction-control source is missing `{required}`" 924 ); 925 } 926 927 for forbidden in [ 928 "pub fn pool(", 929 "pub fn connection(", 930 "pub fn into_inner(", 931 "Deref for ServiceSqliteTransaction", 932 "AsRef<SqliteConnection>", 933 "pub async fn begin(", 934 "pub async fn commit(", 935 "pub async fn rollback(", 936 "pub use sqlx", 937 "impl Drop for ServiceSqliteHost", 938 "pub async fn checkpoint", 939 "pub fn checkpoint", 940 "checkpoint_mode", 941 ] { 942 assert!( 943 !connection_production.contains(forbidden) && !ROOT.contains(forbidden), 944 "Step 061 source exposes forbidden raw authority `{forbidden}`" 945 ); 946 } 947 948 for required in [ 949 "pub struct IntegrityCheckedAtUnixMs", 950 "pub enum IntegrityCheckOutcome", 951 "pub enum IntegrityDiagnosticCode", 952 "pub struct ServiceSqliteIntegrityReport", 953 "SqliteIntegrityFailed", 954 "ForeignKeyViolation", 955 "Box<[IntegrityDiagnosticCode]>", 956 "pub const fn storage_integrity", 957 "crate::persisted_value::INTEGRITY_CHECK_SQL", 958 "SELECT 1 FROM pragma_foreign_key_check LIMIT 1", 959 "connection.begin().await", 960 "transaction.rollback().await", 961 "validate()?", 962 ] { 963 assert!( 964 integrity_inspection_production.contains(required), 965 "Step 070 integrity inspection source is missing `{required}`" 966 ); 967 } 968 for required in [ 969 "integrity_driver: tokio::sync::Mutex<IntegrityInspectionDriver>", 970 ".try_lock()", 971 "driver.close_retained().await", 972 "QuarantinedConnection::new", 973 "crate::integrity::inspect_database_integrity", 974 "connection.trust()", 975 ] { 976 assert!( 977 connection_production.contains(required), 978 "Step 070 host integration is missing `{required}`" 979 ); 980 } 981 for forbidden in [ 982 "pub use sqlx", 983 "SqlitePool", 984 "PoolConnection", 985 "SystemTime", 986 "Instant", 987 "tokio::time", 988 "tokio::task", 989 "spawn", 990 "std::fs", 991 "OpenOptions", 992 "write_all", 993 ".persist(", 994 "cache", 995 "myc_", 996 "rhi_", 997 ] { 998 assert!( 999 !integrity_inspection_production.contains(forbidden), 1000 "Step 070 integrity inspection contains forbidden authority `{forbidden}`" 1001 ); 1002 } 1003 1004 for required in [ 1005 "NativeBackup::start", 1006 "lock_handle()", 1007 "tokio::task::spawn_blocking", 1008 "BACKUP_PAGES_PER_STEP", 1009 "HASH_BUFFER_BYTES", 1010 "OFlags::CREATE | OFlags::EXCL | OFlags::NOFOLLOW | OFlags::CLOEXEC", 1011 "Mode::RUSR | Mode::WUSR | Mode::XUSR", 1012 "Mode::RUSR | Mode::WUSR", 1013 "crate::persisted_value::INTEGRITY_CHECK_SQL", 1014 "crate::persisted_value::bounded_integrity_bytes", 1015 "FROM pragma_foreign_key_check", 1016 "BackupSourceValidator", 1017 "PoolConnection<Sqlite>", 1018 "CaptureCancellation", 1019 "CapturePermit", 1020 "ServiceBackupManifest::from_capture", 1021 "sync_state", 1022 "sync_directories", 1023 "hash_state", 1024 "validate_inventory", 1025 ] { 1026 assert!( 1027 backup_capture_production.contains(required), 1028 "Step 064 backup capture source is missing `{required}`" 1029 ); 1030 } 1031 for required in [ 1032 "use libsqlite3_sys as ffi;", 1033 "LockedSqliteHandle", 1034 "ffi::sqlite3_backup_init", 1035 "ffi::sqlite3_backup_step", 1036 "ffi::sqlite3_backup_finish", 1037 ] { 1038 assert!( 1039 SQLITE_NATIVE_BACKUP_SOURCE.contains(required), 1040 "sealed native backup adapter is missing `{required}`" 1041 ); 1042 } 1043 for forbidden in ["pub ", "SqliteConnection", "PoolConnection", "Path", "File"] { 1044 assert!( 1045 !SQLITE_NATIVE_BACKUP_SOURCE.contains(forbidden), 1046 "sealed native backup adapter exposes or owns forbidden authority `{forbidden}`" 1047 ); 1048 } 1049 for forbidden in [ 1050 "pub use rusqlite", 1051 "pub fn restore", 1052 "pub async fn restore", 1053 "pub fn verify_backup", 1054 "pub async fn verify_backup", 1055 "VACUUM INTO", 1056 "SystemTime::now", 1057 "tokio::time::timeout", 1058 "manifest.json", 1059 "create_dir_all", 1060 "std::fs::copy", 1061 ] { 1062 assert!( 1063 !backup_capture_production.contains(forbidden) && !ROOT.contains(forbidden), 1064 "Step 064 backup capture source contains deferred or public authority `{forbidden}`" 1065 ); 1066 } 1067 1068 for required in [ 1069 "pub fn verify_backup_bundle(", 1070 "NonZeroU64", 1071 "pub struct VerifiedServiceBackup", 1072 "pub const fn manifest(&self)", 1073 "pub const fn database_metadata(&self)", 1074 "Sha256::digest(manifest_bytes)", 1075 "BACKUP_MANIFEST_CANONICAL_MAX_BYTES", 1076 "OFlags::RDONLY | OFlags::DIRECTORY | OFlags::NOFOLLOW | OFlags::CLOEXEC", 1077 "OFlags::RDONLY | OFlags::NONBLOCK | OFlags::NOFOLLOW | OFlags::CLOEXEC", 1078 "Dir::read_from(&self.directory)", 1079 "crate::native_metadata::restrictive_directory(", 1080 "crate::native_metadata::restrictive_regular_file(", 1081 "open_sqlite_from_retained_state", 1082 "/proc/self/fd/{descriptor}", 1083 "/dev/fd/{descriptor}", 1084 ".filename(descriptor_path)", 1085 ".immutable(true)", 1086 "PRAGMA query_only = ON", 1087 "PRAGMA trusted_schema = OFF", 1088 "verify_connection_policy", 1089 "FROM pragma_database_list", 1090 "FROM main.sqlite_schema", 1091 "Some(\"table\")", 1092 "crate::persisted_value::INTEGRITY_CHECK_SQL", 1093 "FROM pragma_foreign_key_check", 1094 "state_schema_version() <= expected.supported_state_schema_version()", 1095 "binding.hash_state(maximum_state_bytes)", 1096 ] { 1097 assert!( 1098 backup_verify_production.contains(required), 1099 "Step 065 backup verifier is missing `{required}`" 1100 ); 1101 } 1102 for forbidden in [ 1103 "tokio::task::spawn_blocking", 1104 "pub fn state_file", 1105 "pub fn directory", 1106 "pub fn path", 1107 "pub fn restore", 1108 "pub async fn restore", 1109 "ServiceSqliteErrorKind::Restore", 1110 "create_dir", 1111 "remove_file", 1112 "std::fs::copy", 1113 "rename", 1114 "SystemTime::now", 1115 "tokio::time::timeout", 1116 ] { 1117 assert!( 1118 !backup_verify_production.contains(forbidden), 1119 "Step 065 backup verifier contains deferred or raw authority `{forbidden}`" 1120 ); 1121 } 1122 1123 for required in [ 1124 "radroots.service-sqlite.restore-marker", 1125 "RESTORE_MARKER_SCHEMA_VERSION: u32 = 1", 1126 "RESTORE_MARKER_MAX_BYTES: usize = 2_048", 1127 "radroots.service_sqlite.restore_marker.v1\\0", 1128 "state.restore-staged.sqlite", 1129 "state.restore-backup.sqlite", 1130 "state.restore-marker.v1", 1131 "state.restore-marker.v1.next", 1132 "enum RestoreRecoveryPhase", 1133 "Prepared", 1134 "LiveRetained", 1135 "ReplacementInstalled", 1136 "fn transitioned_to(", 1137 "serde(deny_unknown_fields)", 1138 "OFlags::RDONLY | OFlags::NOFOLLOW | OFlags::CLOEXEC | OFlags::NONBLOCK", 1139 "OFlags::RDWR", 1140 "OFlags::CREATE", 1141 "OFlags::EXCL", 1142 "Mode::RUSR | Mode::WUSR", 1143 "renameat(", 1144 "sync_all()", 1145 "cleanup_exact", 1146 "ServiceSqliteErrorKind::Restore", 1147 "ServiceSqliteErrorKind::Recovery", 1148 ] { 1149 assert!( 1150 restore_marker_production.contains(required), 1151 "Step 066 restore marker is missing `{required}`" 1152 ); 1153 } 1154 for forbidden in [ 1155 "pub mod restore", 1156 "pub use marker", 1157 "pub struct Restore", 1158 "pub enum Restore", 1159 "pub fn restore", 1160 "pub async fn restore", 1161 "tokio::", 1162 "sqlx::", 1163 "rusqlite::", 1164 "std::fs::copy", 1165 "state_database().rename", 1166 "remove_dir_all", 1167 "SystemTime", 1168 "timeout(", 1169 ] { 1170 assert!( 1171 !restore_marker_production.contains(forbidden) 1172 && !RESTORE_ROOT_SOURCE.contains(forbidden) 1173 && !ROOT.contains(forbidden), 1174 "Step 066 restore marker exposes or implements deferred authority `{forbidden}`" 1175 ); 1176 } 1177 1178 let restore_stage_production = RESTORE_STAGE_SOURCE 1179 .split_once( 1180 "#[cfg(all(test, any(target_os = \"linux\", target_os = \"macos\")))]\nmod tests", 1181 ) 1182 .map(|(production, _)| production) 1183 .expect("restore stage source must keep tests separated"); 1184 for required in [ 1185 "pub async fn stage_verified_restore(", 1186 "pub struct StagedServiceRestore", 1187 "VerifiedServiceBackup", 1188 "WriterAuthority::acquire(paths, OpenMode::ReadWriteExisting)", 1189 "tokio::spawn(async move", 1190 "tokio::task::spawn_blocking", 1191 "OFlags::CREATE", 1192 "OFlags::EXCL", 1193 "OFlags::NOFOLLOW", 1194 "OFlags::NONBLOCK", 1195 "STAGED_FILE_NAME", 1196 "verified.state_file()", 1197 ".validate_binding()", 1198 "verify_database_metadata", 1199 "verify_migration_history", 1200 "verify_database_integrity", 1201 "/proc/self/fd/{descriptor}", 1202 "/dev/fd/{descriptor}", 1203 "PRAGMA query_only = ON", 1204 "PRAGMA trusted_schema = OFF", 1205 "FROM pragma_database_list", 1206 "cleanup_exact_stage", 1207 "authority.release()", 1208 "StagedServiceRestore([redacted])", 1209 ] { 1210 assert!( 1211 restore_stage_production.contains(required), 1212 "Step 067 restore staging source is missing `{required}`" 1213 ); 1214 } 1215 for forbidden in [ 1216 "pub fn state_file", 1217 "pub fn directory", 1218 "pub fn path", 1219 "pub fn authority", 1220 "pub fn artifact", 1221 "RestoreMarkerBinding::create", 1222 "RestoreRecoveryPhase::LiveRetained", 1223 "RestoreRecoveryPhase::ReplacementInstalled", 1224 "renameat(", 1225 "std::fs::rename", 1226 "remove_dir_all", 1227 "SystemTime::now", 1228 "tokio::time::timeout", 1229 ] { 1230 assert!( 1231 !restore_stage_production.contains(forbidden), 1232 "Step 067 restore staging source contains deferred or raw authority `{forbidden}`" 1233 ); 1234 } 1235 let restore_finalize_production = RESTORE_FINALIZE_SOURCE 1236 .split_once( 1237 "#[cfg(all(test, any(target_os = \"linux\", target_os = \"macos\")))]\nstruct FailingFinalizeOperations", 1238 ) 1239 .map(|(production, _)| production) 1240 .expect("restore finalization source must keep failure injection separated"); 1241 for required in [ 1242 "pub async fn finalize_staged_restore(", 1243 "tokio::task::spawn_blocking", 1244 "RestoreRecoveryMarker::prepared(", 1245 "staged.disarm_cleanup()", 1246 "renameat_with(", 1247 "RenameFlags::NOREPLACE", 1248 "directory.sync_all()", 1249 "RestoreRecoveryPhase::LiveRetained", 1250 "RestoreRecoveryPhase::ReplacementInstalled", 1251 "verify_named_artifact(", 1252 "CancellationOnDrop", 1253 ] { 1254 assert!( 1255 restore_finalize_production.contains(required), 1256 "Step 068 restore finalization source is missing `{required}`" 1257 ); 1258 } 1259 for forbidden in [ 1260 "pub struct RestoreRecovery", 1261 "pub enum RestoreRecovery", 1262 "pub fn marker", 1263 "pub fn directory", 1264 "pub fn path", 1265 "sqlx::", 1266 "rusqlite::", 1267 "remove_file", 1268 "unlinkat", 1269 "remove_dir_all", 1270 "tokio::time::timeout", 1271 "ServiceSqliteHost", 1272 ] { 1273 assert!( 1274 !restore_finalize_production.contains(forbidden), 1275 "Step 068 restore finalization source contains deferred or raw authority `{forbidden}`" 1276 ); 1277 } 1278 for required in ["refuse_unresolved_recovery", "recover_for_open"] { 1279 assert!( 1280 RESTORE_ROOT_SOURCE.contains(required), 1281 "Step 068 recovery-open guard is missing `{required}`" 1282 ); 1283 } 1284 assert!(OPEN_SOURCE.contains("crate::restore::refuse_unresolved_recovery")); 1285 assert!(OPEN_SOURCE.contains("crate::restore::recover_for_open(paths, identity, authority)")); 1286 1287 let restore_recover_production = RESTORE_RECOVER_SOURCE 1288 .split_once("#[cfg(all(test, any(target_os = \"linux\", target_os = \"macos\")))]") 1289 .map(|(production, _)| production) 1290 .expect("restore recovery source must keep tests separated"); 1291 for required in [ 1292 "pub(crate) fn recover_for_open(", 1293 "WriterAuthority", 1294 "RestoreMarkerBinding::load_for_recovery", 1295 "matches_identity(identity)", 1296 "interrupted_transition(paths, authority)", 1297 "promote_interrupted_transition", 1298 "advance_for_recovery", 1299 "RestoreRecoveryPhase::Prepared", 1300 "RestoreRecoveryPhase::LiveRetained", 1301 "RestoreRecoveryPhase::ReplacementInstalled", 1302 "RenameFlags::NOREPLACE", 1303 "verify_named_artifact(", 1304 "hash_exact(", 1305 "remove_exact_artifact(", 1306 "marker.retire(paths, authority)", 1307 "state.sqlite-wal", 1308 "state.sqlite-shm", 1309 "state.sqlite-journal", 1310 "ServiceSqliteErrorKind::Recovery", 1311 ] { 1312 assert!( 1313 restore_recover_production.contains(required), 1314 "Step 069 restore recovery source is missing `{required}`" 1315 ); 1316 } 1317 for forbidden in [ 1318 "pub fn recover", 1319 "pub async fn recover", 1320 "pub struct PendingRestore", 1321 "sqlx::", 1322 "rusqlite::", 1323 "tokio::", 1324 "spawn_blocking", 1325 "tokio::time::timeout", 1326 "remove_dir_all", 1327 "SystemTime::now", 1328 ] { 1329 assert!( 1330 !restore_recover_production.contains(forbidden) && !ROOT.contains(forbidden), 1331 "Step 069 recovery exposes or implements forbidden authority `{forbidden}`" 1332 ); 1333 } 1334 for required in [ 1335 "STAGED_FILE_NAME", 1336 "BACKUP_FILE_NAME", 1337 "MARKER_FILE_NAME", 1338 "MARKER_NEXT_FILE_NAME", 1339 ] { 1340 assert!( 1341 RESTORE_RECOVER_SOURCE.contains(required), 1342 "Step 069 refusal inventory is missing `{required}`" 1343 ); 1344 } 1345 assert!(ROOT.contains( 1346 "pub use restore::{StagedServiceRestore, finalize_staged_restore, stage_verified_restore};" 1347 )); 1348 1349 for required in [ 1350 "self.pool.close().await", 1351 "PRAGMA wal_checkpoint(TRUNCATE)", 1352 "if busy == 0", 1353 ".close()", 1354 "authority.release()?", 1355 "inspection.release()?", 1356 "release_resources", 1357 "CheckpointBusy", 1358 "close_driver: tokio::sync::Mutex<PrivateCloseDriver>", 1359 "PrivateCloseDriver::Connecting", 1360 "PrivateCloseDriver::Connected", 1361 "PrivateCloseDriver::Closing", 1362 "connect.as_mut().await", 1363 "future.as_mut().await", 1364 ] { 1365 assert!( 1366 open_production.contains(required), 1367 "Step 062 close source is missing `{required}`" 1368 ); 1369 } 1370 for forbidden in [ 1371 "tokio::spawn", 1372 "Runtime::new", 1373 "pub enum Checkpoint", 1374 "pub struct Checkpoint", 1375 ] { 1376 assert!( 1377 !connection_production.contains(forbidden) && !open_production.contains(forbidden), 1378 "Step 062 close source contains forbidden authority `{forbidden}`" 1379 ); 1380 } 1381 1382 for required in [ 1383 "radroots.service_sqlite.schema_object.v1\\0", 1384 "radroots.service_sqlite.schema_snapshot.v1\\0", 1385 "radroots.service_sqlite.schema_catalog.v1\\0", 1386 "MAX_SCHEMA_OBJECT_COUNT", 1387 "MAX_SCHEMA_SQL_UTF8_BYTES", 1388 "MAX_SCHEMA_CATALOG_UTF8_BYTES", 1389 ".take(MAX_SCHEMA_OBJECT_COUNT + 1)", 1390 ".take(MAX_SCHEMA_VERSION_COUNT + 1)", 1391 "CREATE TABLE radroots_service_metadata", 1392 "CREATE TABLE schema_migrations", 1393 "schema_migrations_no_update", 1394 "schema_migrations_no_delete", 1395 "pub fn computed_digest", 1396 "pub fn new<I>", 1397 "pub(crate) async fn verify_schema_catalog", 1398 "FROM main.sqlite_schema", 1399 "LIMIT 4097", 1400 "typeof(sql) = 'text'", 1401 "length(CAST(sql AS BLOB)) BETWEEN 1 AND 1048576", 1402 ] { 1403 assert!( 1404 INTEGRITY_SOURCE.contains(required) || INTEGRITY_CATALOG_SOURCE.contains(required), 1405 "Step 060 integrity source is missing `{required}`" 1406 ); 1407 } 1408 1409 for forbidden in [ 1410 "pub async fn verify_schema_catalog", 1411 "pub fn verify_schema_catalog", 1412 "pub use sqlx", 1413 "SqlitePool", 1414 "PoolConnection", 1415 "pub fn sql(&self", 1416 "Serialize", 1417 "Deserialize", 1418 "myc_", 1419 "rhi_", 1420 ] { 1421 assert!( 1422 !INTEGRITY_SOURCE.contains(forbidden) && !INTEGRITY_CATALOG_SOURCE.contains(forbidden), 1423 "Step 060 integrity source contains forbidden surface `{forbidden}`" 1424 ); 1425 } 1426 1427 for forbidden in [ 1428 "pub fn content(&self", 1429 "pub fn callback_definition", 1430 "pub fn migration_sql", 1431 "pub fn apply_governed_migrations", 1432 "pub async fn apply_governed_migrations", 1433 "pub use sqlx", 1434 "Serialize", 1435 "Deserialize", 1436 ] { 1437 assert!( 1438 !migration_production.contains(forbidden), 1439 "Step 059 migration source contains deferred surface `{forbidden}`" 1440 ); 1441 } 1442 1443 for forbidden in [ 1444 "radroots_service_host", 1445 "rusqlite", 1446 "tokio", 1447 "std::fs", 1448 "OpenOptions", 1449 "create_dir", 1450 ] { 1451 assert!( 1452 !authority_production.contains(forbidden) 1453 && !ERROR_SOURCE.contains(forbidden) 1454 && !CONFIG_SOURCE.contains(forbidden) 1455 && !STATUS_SOURCE.contains(forbidden), 1456 "service SQLite source contains deferred surface `{forbidden}`" 1457 ); 1458 } 1459 1460 for required in [ 1461 "PRAGMA application_id", 1462 "LIMIT 2", 1463 "SourceGeneration", 1464 "NonZeroU32", 1465 "pub(crate) async fn write_database_metadata", 1466 "pub(crate) async fn verify_database_metadata", 1467 "MigrationLedgerInitializationFailure", 1468 "ServiceSqliteErrorKind::Migration", 1469 ] { 1470 assert!( 1471 METADATA_SOURCE.contains(required), 1472 "Step 057 metadata source is missing `{required}`" 1473 ); 1474 } 1475 1476 for required in [ 1477 "radroots_service_metadata", 1478 "source_generation BLOB", 1479 "state_schema_version INTEGER", 1480 "created_at_unix_ms INTEGER", 1481 "radroots_service_metadata_guard_update", 1482 "radroots_service_metadata_no_delete", 1483 "schema_migrations", 1484 "applied_at_unix_s", 1485 "service_commit TEXT", 1486 "lib_revision TEXT", 1487 "provider_contract_version INTEGER", 1488 ] { 1489 assert!( 1490 INTEGRITY_CATALOG_SOURCE.contains(required), 1491 "shared schema authority is missing `{required}`" 1492 ); 1493 } 1494 1495 for forbidden in [ 1496 "pub use sqlx", 1497 "pub fn write_database_metadata", 1498 "pub async fn write_database_metadata", 1499 "pub fn verify_database_metadata", 1500 "pub async fn verify_database_metadata", 1501 "myc_", 1502 "rhi_", 1503 "SystemTime::now", 1504 "getrandom", 1505 "tokio::runtime", 1506 ] { 1507 assert!( 1508 !METADATA_SOURCE.contains(forbidden), 1509 "Step 057 metadata source contains forbidden surface `{forbidden}`" 1510 ); 1511 } 1512 1513 for required in [ 1514 "journal_mode(SqliteJournalMode::Wal)", 1515 "synchronous(SqliteSynchronous::Full)", 1516 ".foreign_keys(true)", 1517 ".pragma(\"trusted_schema\", \"OFF\")", 1518 ".create_if_missing(false)", 1519 ".statement_cache_capacity(STATEMENT_CACHE_CAPACITY)", 1520 ".command_buffer_size(COMMAND_BUFFER_CAPACITY)", 1521 ".row_buffer_size(ROW_BUFFER_CAPACITY)", 1522 ".immutable(true)", 1523 "\"query_only\"", 1524 "crate::native_metadata::sqlite_wal_header(&sqlite_header)", 1525 "WAL_FILE_NAME", 1526 "SHARED_MEMORY_FILE_NAME", 1527 ".min_connections(1)", 1528 ".max_connections(policy.max_connections())", 1529 ".acquire_timeout(policy.busy_timeout())", 1530 ".idle_timeout(None)", 1531 ".max_lifetime(None)", 1532 ".after_connect", 1533 ".before_acquire", 1534 "PRAGMA busy_timeout", 1535 "connection.close_on_drop()", 1536 ] { 1537 assert!( 1538 OPEN_SOURCE.contains(required), 1539 "Step 056 connection source is missing `{required}`" 1540 ); 1541 } 1542 1543 for forbidden in [ 1544 "pub use sqlx", 1545 "pub use sqlx::SqliteConnection", 1546 "pub use sqlx::SqlitePool", 1547 "pub use sqlx::Pool", 1548 "pub struct PrivateConnectionPool", 1549 "pub fn open_connection_pool", 1550 "pub async fn open_connection_pool", 1551 "tokio::runtime", 1552 "Runtime::new", 1553 ] { 1554 assert!( 1555 !ROOT.contains(forbidden) 1556 && !CONFIG_SOURCE.contains(forbidden) 1557 && !OPEN_SOURCE.contains(forbidden), 1558 "Step 056 source exposes forbidden pool/runtime surface `{forbidden}`" 1559 ); 1560 } 1561 1562 for required in [ 1563 "OFlags::EXCL", 1564 "OFlags::NOFOLLOW", 1565 "OFlags::CLOEXEC", 1566 "SERVICE_STATE_DATABASE_FILE_NAME", 1567 "sync_database", 1568 "sync_directory", 1569 "validate_entry", 1570 "unlink_database", 1571 "pub struct ServiceSqliteInitializer<'connection>", 1572 "impl<'executor, 'connection> Executor<'executor>", 1573 "ServiceSqliteInitializerFuture", 1574 ".begin_with(\"BEGIN IMMEDIATE\")", 1575 "write_database_metadata_in_transaction", 1576 "permit_outer_commit", 1577 "permit_runner_rollback", 1578 "contains_forbidden_statement_control", 1579 "RADROOTS_FORBIDDEN_INITIALIZATION_STATEMENT_CONTROL", 1580 "pending.sqlite_descriptor_path()", 1581 "journal_mode(SqliteJournalMode::Memory)", 1582 "connection.close().await", 1583 ] { 1584 assert!( 1585 INITIALIZE_SOURCE.contains(required), 1586 "Step 055 initialization source is missing `{required}`" 1587 ); 1588 } 1589 1590 for forbidden in [ 1591 "create_dir", 1592 "create_dir_all", 1593 "OpenOptions::new", 1594 "remove_file", 1595 "tokio", 1596 "rusqlite", 1597 "Command::new", 1598 "std::process", 1599 "pub fn directory", 1600 "FnOnce(PathBuf)", 1601 "FnOnce(std::path::PathBuf)", 1602 "to_path_buf()).await", 1603 ".filename(paths.state_database())", 1604 "Deref for ServiceSqliteInitializer", 1605 "AsRef<SqliteConnection>", 1606 "pub fn connection(", 1607 "pub fn into_inner(", 1608 ] { 1609 let production = INITIALIZE_SOURCE 1610 .split_once("#[cfg(test)]") 1611 .map(|(source, _)| source) 1612 .expect("initialization source must keep tests separated"); 1613 assert!( 1614 !production.contains(forbidden), 1615 "Step 055 production source contains deferred or bypass surface `{forbidden}`" 1616 ); 1617 } 1618 1619 for required in [ 1620 "pub async fn open_or_initialize", 1621 "initialize_or_existing_database", 1622 "InitializeDatabaseOutcome::Initialized", 1623 "InitializeDatabaseOutcome::Existing", 1624 "schema.matches_migrations(migrations)", 1625 "open_existing_connection_pool_with_intent_and_authority", 1626 "The existing branch never runs", 1627 "the initializer. Callers therefore do not use pathname probes, error-text", 1628 ] { 1629 assert!( 1630 CONNECTION_SOURCE.contains(required) 1631 || OPEN_SOURCE.contains(required) 1632 || README.contains(required), 1633 "Step 243 atomic bootstrap boundary is missing `{required}`" 1634 ); 1635 } 1636 for forbidden in ["try_exists()", "error.to_string()", "create_dir_all"] { 1637 let connection_production = CONNECTION_SOURCE 1638 .split_once("#[cfg(test)]") 1639 .map_or(CONNECTION_SOURCE, |(production, _)| production); 1640 assert!( 1641 !connection_production.contains(forbidden), 1642 "Step 243 atomic bootstrap uses forbidden branch surface `{forbidden}`" 1643 ); 1644 } 1645 1646 for required in [ 1647 "fs2::FileExt", 1648 "rustix", 1649 "try_lock_exclusive", 1650 "NOFOLLOW", 1651 "CLOEXEC", 1652 "st_nlink", 1653 "fchmod", 1654 "pub fn release(&mut self)", 1655 "database_path: paths.state_database().to_path_buf()", 1656 "pub(crate) fn validate_for", 1657 "validate_authority_binding", 1658 "directory_device", 1659 "directory_inode", 1660 "lock_device", 1661 "lock_inode", 1662 "current_lock_status", 1663 ] { 1664 assert!( 1665 AUTHORITY_SOURCE.contains(required), 1666 "Step 054 authority source is missing `{required}`" 1667 ); 1668 } 1669 1670 for required in [ 1671 ".inspection_guard", 1672 ".validate_for(&self.paths)?", 1673 "fn validate_for(&self, paths: &ServiceSqlitePaths)", 1674 "held_lock_status", 1675 "lock_device", 1676 "directory_device", 1677 "WAL_FILE_NAME", 1678 "SHARED_MEMORY_FILE_NAME", 1679 "crate::native_metadata::secure_directory(", 1680 "crate::native_metadata::exact_regular_file(", 1681 "crate::native_metadata::identity_pair_matches(", 1682 ] { 1683 assert!( 1684 OPEN_SOURCE.contains(required), 1685 "Step 061 live authority source is missing `{required}`" 1686 ); 1687 } 1688 1689 for forbidden in [ 1690 "remove_file", 1691 "create_dir", 1692 "set_len", 1693 "truncate", 1694 "std::process", 1695 "Command::new", 1696 "sqlx", 1697 "rusqlite", 1698 "tokio", 1699 ] { 1700 assert!( 1701 !authority_production.contains(forbidden), 1702 "Step 054 authority production source contains deferred surface `{forbidden}`" 1703 ); 1704 } 1705 1706 for forbidden in [ 1707 "Deserialize", 1708 "impl Default for OpenMode", 1709 "pub fn from_paths", 1710 "pub fn new(", 1711 "std::fs", 1712 "symlink_metadata", 1713 "create_dir", 1714 "File::open", 1715 "OpenOptions", 1716 ] { 1717 assert!( 1718 !open_production.contains(forbidden), 1719 "Step 053 open source contains deferred or forgeable surface `{forbidden}`" 1720 ); 1721 } 1722 } 1723 1724 #[test] 1725 fn production_corpus_is_service_neutral_and_owns_only_shared_persistent_objects() { 1726 let production = [ 1727 ROOT, 1728 AUTHORITY_SOURCE, 1729 BACKUP_SOURCE, 1730 BACKUP_CAPTURE_SOURCE, 1731 BACKUP_VERIFY_SOURCE, 1732 CONFIG_SOURCE, 1733 CONNECTION_SOURCE, 1734 ERROR_SOURCE, 1735 FAILPOINT_SOURCE, 1736 INITIALIZE_SOURCE, 1737 INTEGRITY_SOURCE, 1738 INTEGRITY_CATALOG_SOURCE, 1739 INTEGRITY_INSPECTION_SOURCE, 1740 METADATA_SOURCE, 1741 MIGRATION_SOURCE, 1742 OPEN_SOURCE, 1743 RESTORE_MARKER_SOURCE, 1744 RESTORE_FINALIZE_SOURCE, 1745 RESTORE_RECOVER_SOURCE, 1746 RESTORE_ROOT_SOURCE, 1747 RESTORE_STAGE_SOURCE, 1748 STATUS_SOURCE, 1749 DISK_SOURCE, 1750 TRANSACTION_CONTROL_SOURCE, 1751 ] 1752 .map(production_before_tests) 1753 .join("\n"); 1754 1755 for surface in [README, PUBLIC_API, production.as_str()] { 1756 let lowercase = surface.to_ascii_lowercase(); 1757 for forbidden in ["myc", "rhi"] { 1758 assert!( 1759 !lowercase.contains(forbidden), 1760 "public or production boundary contains product identifier `{forbidden}`" 1761 ); 1762 } 1763 } 1764 1765 assert_eq!( 1766 production.matches("CREATE TABLE ").count(), 1767 2, 1768 "built-in persistent table inventory drifted" 1769 ); 1770 assert_eq!( 1771 production.matches("CREATE TRIGGER ").count(), 1772 4, 1773 "built-in persistent trigger inventory drifted" 1774 ); 1775 for required in [ 1776 "CREATE TABLE radroots_service_metadata", 1777 "CREATE TABLE schema_migrations", 1778 "CREATE TRIGGER radroots_service_metadata_guard_update", 1779 "CREATE TRIGGER radroots_service_metadata_no_delete", 1780 "CREATE TRIGGER schema_migrations_no_update", 1781 "CREATE TRIGGER schema_migrations_no_delete", 1782 ] { 1783 assert!( 1784 production.contains(required), 1785 "shared persistent-object inventory is missing `{required}`" 1786 ); 1787 } 1788 for forbidden in ["CREATE INDEX ", "CREATE VIEW "] { 1789 assert!( 1790 !production.contains(forbidden), 1791 "built-in persistent-object inventory contains forbidden `{forbidden}`" 1792 ); 1793 } 1794 } 1795 1796 fn production_before_tests(source: &str) -> &str { 1797 let Some(module_position) = source.rfind("mod tests {") else { 1798 return source; 1799 }; 1800 let prefix = &source[..module_position]; 1801 let attribute_position = prefix 1802 .rfind("#[cfg") 1803 .expect("test module must retain an explicit cfg attribute"); 1804 &source[..attribute_position] 1805 } 1806 1807 fn public_modules(root: &str) -> BTreeSet<&str> { 1808 root.lines() 1809 .filter_map(|line| line.strip_prefix("pub mod ")) 1810 .filter_map(|module| module.strip_suffix(';')) 1811 .collect() 1812 } 1813 1814 fn private_modules(root: &str) -> BTreeSet<&str> { 1815 root.lines() 1816 .filter_map(|line| line.strip_prefix("mod ")) 1817 .filter_map(|module| module.strip_suffix(';')) 1818 .collect() 1819 } 1820 1821 fn dependency_keys<'a>(manifest: &'a str, section: &str) -> BTreeSet<&'a str> { 1822 manifest 1823 .split_once(section) 1824 .map(|(_, dependencies)| dependencies) 1825 .unwrap_or_default() 1826 .lines() 1827 .take_while(|line| !line.starts_with('[')) 1828 .filter_map(|line| line.split_once('=').map(|(key, _)| key.trim())) 1829 .filter(|key| !key.is_empty()) 1830 .collect() 1831 } 1832 1833 fn package_catalog_entry<'a>(catalog: &'a str, package: &str) -> &'a str { 1834 let marker = format!("[[package]]\nname = \"{package}\"\n"); 1835 let entry = catalog 1836 .split_once(&marker) 1837 .map(|(_, entry)| entry) 1838 .expect("package catalog must contain service SQLite"); 1839 entry 1840 .split_once("\n[[package]]") 1841 .map_or(entry, |(entry, _)| entry) 1842 } 1843 1844 fn toml_section<'a>(document: &'a str, start: &str, end: &str) -> &'a str { 1845 let section = document 1846 .split_once(start) 1847 .map(|(_, section)| section) 1848 .expect("TOML section must exist"); 1849 section 1850 .split_once(end) 1851 .map(|(section, _)| section) 1852 .expect("TOML section terminator must exist") 1853 } 1854 1855 fn toml_array<'a>(section: &'a str, key: &str) -> &'a str { 1856 let marker = format!("{key} = ["); 1857 let values = section 1858 .split_once(&marker) 1859 .map(|(_, values)| values) 1860 .expect("TOML array must exist"); 1861 values 1862 .split_once(']') 1863 .map(|(values, _)| values) 1864 .expect("TOML array must terminate") 1865 }