tangle


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

lib.rs (5210B)


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