a2ui_gallery/
sample_loader.rs1use std::fs;
9use std::path::Path;
10
11use include_dir::{include_dir, Dir};
12
13use a2ui_base::message_processor::MessageProcessor;
14use a2ui_base::protocol::server_to_client::A2uiMessage;
15
16pub static SPEC_DIR: Dir<'static> =
22 include_dir!("$CARGO_MANIFEST_DIR/a2ui/specification");
23
24pub struct Sample {
26 pub name: String,
28 pub description: String,
30 pub file_path: String,
32 pub messages: Vec<A2uiMessage>,
34}
35
36pub fn load_samples_from_dir(dir: &str) -> Vec<Sample> {
41 let path = Path::new(dir);
42 if !path.is_dir() {
43 return Vec::new();
44 }
45
46 let dir_entries = match fs::read_dir(path) {
47 Ok(de) => de,
48 Err(e) => {
49 eprintln!("Warning: cannot read sample directory {:?}: {}", dir, e);
50 return Vec::new();
51 }
52 };
53
54 let mut entries: Vec<String> = dir_entries
55 .filter_map(|entry| {
56 let entry = entry.ok()?;
57 let file_name = entry.file_name().to_string_lossy().to_string();
58 if file_name.ends_with(".json") {
59 Some(file_name)
60 } else {
61 None
62 }
63 })
64 .collect();
65
66 entries.sort();
68
69 let mut samples = Vec::new();
70
71 for file_name in &entries {
72 let full_path = path.join(file_name);
73 let content = match fs::read_to_string(&full_path) {
74 Ok(c) => c,
75 Err(e) => {
76 eprintln!("Warning: cannot read {:?}: {}", full_path, e);
77 continue;
78 }
79 };
80
81 match MessageProcessor::load_sample(&content) {
82 Ok((name, description, messages)) => {
83 samples.push(Sample {
84 name,
85 description,
86 file_path: file_name.clone(),
87 messages,
88 });
89 }
90 Err(e) => {
91 eprintln!(
92 "Warning: failed to parse sample {:?}: {}",
93 full_path, e
94 );
95 }
96 }
97 }
98
99 samples
100}
101
102pub fn load_samples(subpath: &str) -> Vec<Sample> {
108 let dir = match SPEC_DIR.get_dir(subpath) {
109 Some(d) => d,
110 None => {
111 eprintln!("Warning: embedded sample directory not found: {subpath:?}");
112 return Vec::new();
113 }
114 };
115
116 let mut files: Vec<&include_dir::File> = dir.files().collect();
117 files.sort_by_key(|f| f.path().to_string_lossy().to_string());
118 files.retain(|f| f.path().extension().is_some_and(|ext| ext == "json"));
119
120 let mut samples = Vec::new();
121 for file in files {
122 let file_name = file
123 .path()
124 .file_name()
125 .map(|n| n.to_string_lossy().to_string())
126 .unwrap_or_default();
127 let content = match std::str::from_utf8(file.contents()) {
128 Ok(s) => s,
129 Err(e) => {
130 eprintln!("Warning: sample {file_name:?} is not valid UTF-8: {e}");
131 continue;
132 }
133 };
134 match MessageProcessor::load_sample(content) {
135 Ok((name, description, messages)) => samples.push(Sample {
136 name,
137 description,
138 file_path: file_name,
139 messages,
140 }),
141 Err(e) => {
142 eprintln!("Warning: failed to parse sample {file_name:?}: {e}");
143 }
144 }
145 }
146 samples
147}