lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

package_boundary.rs (6165B)


      1 use std::collections::BTreeSet;
      2 use std::fs;
      3 use std::path::Path;
      4 
      5 const MANIFEST: &str = include_str!("../Cargo.toml");
      6 const README: &str = include_str!("../README.md");
      7 const EXAMPLE: &str = include_str!("../examples/prepare_query.rs");
      8 const PUBLIC_API: &str = include_str!("../../../contracts/api_baselines/radroots_geonames.txt");
      9 const ROOT: &str = include_str!("../src/lib.rs");
     10 const PUBLISH_POLICY: &str = include_str!("../../../contracts/releases/publish_policy.toml");
     11 
     12 #[test]
     13 fn manifest_and_root_match_the_governed_provider_boundary() {
     14     for required in [
     15         "name = \"radroots_geonames\"",
     16         "version = \"0.1.0-alpha\"",
     17         "publish = [\"crates-io\"]",
     18         "[lib]\nname = \"radroots_geonames\"",
     19     ] {
     20         assert!(
     21             MANIFEST.contains(required),
     22             "manifest is missing `{required}`"
     23         );
     24     }
     25     assert!(!MANIFEST.contains("[features]"));
     26     assert!(radroots_dependency_keys(MANIFEST).is_empty());
     27     assert_eq!(
     28         public_modules(ROOT),
     29         BTreeSet::from(["asset", "database", "download", "model", "query"])
     30     );
     31     assert_eq!(private_modules(ROOT), BTreeSet::from(["error"]));
     32     for export in [
     33         "pub use asset::{AssetSpec, AssetStatus};",
     34         "pub use database::Geocoder;",
     35         "pub use error::Error;",
     36         "pub use model::{Candidate, Point};",
     37         "pub use query::Query;",
     38     ] {
     39         assert!(ROOT.contains(export), "crate root is missing `{export}`");
     40     }
     41 }
     42 
     43 #[test]
     44 fn documentation_example_and_reviewed_api_baseline_are_complete() {
     45     for required in [
     46         "## Prepare a query without I/O",
     47         "## Asset identity and acquisition",
     48         "## Database and query behavior",
     49         "## Errors, serialization, and side effects",
     50         "## Intended consumers",
     51         "radroots_crates_release_v1.toml",
     52         "examples/prepare_query.rs",
     53         "contracts/api_baselines/radroots_geonames.txt",
     54     ] {
     55         assert!(README.contains(required), "README is missing `{required}`");
     56     }
     57     for required in [
     58         "official_asset_spec()",
     59         "Query::locality(\"Victoria\")",
     60         "Query::reverse(Point::new(",
     61         "without I/O",
     62     ] {
     63         assert!(
     64             EXAMPLE.contains(required),
     65             "example is missing `{required}`"
     66         );
     67     }
     68     for required in [
     69         "pub struct radroots_geonames::AssetSpec",
     70         "pub enum radroots_geonames::AssetStatus",
     71         "pub struct radroots_geonames::Candidate",
     72         "pub struct radroots_geonames::Geocoder",
     73         "pub struct radroots_geonames::Point",
     74         "pub struct radroots_geonames::Query",
     75         "pub enum radroots_geonames::Error",
     76         "Geocoder::open",
     77         "Geocoder::query",
     78         "Geocoder::close",
     79         "pub async fn radroots_geonames::database::Geocoder::close",
     80         "pub async fn radroots_geonames::database::Geocoder::open",
     81         "pub async fn radroots_geonames::database::Geocoder::query",
     82     ] {
     83         assert!(
     84             PUBLIC_API.contains(required),
     85             "public API is missing `{required}`"
     86         );
     87     }
     88     for forbidden in [
     89         "rusqlite",
     90         "sqlx",
     91         "reqwest",
     92         "tokio",
     93         "runtime_paths",
     94         "GeocoderLocality",
     95         "GeoNamesAsset",
     96     ] {
     97         assert!(
     98             !PUBLIC_API.contains(forbidden),
     99             "public API exposes `{forbidden}`"
    100         );
    101     }
    102 }
    103 
    104 #[test]
    105 fn provider_source_has_no_hidden_runtime_path_or_download_implementation() {
    106     let source_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
    107     let source = fs::read_dir(source_root)
    108         .expect("source directory")
    109         .map(|entry| {
    110             let path = entry.expect("source entry").path();
    111             fs::read_to_string(path).expect("utf-8 source")
    112         })
    113         .collect::<String>();
    114     for forbidden in [
    115         "radroots_runtime_paths",
    116         "reqwest::",
    117         "tokio::runtime",
    118         "Runtime::new",
    119         "Builder::new_",
    120         "tokio::spawn",
    121         "spawn_blocking",
    122         "pub use sqlx",
    123         "std::env::",
    124         "directories::",
    125         "test-fixture-geonames-asset",
    126     ] {
    127         assert!(
    128             !source.contains(forbidden),
    129             "provider source contains `{forbidden}`"
    130         );
    131     }
    132     for required in [
    133         "use sqlx::{",
    134         "use tokio::sync::Mutex;",
    135         "pub async fn open",
    136         "pub async fn query",
    137         "pub async fn close",
    138     ] {
    139         assert!(
    140             source.contains(required),
    141             "provider source is missing `{required}`"
    142         );
    143     }
    144     for required in [
    145         "caller-driven async operations",
    146         "provides the async runtime",
    147         "does not create one or spawn crate-owned",
    148     ] {
    149         assert!(README.contains(required), "README is missing `{required}`");
    150     }
    151 }
    152 
    153 #[test]
    154 fn superseded_geocoder_package_is_removed() {
    155     let crate_root = Path::new(env!("CARGO_MANIFEST_DIR"));
    156     assert!(
    157         !crate_root
    158             .parent()
    159             .expect("crates root")
    160             .join("geocoder")
    161             .exists()
    162     );
    163     assert!(!PUBLISH_POLICY.contains("\"radroots_geocoder\""));
    164 }
    165 
    166 fn radroots_dependency_keys(manifest: &str) -> BTreeSet<&str> {
    167     dependency_keys(manifest)
    168         .into_iter()
    169         .filter(|key| key.starts_with("radroots_"))
    170         .collect()
    171 }
    172 
    173 fn dependency_keys(manifest: &str) -> BTreeSet<&str> {
    174     manifest
    175         .split_once("[dependencies]")
    176         .map(|(_, dependencies)| dependencies)
    177         .unwrap_or_default()
    178         .lines()
    179         .skip(1)
    180         .take_while(|line| !line.starts_with('['))
    181         .filter_map(|line| line.split_once('=').map(|(key, _)| key.trim()))
    182         .filter(|key| !key.is_empty())
    183         .collect()
    184 }
    185 
    186 fn public_modules(root: &str) -> BTreeSet<&str> {
    187     root.lines()
    188         .filter_map(|line| line.trim().strip_prefix("pub mod "))
    189         .filter_map(|module| module.strip_suffix(';'))
    190         .collect()
    191 }
    192 
    193 fn private_modules(root: &str) -> BTreeSet<&str> {
    194     root.lines()
    195         .filter_map(|line| line.trim().strip_prefix("mod "))
    196         .filter_map(|module| module.strip_suffix(';'))
    197         .collect()
    198 }