use std::sync::Arc;
use lunaris_core::{LunarisError, StorageError, StoragePort};
pub(crate) const RETIRED_SCHEME_EXIT_RAMP: &str = "\
0.7.0 is Moon-only — use `moon://host:port`. \
Migrate your data with `lunaris-migrate` from the **v0.6.2 release binary** \
(it cannot be built from main; the crate was deleted with its backends), then \
re-open the store as `moon://`. See docs/migration/0.6-to-0.7.md for the \
procedure and the lossy-conversion contract, and \
docs/operations/external-moon.md to stand a Moon up.";
pub(crate) fn retired_scheme_error(scheme: &str) -> Option<StorageError> {
let backend = match scheme {
"postgres" | "postgresql" => "the Postgres backend (`lunaris-storage-postgres`)",
"memory" | "sqlite" => "the embedded SQLite backend (`lunaris-storage-embedded`)",
_ => return None,
};
Some(StorageError::UnsupportedScheme(format!(
"`{scheme}://` was removed in 0.7.0 together with {backend}. {RETIRED_SCHEME_EXIT_RAMP}"
)))
}
pub async fn open(url: &str) -> Result<Arc<dyn StoragePort>, LunarisError> {
let parsed = url::Url::parse(url).map_err(|e| {
LunarisError::Storage(StorageError::UnsupportedScheme(format!("parse: {e}")))
})?;
match parsed.scheme() {
"moon" => {
let s = lunaris_storage_moon::MoonStorage::connect(url).await?;
Ok(Arc::new(s) as Arc<dyn StoragePort>)
}
other => Err(LunarisError::Storage(
retired_scheme_error(other)
.unwrap_or_else(|| StorageError::UnsupportedScheme(other.into())),
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn retired_schemes_name_the_exit_ramp() {
for url in ["memory://", "sqlite:///tmp/lunaris.db", "postgres://u@h/db"] {
let Err(err) = open(url).await else {
panic!("{url}: retired scheme must not open");
};
let LunarisError::Storage(StorageError::UnsupportedScheme(msg)) = err else {
panic!("{url}: expected UnsupportedScheme, got {err:?}");
};
assert!(msg.contains("removed in 0.7.0"), "{url}: {msg}");
assert!(msg.contains("lunaris-migrate"), "{url}: {msg}");
assert!(msg.contains("v0.6.2"), "{url}: {msg}");
assert!(msg.contains("docs/migration/0.6-to-0.7.md"), "{url}: {msg}");
assert!(msg.contains("moon://"), "{url}: {msg}");
}
}
#[tokio::test]
async fn unknown_scheme_is_rejected() {
assert!(matches!(
open("mysql://localhost/db").await,
Err(LunarisError::Storage(StorageError::UnsupportedScheme(_)))
));
}
#[test]
fn foreign_scheme_gets_no_migration_prose() {
assert!(retired_scheme_error("mysql").is_none());
}
}