locus_core_rs/storage/surrealdb/
runtime.rs1use std::env;
2use std::path::{Path, PathBuf};
3
4use anyhow::{Result, anyhow};
5
6#[derive(Debug, Clone)]
7pub struct SurrealDbEndpointsSettings {
8 pub embedded: Option<String>,
9 pub remote: Option<String>,
10}
11
12impl Default for SurrealDbEndpointsSettings {
13 fn default() -> Self {
14 Self {
15 embedded: Some("surrealkv://data/locus".to_string()),
16 remote: None,
17 }
18 }
19}
20
21#[derive(Debug, Clone)]
22pub struct SurrealDbSettings {
23 pub endpoints: SurrealDbEndpointsSettings,
24 pub namespace: String,
25 pub database: String,
26 pub user: Option<String>,
27 pub password: Option<String>,
28}
29
30impl Default for SurrealDbSettings {
31 fn default() -> Self {
32 Self {
33 endpoints: SurrealDbEndpointsSettings::default(),
34 namespace: "entasis".to_string(),
35 database: "locus".to_string(),
36 user: Some("root".to_string()),
37 password: Some("root".to_string()),
38 }
39 }
40}
41
42impl SurrealDbSettings {
43 pub fn endpoint(&self, use_remote: bool) -> Result<String> {
44 if use_remote {
45 if let Some(remote) = self
46 .endpoints
47 .remote
48 .as_ref()
49 .filter(|value| !value.trim().is_empty())
50 {
51 return Ok(remote.clone());
52 }
53 }
54
55 if let Some(embedded) = self
56 .endpoints
57 .embedded
58 .as_ref()
59 .filter(|value| !value.trim().is_empty())
60 {
61 return Ok(embedded.clone());
62 }
63
64 let mode = if use_remote { "remote" } else { "embedded" };
65 Err(anyhow!(
66 "No SurrealDB endpoint configured for mode {mode}. Set SurrealDb Endpoints."
67 ))
68 }
69}
70
71#[derive(Debug, Clone)]
72pub struct SurrealDbRuntimeOptions {
73 pub root_dir: String,
74 pub use_remote: bool,
75 pub endpoint: String,
76 pub namespace: String,
77 pub database: String,
78}
79
80impl SurrealDbRuntimeOptions {
81 pub fn from_args(
82 args: &[String],
83 settings: &SurrealDbSettings,
84 root_directory_name: Option<&str>,
85 ) -> Result<Self> {
86 let use_remote = args.iter().any(|arg| arg.eq_ignore_ascii_case("--remote"));
87 let root_name = root_directory_name
88 .unwrap_or(".locus")
89 .trim()
90 .to_string();
91
92 let home = env::var("HOME").map_err(|_| anyhow!("HOME is not set"))?;
93 let root_dir = PathBuf::from(home).join(if root_name.is_empty() {
94 ".locus"
95 } else {
96 root_name.as_str()
97 });
98
99 let endpoint = settings.endpoint(use_remote)?;
100 let normalized_endpoint = normalize_embedded_endpoint(&endpoint, &root_dir, use_remote)?;
101
102 Ok(Self {
103 root_dir: root_dir.to_string_lossy().to_string(),
104 use_remote,
105 endpoint: normalized_endpoint,
106 namespace: settings.namespace.clone(),
107 database: settings.database.clone(),
108 })
109 }
110}
111
112fn normalize_embedded_endpoint(
113 endpoint: &str,
114 root_dir: &Path,
115 use_remote: bool,
116) -> Result<String> {
117 if use_remote {
118 return Ok(endpoint.to_string());
119 }
120
121 const SCHEME: &str = "surrealkv://";
122 if !endpoint.starts_with(SCHEME) {
123 return Ok(endpoint.to_string());
124 }
125
126 let endpoint_path = endpoint.trim_start_matches(SCHEME);
127 let absolute_path = if Path::new(endpoint_path).is_absolute() {
128 PathBuf::from(endpoint_path)
129 } else {
130 root_dir.join(endpoint_path)
131 };
132
133 if let Some(parent) = absolute_path.parent() {
134 std::fs::create_dir_all(parent)?;
135 }
136
137 Ok(format!("{SCHEME}{}", absolute_path.to_string_lossy()))
138}