use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use crate::identifier::Identifier;
use crate::ir::difference::Difference;
use crate::ir::eq::{Equiv, field_difference};
#[must_use]
pub(crate) fn normalize_location(s: &str) -> String {
let trimmed = s.trim_end_matches('/');
if trimmed.is_empty() {
if s.is_empty() {
String::new()
} else {
"/".to_string()
}
} else {
trimmed.to_string()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Tablespace {
pub name: Identifier,
pub location: String,
pub owner: Option<Identifier>,
pub options: BTreeMap<String, String>,
pub comment: Option<String>,
}
impl Equiv for Tablespace {
fn differences(&self, other: &Self) -> Vec<Difference> {
let Self {
name: _,
location: _,
owner: _,
options: _,
comment: _,
} = self;
let mut out = Vec::new();
out.extend(field_difference("name", &self.name, &other.name));
out.extend(field_difference(
"location",
&format!("{:?}", self.location),
&format!("{:?}", other.location),
));
out.extend(field_difference(
"owner",
&format!("{:?}", self.owner),
&format!("{:?}", other.owner),
));
out.extend(field_difference(
"options",
&format!("{:?}", self.options),
&format!("{:?}", other.options),
));
out.extend(field_difference(
"comment",
&format!("{:?}", self.comment),
&format!("{:?}", other.comment),
));
out
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir::eq::Equiv;
#[test]
fn normalize_location_strips_single_trailing_slash() {
assert_eq!(normalize_location("/data/ts/"), "/data/ts");
}
#[test]
fn normalize_location_strips_multiple_trailing_slashes() {
assert_eq!(normalize_location("/data/ts///"), "/data/ts");
}
#[test]
fn normalize_location_no_trailing_slash_unchanged() {
assert_eq!(normalize_location("/data/ts"), "/data/ts");
}
#[test]
fn normalize_location_root_preserved() {
assert_eq!(normalize_location("/"), "/");
}
#[test]
fn normalize_location_empty_preserved() {
assert_eq!(normalize_location(""), "");
}
fn id(s: &str) -> Identifier {
Identifier::from_unquoted(s).unwrap()
}
fn base() -> Tablespace {
Tablespace {
name: id("fast_ssd"),
location: "/mnt/ssd".to_string(),
owner: None,
options: BTreeMap::new(),
comment: None,
}
}
#[test]
fn equal_tablespaces_have_no_diff() {
assert!(base().canonical_eq(&base()));
}
#[test]
fn owner_change_diffs() {
let mut b = base();
b.owner = Some(id("dba"));
assert!(base().differences(&b).iter().any(|x| x.path == "owner"));
}
#[test]
fn options_change_diffs() {
let mut b = base();
b.options
.insert("seq_page_cost".to_string(), "1.5".to_string());
assert!(base().differences(&b).iter().any(|x| x.path == "options"));
}
#[test]
fn location_change_diffs() {
let mut b = base();
b.location = "/mnt/nvme".to_string();
assert!(base().differences(&b).iter().any(|x| x.path == "location"));
}
#[test]
fn comment_change_diffs() {
let mut b = base();
b.comment = Some("fast storage".into());
assert!(base().differences(&b).iter().any(|x| x.path == "comment"));
}
}