locus-core-rs 0.2.0

Core STTP parsing, validation, storage contracts, and application services for Rust
Documentation
use std::env;
use std::path::{Path, PathBuf};

use anyhow::{Result, anyhow};

#[derive(Debug, Clone)]
pub struct SurrealDbEndpointsSettings {
    pub embedded: Option<String>,
    pub remote: Option<String>,
}

impl Default for SurrealDbEndpointsSettings {
    fn default() -> Self {
        Self {
            embedded: Some("surrealkv://data/locus".to_string()),
            remote: None,
        }
    }
}

#[derive(Debug, Clone)]
pub struct SurrealDbSettings {
    pub endpoints: SurrealDbEndpointsSettings,
    pub namespace: String,
    pub database: String,
    pub user: Option<String>,
    pub password: Option<String>,
}

impl Default for SurrealDbSettings {
    fn default() -> Self {
        Self {
            endpoints: SurrealDbEndpointsSettings::default(),
            namespace: "entasis".to_string(),
            database: "locus".to_string(),
            user: Some("root".to_string()),
            password: Some("root".to_string()),
        }
    }
}

impl SurrealDbSettings {
    pub fn endpoint(&self, use_remote: bool) -> Result<String> {
        if use_remote {
            if let Some(remote) = self
                .endpoints
                .remote
                .as_ref()
                .filter(|value| !value.trim().is_empty())
            {
                return Ok(remote.clone());
            }
        }

        if let Some(embedded) = self
            .endpoints
            .embedded
            .as_ref()
            .filter(|value| !value.trim().is_empty())
        {
            return Ok(embedded.clone());
        }

        let mode = if use_remote { "remote" } else { "embedded" };
        Err(anyhow!(
            "No SurrealDB endpoint configured for mode {mode}. Set SurrealDb Endpoints."
        ))
    }
}

#[derive(Debug, Clone)]
pub struct SurrealDbRuntimeOptions {
    pub root_dir: String,
    pub use_remote: bool,
    pub endpoint: String,
    pub namespace: String,
    pub database: String,
}

impl SurrealDbRuntimeOptions {
    pub fn from_args(
        args: &[String],
        settings: &SurrealDbSettings,
        root_directory_name: Option<&str>,
    ) -> Result<Self> {
        let use_remote = args.iter().any(|arg| arg.eq_ignore_ascii_case("--remote"));
        let root_name = root_directory_name
            .unwrap_or(".locus")
            .trim()
            .to_string();

        let home = env::var("HOME").map_err(|_| anyhow!("HOME is not set"))?;
        let root_dir = PathBuf::from(home).join(if root_name.is_empty() {
            ".locus"
        } else {
            root_name.as_str()
        });

        let endpoint = settings.endpoint(use_remote)?;
        let normalized_endpoint = normalize_embedded_endpoint(&endpoint, &root_dir, use_remote)?;

        Ok(Self {
            root_dir: root_dir.to_string_lossy().to_string(),
            use_remote,
            endpoint: normalized_endpoint,
            namespace: settings.namespace.clone(),
            database: settings.database.clone(),
        })
    }
}

fn normalize_embedded_endpoint(
    endpoint: &str,
    root_dir: &Path,
    use_remote: bool,
) -> Result<String> {
    if use_remote {
        return Ok(endpoint.to_string());
    }

    const SCHEME: &str = "surrealkv://";
    if !endpoint.starts_with(SCHEME) {
        return Ok(endpoint.to_string());
    }

    let endpoint_path = endpoint.trim_start_matches(SCHEME);
    let absolute_path = if Path::new(endpoint_path).is_absolute() {
        PathBuf::from(endpoint_path)
    } else {
        root_dir.join(endpoint_path)
    };

    if let Some(parent) = absolute_path.parent() {
        std::fs::create_dir_all(parent)?;
    }

    Ok(format!("{SCHEME}{}", absolute_path.to_string_lossy()))
}