use std::borrow::Borrow;
use std::fmt;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use crate::core::validation;
use crate::error::{Result, ValidationError};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Key(String);
impl Key {
pub fn new(input: impl Into<String>) -> Result<Self> {
let input = input.into();
validation::validate_key(&input)?;
Ok(Self(input))
}
#[allow(dead_code)]
pub(crate) fn new_unchecked(input: String) -> Self {
Self(input)
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn namespace(&self) -> Option<Namespace> {
self.0
.rsplit_once('/')
.and_then(|(prefix, _)| Namespace::new(prefix).ok())
}
#[must_use]
pub fn leaf(&self) -> &str {
self.0.rsplit('/').next().unwrap_or(self.0.as_str())
}
#[must_use]
pub fn starts_with_namespace(&self, namespace: &Namespace) -> bool {
if self.0 == namespace.as_str() {
return true;
}
self.0
.strip_prefix(namespace.as_str())
.is_some_and(|suffix| suffix.starts_with('/'))
}
#[must_use]
pub fn into_inner(self) -> String {
self.0
}
}
impl AsRef<str> for Key {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Borrow<str> for Key {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl Deref for Key {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl fmt::Display for Key {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl TryFrom<String> for Key {
type Error = crate::error::AgentMemoryError;
fn try_from(value: String) -> Result<Self> {
Self::new(value)
}
}
impl TryFrom<&str> for Key {
type Error = crate::error::AgentMemoryError;
fn try_from(value: &str) -> Result<Self> {
Self::new(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Namespace(String);
impl Namespace {
pub fn new(input: impl Into<String>) -> Result<Self> {
let input = input.into();
validation::validate_namespace(&input)?;
Ok(Self(input))
}
pub(crate) fn new_unchecked(input: String) -> Self {
Self(input)
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
pub fn join(&self, leaf: &str) -> Result<Key> {
validation::validate_key_leaf(leaf)?;
Key::new(format!("{}/{}", self.0, leaf))
}
#[must_use]
pub fn parent(&self) -> Option<Self> {
self.0
.rsplit_once('/')
.map(|(parent, _)| Self::new_unchecked(parent.to_owned()))
}
#[must_use]
pub fn leaf(&self) -> &str {
self.0.rsplit('/').next().unwrap_or(self.0.as_str())
}
#[must_use]
pub fn into_inner(self) -> String {
self.0
}
}
impl AsRef<str> for Namespace {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Borrow<str> for Namespace {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl Deref for Namespace {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl fmt::Display for Namespace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl TryFrom<String> for Namespace {
type Error = crate::error::AgentMemoryError;
fn try_from(value: String) -> Result<Self> {
Self::new(value)
}
}
impl TryFrom<&str> for Namespace {
type Error = crate::error::AgentMemoryError;
fn try_from(value: &str) -> Result<Self> {
Self::new(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Value(String);
impl Value {
pub fn new(input: impl Into<String>) -> Result<Self> {
let input = input.into();
validation::validate_value(&input)?;
Ok(Self(input))
}
#[allow(dead_code)]
pub(crate) fn new_unchecked(input: String) -> Self {
Self(input)
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn into_inner(self) -> String {
self.0
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
}
impl AsRef<str> for Value {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Borrow<str> for Value {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl Deref for Value {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl TryFrom<String> for Value {
type Error = crate::error::AgentMemoryError;
fn try_from(value: String) -> Result<Self> {
Self::new(value)
}
}
impl TryFrom<&str> for Value {
type Error = crate::error::AgentMemoryError;
fn try_from(value: &str) -> Result<Self> {
Self::new(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ProjectName(String);
impl ProjectName {
pub fn new(input: impl Into<String>) -> Result<Self> {
let input = input.into();
validation::validate_project_name(&input)?;
Ok(Self(input))
}
#[allow(dead_code)]
pub(crate) fn new_unchecked(input: String) -> Self {
Self(input)
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
pub fn as_project_namespace(&self) -> Namespace {
Namespace::new_unchecked(format!("project/{}", self.0))
}
#[must_use]
pub fn into_inner(self) -> String {
self.0
}
}
impl AsRef<str> for ProjectName {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Borrow<str> for ProjectName {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl Deref for ProjectName {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl fmt::Display for ProjectName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl TryFrom<String> for ProjectName {
type Error = crate::error::AgentMemoryError;
fn try_from(value: String) -> Result<Self> {
Self::new(value)
}
}
impl TryFrom<&str> for ProjectName {
type Error = crate::error::AgentMemoryError;
fn try_from(value: &str) -> Result<Self> {
Self::new(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct StorePath(PathBuf);
impl StorePath {
pub fn new(path: impl Into<PathBuf>) -> Result<Self> {
let path = path.into();
validation::validate_store_path(&path)?;
Ok(Self(path))
}
#[allow(dead_code)]
pub(crate) fn new_unchecked(path: PathBuf) -> Self {
Self(path)
}
#[must_use]
pub fn as_path(&self) -> &Path {
&self.0
}
#[must_use]
pub fn parent(&self) -> Option<&Path> {
self.0.parent()
}
#[must_use]
pub fn file_name(&self) -> Option<&std::ffi::OsStr> {
self.0.file_name()
}
#[must_use]
pub fn into_inner(self) -> PathBuf {
self.0
}
}
impl AsRef<Path> for StorePath {
fn as_ref(&self) -> &Path {
self.as_path()
}
}
impl Borrow<Path> for StorePath {
fn borrow(&self) -> &Path {
self.as_path()
}
}
impl Deref for StorePath {
type Target = Path;
fn deref(&self) -> &Self::Target {
self.as_path()
}
}
impl fmt::Display for StorePath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write_path_for_display(&self.0, f)
}
}
impl TryFrom<PathBuf> for StorePath {
type Error = crate::error::AgentMemoryError;
fn try_from(value: PathBuf) -> Result<Self> {
Self::new(value)
}
}
impl TryFrom<&Path> for StorePath {
type Error = crate::error::AgentMemoryError;
fn try_from(value: &Path) -> Result<Self> {
Self::new(value.to_path_buf())
}
}
impl TryFrom<&str> for StorePath {
type Error = crate::error::AgentMemoryError;
fn try_from(value: &str) -> Result<Self> {
Self::new(PathBuf::from(value))
}
}
fn write_path_for_display(path: &Path, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", path.display())
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Entry {
pub key: Key,
pub value: Value,
}
impl Entry {
#[must_use]
pub const fn new(key: Key, value: Value) -> Self {
Self { key, value }
}
pub fn try_new(key: impl Into<String>, value: impl Into<String>) -> Result<Self> {
Ok(Self {
key: Key::new(key.into())?,
value: Value::new(value.into())?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SetRequest {
pub key: Key,
pub value: Value,
}
impl SetRequest {
#[must_use]
pub const fn new(key: Key, value: Value) -> Self {
Self { key, value }
}
pub fn try_new(key: impl Into<String>, value: impl Into<String>) -> Result<Self> {
Ok(Self {
key: Key::new(key.into())?,
value: Value::new(value.into())?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct KeyPrefix(String);
impl KeyPrefix {
pub fn new(input: impl Into<String>) -> Result<Self> {
let input = input.into();
validation::validate_namespace(&input)?;
Ok(Self(input))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn matches(&self, key: &Key) -> bool {
if key.as_str() == self.0 {
return true;
}
key.as_str()
.strip_prefix(self.0.as_str())
.is_some_and(|suffix| suffix.starts_with('/'))
}
}
impl fmt::Display for KeyPrefix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl TryFrom<&str> for KeyPrefix {
type Error = crate::error::AgentMemoryError;
fn try_from(value: &str) -> Result<Self> {
Self::new(value)
}
}
impl TryFrom<String> for KeyPrefix {
type Error = crate::error::AgentMemoryError;
fn try_from(value: String) -> Result<Self> {
Self::new(value)
}
}
fn _ensure_non_empty(field: &'static str, value: &str) -> std::result::Result<(), ValidationError> {
if value.is_empty() {
return Err(ValidationError::empty(field));
}
Ok(())
}