use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
fn normalized_why_tag_token(input: &str) -> String {
let trimmed = input.trim();
let mut normalized = String::with_capacity(trimmed.len());
let mut previous_was_lowercase = false;
let mut previous_was_separator = false;
for character in trimmed.chars() {
match character {
'-' | '_' => {
if !normalized.is_empty() && !previous_was_separator {
normalized.push('_');
}
previous_was_lowercase = false;
previous_was_separator = true;
}
character if character.is_ascii_uppercase() => {
if previous_was_lowercase && !previous_was_separator {
normalized.push('_');
}
normalized.push(character.to_ascii_lowercase());
previous_was_lowercase = false;
previous_was_separator = false;
}
character => {
normalized.push(character.to_ascii_lowercase());
previous_was_lowercase = character.is_ascii_lowercase();
previous_was_separator = false;
}
}
}
normalized
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WhyTag {
WorkspaceScope,
TagMatch,
ProvenRule,
Fresh,
HighConfidence,
HighUtility,
GraphCentral,
Pinned,
SemanticMatch,
LexicalMatch,
LinkedTo,
SupportsEvidence,
RecentDecision,
ProceduralGuidance,
TrustedSource,
SectionFill,
DiversityPick,
FallbackSource,
}
impl WhyTag {
pub const ALL: &'static [WhyTag] = &[
WhyTag::WorkspaceScope,
WhyTag::TagMatch,
WhyTag::ProvenRule,
WhyTag::Fresh,
WhyTag::HighConfidence,
WhyTag::HighUtility,
WhyTag::GraphCentral,
WhyTag::Pinned,
WhyTag::SemanticMatch,
WhyTag::LexicalMatch,
WhyTag::LinkedTo,
WhyTag::SupportsEvidence,
WhyTag::RecentDecision,
WhyTag::ProceduralGuidance,
WhyTag::TrustedSource,
WhyTag::SectionFill,
WhyTag::DiversityPick,
WhyTag::FallbackSource,
];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
WhyTag::WorkspaceScope => "workspace_scope",
WhyTag::TagMatch => "tag_match",
WhyTag::ProvenRule => "proven_rule",
WhyTag::Fresh => "fresh",
WhyTag::HighConfidence => "high_confidence",
WhyTag::HighUtility => "high_utility",
WhyTag::GraphCentral => "graph_central",
WhyTag::Pinned => "pinned",
WhyTag::SemanticMatch => "semantic_match",
WhyTag::LexicalMatch => "lexical_match",
WhyTag::LinkedTo => "linked_to",
WhyTag::SupportsEvidence => "supports_evidence",
WhyTag::RecentDecision => "recent_decision",
WhyTag::ProceduralGuidance => "procedural_guidance",
WhyTag::TrustedSource => "trusted_source",
WhyTag::SectionFill => "section_fill",
WhyTag::DiversityPick => "diversity_pick",
WhyTag::FallbackSource => "fallback_source",
}
}
#[must_use]
pub const fn description(self) -> &'static str {
match self {
WhyTag::WorkspaceScope => "in the same workspace scope as the query",
WhyTag::TagMatch => "matched one or more query tags",
WhyTag::ProvenRule => "a procedural rule proven effective",
WhyTag::Fresh => "recently created or updated",
WhyTag::HighConfidence => "high confidence from multiple sources",
WhyTag::HighUtility => "demonstrated utility in past packs",
WhyTag::GraphCentral => "central in the memory graph",
WhyTag::Pinned => "explicitly pinned by user or rule",
WhyTag::SemanticMatch => "semantically similar to the query",
WhyTag::LexicalMatch => "matched query terms lexically",
WhyTag::LinkedTo => "linked to another selected result",
WhyTag::SupportsEvidence => "supports a rule or claim with evidence",
WhyTag::RecentDecision => "a recent decision relevant to context",
WhyTag::ProceduralGuidance => "contains procedural guidance",
WhyTag::TrustedSource => "from a trusted source",
WhyTag::SectionFill => "fills a required context section",
WhyTag::DiversityPick => "selected for diversity",
WhyTag::FallbackSource => "fallback when primary sources unavailable",
}
}
}
impl fmt::Display for WhyTag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParseWhyTagError {
pub input: String,
}
impl fmt::Display for ParseWhyTagError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unknown why tag: {}", self.input)
}
}
impl std::error::Error for ParseWhyTagError {}
impl FromStr for WhyTag {
type Err = ParseWhyTagError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match normalized_why_tag_token(s).as_str() {
"workspace_scope" => Ok(WhyTag::WorkspaceScope),
"tag_match" => Ok(WhyTag::TagMatch),
"proven_rule" => Ok(WhyTag::ProvenRule),
"fresh" => Ok(WhyTag::Fresh),
"high_confidence" => Ok(WhyTag::HighConfidence),
"high_utility" => Ok(WhyTag::HighUtility),
"graph_central" => Ok(WhyTag::GraphCentral),
"pinned" => Ok(WhyTag::Pinned),
"semantic_match" => Ok(WhyTag::SemanticMatch),
"lexical_match" => Ok(WhyTag::LexicalMatch),
"linked_to" => Ok(WhyTag::LinkedTo),
"supports_evidence" => Ok(WhyTag::SupportsEvidence),
"recent_decision" => Ok(WhyTag::RecentDecision),
"procedural_guidance" => Ok(WhyTag::ProceduralGuidance),
"trusted_source" => Ok(WhyTag::TrustedSource),
"section_fill" => Ok(WhyTag::SectionFill),
"diversity_pick" => Ok(WhyTag::DiversityPick),
"fallback_source" => Ok(WhyTag::FallbackSource),
_ => Err(ParseWhyTagError {
input: s.to_owned(),
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_tags_round_trip_through_from_str() -> Result<(), String> {
for tag in WhyTag::ALL {
let s = tag.as_str();
let parsed: WhyTag = s.parse().map_err(|error: ParseWhyTagError| {
format!("why tag should parse from {s}: {error}")
})?;
assert_eq!(parsed, *tag);
}
Ok(())
}
#[test]
fn tags_parse_operator_spelling_variants() -> Result<(), String> {
let variants = [
(" Workspace-Scope ", WhyTag::WorkspaceScope),
("highConfidence", WhyTag::HighConfidence),
("SemanticMatch", WhyTag::SemanticMatch),
("supportsEvidence", WhyTag::SupportsEvidence),
("ProceduralGuidance", WhyTag::ProceduralGuidance),
("fallbackSource", WhyTag::FallbackSource),
];
for (raw, expected) in variants {
let parsed: WhyTag = raw.parse().map_err(|error: ParseWhyTagError| {
format!("why tag should parse from {raw:?}: {error}")
})?;
assert_eq!(parsed, expected);
}
Ok(())
}
#[test]
fn all_tags_round_trip_through_serde() -> Result<(), String> {
for tag in WhyTag::ALL {
let json = serde_json::to_string(tag)
.map_err(|error| format!("why tag should serialize: {error}"))?;
let parsed: WhyTag = serde_json::from_str(&json)
.map_err(|error| format!("why tag should deserialize from {json}: {error}"))?;
assert_eq!(parsed, *tag);
}
Ok(())
}
#[test]
fn display_matches_as_str() {
for tag in WhyTag::ALL {
assert_eq!(tag.to_string(), tag.as_str());
}
}
#[test]
fn unknown_tag_returns_error() -> Result<(), String> {
match "unknown_tag".parse::<WhyTag>() {
Ok(tag) => Err(format!("unknown_tag unexpectedly parsed as {tag:?}")),
Err(err) => {
assert_eq!(err.input, "unknown_tag");
Ok(())
}
}
}
#[test]
fn all_array_is_complete() {
assert_eq!(WhyTag::ALL.len(), 18);
}
#[test]
fn descriptions_are_non_empty() {
for tag in WhyTag::ALL {
assert!(!tag.description().is_empty());
}
}
}