tangle


git clone https://radroots.dev/git/tangle.git
Log | Files | Refs | README | LICENSE

lib.rs (5185B)


      1 #![forbid(unsafe_code)]
      2 
      3 pub mod backup;
      4 pub(crate) mod client_message;
      5 pub mod config;
      6 pub mod errors;
      7 pub mod event_bus;
      8 pub mod export;
      9 pub mod groups;
     10 pub mod host;
     11 pub mod logging;
     12 pub mod nip11;
     13 pub mod ops;
     14 pub(crate) mod pocket_conversion;
     15 pub(crate) mod pocket_event_validation;
     16 pub mod rate_limits;
     17 pub mod relay;
     18 pub mod resource_limits;
     19 pub mod runtime;
     20 pub mod server;
     21 pub mod session;
     22 pub mod tenant;
     23 
     24 use std::{fmt, fs, path::Path, path::PathBuf};
     25 
     26 use config::{
     27     TangleHostRuntimeConfigSet, parse_tangle_host_runtime_config_json,
     28     parse_tenant_runtime_config_json,
     29 };
     30 use errors::BaseRelayError;
     31 
     32 pub const TANGLE_RELAY_SOFTWARE: &str = "https://github.com/radrootslabs/tangle";
     33 pub const TANGLE_RELAY_VERSION: &str = env!("CARGO_PKG_VERSION");
     34 
     35 #[derive(Debug)]
     36 pub enum TangleRuntimeLoadError {
     37     ReadConfig {
     38         path: PathBuf,
     39         source: std::io::Error,
     40     },
     41     ReadTenantConfigDir {
     42         path: PathBuf,
     43         source: std::io::Error,
     44     },
     45     ReadTenantConfig {
     46         path: PathBuf,
     47         source: std::io::Error,
     48     },
     49     ParseConfig(BaseRelayError),
     50     OpenRelay(BaseRelayError),
     51 }
     52 
     53 impl fmt::Display for TangleRuntimeLoadError {
     54     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
     55         match self {
     56             Self::ReadConfig { path, source } => {
     57                 write!(
     58                     formatter,
     59                     "failed to read tangle runtime config `{}`: {source}",
     60                     path.display()
     61                 )
     62             }
     63             Self::ReadTenantConfigDir { path, source } => {
     64                 write!(
     65                     formatter,
     66                     "failed to read tangle tenant config directory `{}`: {source}",
     67                     path.display()
     68                 )
     69             }
     70             Self::ReadTenantConfig { path, source } => {
     71                 write!(
     72                     formatter,
     73                     "failed to read tangle tenant config `{}`: {source}",
     74                     path.display()
     75                 )
     76             }
     77             Self::ParseConfig(error) => write!(formatter, "{error}"),
     78             Self::OpenRelay(error) => write!(formatter, "{error}"),
     79         }
     80     }
     81 }
     82 
     83 impl std::error::Error for TangleRuntimeLoadError {}
     84 
     85 pub fn load_tangle_host_runtime_config(
     86     path: impl AsRef<Path>,
     87 ) -> Result<TangleHostRuntimeConfigSet, TangleRuntimeLoadError> {
     88     let path = path.as_ref();
     89     let raw = fs::read_to_string(path).map_err(|source| TangleRuntimeLoadError::ReadConfig {
     90         path: path.to_path_buf(),
     91         source,
     92     })?;
     93     let host =
     94         parse_tangle_host_runtime_config_json(&raw).map_err(TangleRuntimeLoadError::ParseConfig)?;
     95     let config_dir = resolve_config_path(path.parent(), host.tenant_config_dir());
     96     let mut tenant_paths = fs::read_dir(&config_dir)
     97         .map_err(|source| TangleRuntimeLoadError::ReadTenantConfigDir {
     98             path: config_dir.clone(),
     99             source,
    100         })?
    101         .map(|entry| entry.map(|entry| entry.path()))
    102         .collect::<Result<Vec<_>, _>>()
    103         .map_err(|source| TangleRuntimeLoadError::ReadTenantConfigDir {
    104             path: config_dir.clone(),
    105             source,
    106         })?;
    107     tenant_paths.retain(|path| {
    108         path.is_file()
    109             && path
    110                 .extension()
    111                 .is_some_and(|extension| extension == "json")
    112     });
    113     tenant_paths.sort();
    114     let mut tenants = Vec::with_capacity(tenant_paths.len());
    115     for tenant_path in tenant_paths {
    116         let raw = fs::read_to_string(&tenant_path).map_err(|source| {
    117             TangleRuntimeLoadError::ReadTenantConfig {
    118                 path: tenant_path.clone(),
    119                 source,
    120             }
    121         })?;
    122         let tenant =
    123             parse_tenant_runtime_config_json(&raw).map_err(TangleRuntimeLoadError::ParseConfig)?;
    124         tenants.push(tenant);
    125     }
    126     TangleHostRuntimeConfigSet::new(host, tenants).map_err(TangleRuntimeLoadError::ParseConfig)
    127 }
    128 
    129 fn resolve_config_path(base: Option<&Path>, path: &Path) -> PathBuf {
    130     if path.is_absolute() {
    131         path.to_path_buf()
    132     } else if let Some(base) = base {
    133         base.join(path)
    134     } else {
    135         path.to_path_buf()
    136     }
    137 }
    138 
    139 #[cfg(test)]
    140 mod tests {
    141     use crate::pocket_conversion::{pocket_event_to_tangle, tangle_event_to_pocket};
    142     use tangle_protocol::{Tag, event_from_value, event_to_value};
    143     use tangle_test_support::{FixtureKey, tangle_v2_event};
    144 
    145     #[test]
    146     fn pocket_event_conversion_accepts_protocol_event_json_shapes() {
    147         let event = tangle_v2_event(
    148             FixtureKey::Owner,
    149             1_714_124_433,
    150             30_402,
    151             vec![
    152                 Tag::from_parts("d", &["market"]).expect("d"),
    153                 Tag::from_parts("t", &["radroots", "farm"]).expect("t"),
    154             ],
    155             "json parity",
    156         )
    157         .expect("event");
    158         let parsed = event_from_value(&event_to_value(&event)).expect("parsed");
    159         let pocket = tangle_event_to_pocket(&parsed).expect("pocket");
    160         let converted = pocket_event_to_tangle(&pocket).expect("converted");
    161 
    162         assert_eq!(parsed, event);
    163         assert_eq!(converted, event);
    164     }
    165 }