use serde::{Deserialize, Serialize};
use tatara_lisp::DeriveTataraDomain;
#[derive(DeriveTataraDomain, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
#[serde(rename_all = "camelCase")]
#[tatara(keyword = "defsnippet")]
pub struct SnippetSpec {
pub trigger: String,
#[serde(default)]
pub body: String,
#[serde(default)]
pub hash: String,
#[serde(default)]
pub filetype: String,
#[serde(default)]
pub description: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Resolution {
Inline,
Hashed,
Invalid,
}
impl SnippetSpec {
#[must_use]
pub fn resolution(&self) -> Resolution {
match (self.body.is_empty(), self.hash.is_empty()) {
(false, true) => Resolution::Inline,
(true, false) => Resolution::Hashed,
_ => Resolution::Invalid,
}
}
#[must_use]
pub fn has_valid_hash_format(&self) -> bool {
crate::hash::is_blake3_128_hex(&self.hash)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resolution_classifies_body_and_hash_modes() {
let inline = SnippetSpec {
trigger: "fn".into(),
body: "fn ${1}() { ${0} }".into(),
..Default::default()
};
assert_eq!(inline.resolution(), Resolution::Inline);
let hashed = SnippetSpec {
trigger: "deploy".into(),
hash: "af42c0d18e9b3f4aa18b7c3ef1de93a4".into(),
..Default::default()
};
assert_eq!(hashed.resolution(), Resolution::Hashed);
let neither = SnippetSpec {
trigger: "oops".into(),
..Default::default()
};
assert_eq!(neither.resolution(), Resolution::Invalid);
let both = SnippetSpec {
trigger: "both".into(),
body: "x".into(),
hash: "af42c0d18e9b3f4aa18b7c3ef1de93a4".into(),
..Default::default()
};
assert_eq!(both.resolution(), Resolution::Invalid);
}
#[test]
fn hash_format_validates_blake3_128_hex() {
let ok = SnippetSpec {
trigger: "x".into(),
hash: "af42c0d18e9b3f4aa18b7c3ef1de93a4".into(),
..Default::default()
};
assert!(ok.has_valid_hash_format());
let too_short = SnippetSpec {
trigger: "x".into(),
hash: "af42".into(),
..Default::default()
};
assert!(!too_short.has_valid_hash_format());
let has_uppercase = SnippetSpec {
trigger: "x".into(),
hash: "AF42c0d18e9b3f4aa18b7c3ef1de93a4".into(),
..Default::default()
};
assert!(!has_uppercase.has_valid_hash_format());
let has_garbage = SnippetSpec {
trigger: "x".into(),
hash: "af42c0d18e9b3f4aa18b7c3ef1de93_!".into(),
..Default::default()
};
assert!(!has_garbage.has_valid_hash_format());
let empty = SnippetSpec {
trigger: "x".into(),
..Default::default()
};
assert!(!empty.has_valid_hash_format());
}
}