adaptive_card_core/knowledge/
mod.rs1pub mod format;
4pub mod loader;
5pub mod selector;
6
7pub use format::{Complexity, KnowledgeEntry, KnowledgeManifest, ManifestEntry};
8pub use selector::SuggestResult;
9
10use crate::error::{Error, Result};
11use std::path::Path;
12use std::sync::LazyLock;
13
14#[derive(Debug, Clone, Default)]
16pub struct KnowledgeBase {
17 entries: Vec<KnowledgeEntry>,
18}
19
20const EMBEDDED_ENTRIES_JSON: &[&str] = &[
27 include_str!("../../data/knowledge-base/examples/commerce/order-tracker.json"),
29 include_str!("../../data/knowledge-base/examples/communication/feedback-survey.json"),
30 include_str!("../../data/knowledge-base/examples/data-viz/kpi-dashboard.json"),
31 include_str!("../../data/knowledge-base/examples/data-viz/weather-forecast.json"),
32 include_str!("../../data/knowledge-base/examples/finance/expense-report.json"),
33 include_str!("../../data/knowledge-base/examples/finance/invoice-processing.json"),
34 include_str!("../../data/knowledge-base/examples/helpdesk/it-helpdesk.json"),
35 include_str!("../../data/knowledge-base/examples/hr/employee-onboarding.json"),
36 include_str!("../../data/knowledge-base/examples/hr/leave-management.json"),
37 include_str!("../../data/knowledge-base/examples/hr/performance-review.json"),
38 include_str!("../../data/knowledge-base/examples/hr/recruitment.json"),
39 include_str!("../../data/knowledge-base/examples/hr/timesheet.json"),
40 include_str!("../../data/knowledge-base/examples/hr/training-lms.json"),
41 include_str!("../../data/knowledge-base/examples/it/asset-management.json"),
42 include_str!("../../data/knowledge-base/examples/productivity/meeting-scheduler.json"),
43 include_str!("../../data/knowledge-base/examples/reference/element-showcase.json"),
44 include_str!("../../data/knowledge-base/examples/travel/digital-travel-agent.json"),
45 include_str!("../../data/knowledge-base/examples/workflow/document-approval.json"),
46 include_str!("../../data/knowledge-base/examples/patterns/approval-split-layout.json"),
48 include_str!("../../data/knowledge-base/examples/patterns/calendar-ooo.json"),
49 include_str!("../../data/knowledge-base/examples/patterns/employee-onboarding.json"),
50 include_str!("../../data/knowledge-base/examples/patterns/events-timeline.json"),
51 include_str!("../../data/knowledge-base/examples/patterns/expense-report-approval.json"),
52 include_str!("../../data/knowledge-base/examples/patterns/kpi-counter.json"),
53 include_str!("../../data/knowledge-base/examples/patterns/meeting-room-booking.json"),
54 include_str!("../../data/knowledge-base/examples/patterns/payslip-viewer.json"),
55 include_str!("../../data/knowledge-base/examples/patterns/product-showcase.json"),
56 include_str!("../../data/knowledge-base/examples/patterns/pto-balance-request.json"),
57 include_str!("../../data/knowledge-base/examples/patterns/safety-alert.json"),
58 include_str!("../../data/knowledge-base/examples/patterns/social-news-feed.json"),
59 include_str!("../../data/knowledge-base/examples/patterns/stock-price-widget.json"),
60 include_str!("../../data/knowledge-base/examples/patterns/warehouse-inventory.json"),
61 include_str!("../../data/knowledge-base/examples/patterns/work-anniversary.json"),
62 ];
65
66impl KnowledgeBase {
67 #[must_use]
73 pub fn embedded() -> &'static KnowledgeBase {
74 static KB: LazyLock<KnowledgeBase> = LazyLock::new(|| {
75 let entries: Vec<KnowledgeEntry> = EMBEDDED_ENTRIES_JSON
76 .iter()
77 .filter_map(|json_str| serde_json::from_str(json_str).ok())
78 .collect();
79 KnowledgeBase { entries }
80 });
81 &KB
82 }
83
84 pub fn from_dir(dir: &Path) -> Result<Self> {
86 let entries = loader::from_dir(dir)?;
87 Ok(Self { entries })
88 }
89
90 #[must_use]
92 pub fn from_entries(entries: Vec<KnowledgeEntry>) -> Self {
93 Self { entries }
94 }
95
96 #[must_use]
97 pub fn all(&self) -> &[KnowledgeEntry] {
98 &self.entries
99 }
100
101 #[must_use]
102 pub fn by_id(&self, id: &str) -> Option<&KnowledgeEntry> {
103 self.entries.iter().find(|e| e.id == id)
104 }
105
106 #[must_use]
107 pub fn by_category(&self, category: &str) -> Vec<&KnowledgeEntry> {
108 self.entries
109 .iter()
110 .filter(|e| e.category == category)
111 .collect()
112 }
113
114 pub fn require(&self, id: &str) -> Result<&KnowledgeEntry> {
116 self.by_id(id)
117 .ok_or(Error::KnowledgeEntryNotFound { id: id.to_string() })
118 }
119
120 #[must_use]
121 pub fn suggest(&self, query: &str, limit: usize) -> Vec<SuggestResult> {
122 selector::suggest(&self.entries, query, limit)
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use crate::types::Host;
130 use serde_json::json;
131
132 fn make(id: &str, category: &str) -> KnowledgeEntry {
133 KnowledgeEntry {
134 id: id.to_string(),
135 title: id.to_string(),
136 description: String::new(),
137 category: category.to_string(),
138 tags: vec![],
139 use_cases: vec![],
140 host_targets: vec![Host::Teams],
141 complexity: Complexity::Basic,
142 card: json!({ "type": "AdaptiveCard" }),
143 notes: String::new(),
144 }
145 }
146
147 #[test]
148 fn embedded_loads_seeded_entries() {
149 let kb = KnowledgeBase::embedded();
150 assert!(
151 !kb.all().is_empty(),
152 "embedded KB should have seeded entries"
153 );
154 assert!(kb.by_id("travel/digital-travel-agent").is_some());
156 }
157
158 #[test]
159 fn by_id_returns_entry() {
160 let kb = KnowledgeBase::from_entries(vec![make("a", "finance"), make("b", "hr")]);
161 assert!(kb.by_id("a").is_some());
162 assert!(kb.by_id("c").is_none());
163 }
164
165 #[test]
166 fn by_category_filters() {
167 let kb = KnowledgeBase::from_entries(vec![make("a", "finance"), make("b", "hr")]);
168 assert_eq!(kb.by_category("finance").len(), 1);
169 }
170
171 #[test]
172 fn require_errors_on_missing() {
173 let kb = KnowledgeBase::default();
174 assert!(matches!(
175 kb.require("x"),
176 Err(Error::KnowledgeEntryNotFound { .. })
177 ));
178 }
179}