1use crate::{BindingCoreError, BindingResult};
8use jacs::simple::SimpleAgent;
9use std::sync::Arc;
10
11#[derive(Clone)]
16pub struct SimpleAgentWrapper {
17 inner: Arc<SimpleAgent>,
18}
19
20const _: () = {
22 fn _assert<T: Send + Sync>() {}
23 let _ = _assert::<SimpleAgentWrapper>;
24};
25
26impl SimpleAgentWrapper {
27 pub fn create(
41 name: &str,
42 purpose: Option<&str>,
43 key_algorithm: Option<&str>,
44 ) -> BindingResult<(Self, String)> {
45 let (agent, info) = SimpleAgent::create(name, purpose, key_algorithm)
46 .map_err(|e| BindingCoreError::agent_load(format!("Failed to create agent: {}", e)))?;
47 let info_json = crate::serialize_agent_info(&info)?;
48
49 Ok((
50 Self {
51 inner: Arc::new(agent),
52 },
53 info_json,
54 ))
55 }
56
57 pub fn load(config_path: Option<&str>, strict: Option<bool>) -> BindingResult<Self> {
59 let (wrapper, _info_json) = Self::load_with_info(config_path, strict)?;
60 Ok(wrapper)
61 }
62
63 pub fn load_with_info(
65 config_path: Option<&str>,
66 strict: Option<bool>,
67 ) -> BindingResult<(Self, String)> {
68 let requested_path = config_path.unwrap_or("./jacs.config.json");
69 let resolved_config_path = crate::resolve_existing_config_path(requested_path)?;
70 let agent = SimpleAgent::load(Some(&resolved_config_path), strict)
71 .map_err(|e| BindingCoreError::agent_load(format!("Failed to load agent: {}", e)))?;
72 let info = agent
73 .loaded_info()
74 .map_err(|e| BindingCoreError::agent_load(format!("Failed to load agent: {}", e)))?;
75 let info_json = crate::serialize_agent_info(&info)?;
76
77 Ok((
78 Self {
79 inner: Arc::new(agent),
80 },
81 info_json,
82 ))
83 }
84
85 pub fn ephemeral(algorithm: Option<&str>) -> BindingResult<(Self, String)> {
89 let (agent, info) = SimpleAgent::ephemeral(algorithm).map_err(|e| {
90 BindingCoreError::agent_load(format!("Failed to create ephemeral agent: {}", e))
91 })?;
92 let info_json = crate::serialize_agent_info(&info)?;
93
94 Ok((
95 Self {
96 inner: Arc::new(agent),
97 },
98 info_json,
99 ))
100 }
101
102 pub fn create_with_params(params_json: &str) -> BindingResult<(Self, String)> {
109 let params: jacs::simple::CreateAgentParams =
110 serde_json::from_str(params_json).map_err(|e| {
111 BindingCoreError::invalid_argument(format!("Invalid CreateAgentParams JSON: {}", e))
112 })?;
113
114 let (agent, info) = SimpleAgent::create_with_params(params).map_err(|e| {
115 BindingCoreError::agent_load(format!("Failed to create agent with params: {}", e))
116 })?;
117 let info_json = crate::serialize_agent_info(&info)?;
118
119 Ok((
120 Self {
121 inner: Arc::new(agent),
122 },
123 info_json,
124 ))
125 }
126
127 pub fn from_agent(agent: SimpleAgent) -> Self {
129 Self {
130 inner: Arc::new(agent),
131 }
132 }
133
134 pub fn inner_ref(&self) -> &SimpleAgent {
140 &self.inner
141 }
142
143 pub fn get_agent_id(&self) -> BindingResult<String> {
149 self.inner
150 .get_agent_id()
151 .map_err(|e| BindingCoreError::generic(format!("Failed to get agent ID: {}", e)))
152 }
153
154 pub fn key_id(&self) -> BindingResult<String> {
156 self.inner
157 .key_id()
158 .map_err(|e| BindingCoreError::generic(format!("Failed to get key ID: {}", e)))
159 }
160
161 pub fn is_strict(&self) -> bool {
163 self.inner.is_strict()
164 }
165
166 pub fn config_path(&self) -> Option<String> {
168 self.inner.config_path().map(|s| s.to_string())
169 }
170
171 pub fn export_agent(&self) -> BindingResult<String> {
173 self.inner
174 .export_agent()
175 .map_err(|e| BindingCoreError::generic(format!("Failed to export agent: {}", e)))
176 }
177
178 pub fn get_public_key_pem(&self) -> BindingResult<String> {
180 self.inner.get_public_key_pem().map_err(|e| {
181 BindingCoreError::key_not_found(format!("Failed to get public key PEM: {}", e))
182 })
183 }
184
185 pub fn get_public_key_base64(&self) -> BindingResult<String> {
187 use base64::Engine;
188 let bytes = self.inner.get_public_key().map_err(|e| {
189 BindingCoreError::key_not_found(format!("Failed to get public key: {}", e))
190 })?;
191 Ok(base64::engine::general_purpose::STANDARD.encode(&bytes))
192 }
193
194 pub fn diagnostics(&self) -> String {
196 self.inner.diagnostics().to_string()
197 }
198
199 pub fn verify_self(&self) -> BindingResult<String> {
205 let result = self.inner.verify_self().map_err(|e| {
206 BindingCoreError::verification_failed(format!("Verify self failed: {}", e))
207 })?;
208 serde_json::to_string(&result).map_err(|e| {
209 BindingCoreError::serialization_failed(format!(
210 "Failed to serialize VerificationResult: {}",
211 e
212 ))
213 })
214 }
215
216 pub fn verify_json(&self, signed_document: &str) -> BindingResult<String> {
218 let result = self.inner.verify(signed_document).map_err(|e| {
219 BindingCoreError::verification_failed(format!("Verification failed: {}", e))
220 })?;
221 serde_json::to_string(&result).map_err(|e| {
222 BindingCoreError::serialization_failed(format!(
223 "Failed to serialize VerificationResult: {}",
224 e
225 ))
226 })
227 }
228
229 pub fn verify_with_key_json(
232 &self,
233 signed_document: &str,
234 public_key_base64: &str,
235 ) -> BindingResult<String> {
236 use base64::Engine;
237 let key_bytes = base64::engine::general_purpose::STANDARD
238 .decode(public_key_base64)
239 .map_err(|e| {
240 BindingCoreError::invalid_argument(format!("Invalid base64 public key: {}", e))
241 })?;
242
243 let result = self
244 .inner
245 .verify_with_key(signed_document, key_bytes)
246 .map_err(|e| {
247 BindingCoreError::verification_failed(format!(
248 "Verification with key failed: {}",
249 e
250 ))
251 })?;
252 serde_json::to_string(&result).map_err(|e| {
253 BindingCoreError::serialization_failed(format!(
254 "Failed to serialize VerificationResult: {}",
255 e
256 ))
257 })
258 }
259
260 pub fn verify_by_id_json(&self, document_id: &str) -> BindingResult<String> {
263 let result = self.inner.verify_by_id(document_id).map_err(|e| {
264 BindingCoreError::verification_failed(format!("Verify by ID failed: {}", e))
265 })?;
266 serde_json::to_string(&result).map_err(|e| {
267 BindingCoreError::serialization_failed(format!(
268 "Failed to serialize VerificationResult: {}",
269 e
270 ))
271 })
272 }
273
274 pub fn sign_message_json(&self, data_json: &str) -> BindingResult<String> {
280 let value: serde_json::Value = serde_json::from_str(data_json).map_err(|e| {
281 BindingCoreError::invalid_argument(format!("Invalid JSON input: {}", e))
282 })?;
283
284 let signed = self
285 .inner
286 .sign_message(&value)
287 .map_err(|e| BindingCoreError::signing_failed(format!("Sign message failed: {}", e)))?;
288
289 Ok(signed.raw)
290 }
291
292 pub fn sign_raw_bytes_base64(&self, data: &[u8]) -> BindingResult<String> {
294 use base64::Engine;
295 let sig_bytes = self.inner.sign_raw_bytes(data).map_err(|e| {
296 BindingCoreError::signing_failed(format!("Sign raw bytes failed: {}", e))
297 })?;
298 Ok(base64::engine::general_purpose::STANDARD.encode(&sig_bytes))
299 }
300
301 pub fn sign_file_json(&self, file_path: &str, embed: bool) -> BindingResult<String> {
304 let signed = self
305 .inner
306 .sign_file(file_path, embed)
307 .map_err(|e| BindingCoreError::signing_failed(format!("Sign file failed: {}", e)))?;
308 Ok(signed.raw)
309 }
310
311 pub fn to_yaml(&self, json_str: &str) -> BindingResult<String> {
317 jacs::convert::jacs_to_yaml(json_str).map_err(|e| {
318 BindingCoreError::new(
319 crate::ErrorKind::SerializationFailed,
320 format!("to_yaml failed: {}", e),
321 )
322 })
323 }
324
325 pub fn from_yaml(&self, yaml_str: &str) -> BindingResult<String> {
327 jacs::convert::yaml_to_jacs(yaml_str).map_err(|e| {
328 BindingCoreError::new(
329 crate::ErrorKind::SerializationFailed,
330 format!("from_yaml failed: {}", e),
331 )
332 })
333 }
334
335 pub fn to_html(&self, json_str: &str) -> BindingResult<String> {
337 jacs::convert::jacs_to_html(json_str).map_err(|e| {
338 BindingCoreError::new(
339 crate::ErrorKind::SerializationFailed,
340 format!("to_html failed: {}", e),
341 )
342 })
343 }
344
345 pub fn from_html(&self, html_str: &str) -> BindingResult<String> {
347 jacs::convert::html_to_jacs(html_str).map_err(|e| {
348 BindingCoreError::new(
349 crate::ErrorKind::SerializationFailed,
350 format!("from_html failed: {}", e),
351 )
352 })
353 }
354}
355
356pub fn sign_message_json(wrapper: &SimpleAgentWrapper, data_json: &str) -> BindingResult<String> {
362 wrapper.sign_message_json(data_json)
363}
364
365pub fn verify_json(wrapper: &SimpleAgentWrapper, signed_document: &str) -> BindingResult<String> {
367 wrapper.verify_json(signed_document)
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373
374 fn test_wrapper() -> SimpleAgentWrapper {
377 let (wrapper, _info) =
378 SimpleAgentWrapper::ephemeral(Some("ed25519")).expect("ephemeral agent");
379 wrapper
380 }
381
382 #[test]
383 fn to_yaml_valid_json_succeeds() {
384 let wrapper = test_wrapper();
385 let result = wrapper.to_yaml(r#"{"key": "value"}"#);
386 assert!(result.is_ok(), "to_yaml should succeed for valid JSON");
387 let yaml = result.unwrap();
388 assert!(yaml.contains("key"), "YAML should contain 'key'");
389 assert!(yaml.contains("value"), "YAML should contain 'value'");
390 }
391
392 #[test]
393 fn from_yaml_valid_yaml_succeeds() {
394 let wrapper = test_wrapper();
395 let result = wrapper.from_yaml("key: value\n");
396 assert!(result.is_ok(), "from_yaml should succeed for valid YAML");
397 let json = result.unwrap();
398 assert!(json.contains("\"key\""), "JSON should contain key");
399 assert!(json.contains("\"value\""), "JSON should contain value");
400 }
401
402 #[test]
403 fn to_html_valid_json_succeeds() {
404 let wrapper = test_wrapper();
405 let result = wrapper.to_html(r#"{"key": "value"}"#);
406 assert!(result.is_ok(), "to_html should succeed for valid JSON");
407 let html = result.unwrap();
408 assert!(html.contains("<!DOCTYPE html>"), "HTML should have DOCTYPE");
409 assert!(
410 html.contains(r#"id="jacs-data">"#),
411 "HTML should have jacs-data script tag"
412 );
413 }
414
415 #[test]
416 fn from_html_valid_html_succeeds() {
417 let wrapper = test_wrapper();
418 let json = r#"{"key": "value"}"#;
419 let html = wrapper.to_html(json).unwrap();
420 let result = wrapper.from_html(&html);
421 assert!(result.is_ok(), "from_html should succeed for valid HTML");
422 assert_eq!(result.unwrap(), json, "Extracted JSON should match input");
423 }
424
425 #[test]
426 fn yaml_round_trip_preserves_content() {
427 let wrapper = test_wrapper();
428 let json = r#"{"hello": "world", "count": 42}"#;
429 let yaml = wrapper.to_yaml(json).unwrap();
430 let back = wrapper.from_yaml(&yaml).unwrap();
431 let original: serde_json::Value = serde_json::from_str(json).unwrap();
432 let reconstituted: serde_json::Value = serde_json::from_str(&back).unwrap();
433 assert_eq!(
434 original, reconstituted,
435 "YAML round-trip should preserve content"
436 );
437 }
438
439 #[test]
440 fn html_round_trip_preserves_content() {
441 let wrapper = test_wrapper();
442 let json = r#"{"hello": "world", "count": 42}"#;
443 let html = wrapper.to_html(json).unwrap();
444 let back = wrapper.from_html(&html).unwrap();
445 assert_eq!(back, json, "HTML round-trip should preserve exact JSON");
446 }
447
448 #[test]
449 fn to_yaml_invalid_json_returns_serialization_failed() {
450 let wrapper = test_wrapper();
451 let result = wrapper.to_yaml("{not valid json}");
452 assert!(result.is_err(), "to_yaml should fail for invalid JSON");
453 let err = result.unwrap_err();
454 assert_eq!(
455 err.kind,
456 crate::ErrorKind::SerializationFailed,
457 "Error should be SerializationFailed, got: {:?}",
458 err.kind
459 );
460 }
461
462 #[test]
463 fn from_yaml_invalid_yaml_returns_serialization_failed() {
464 let wrapper = test_wrapper();
465 let result = wrapper.from_yaml("{{{{ not yaml ::::");
466 assert!(result.is_err(), "from_yaml should fail for invalid YAML");
467 let err = result.unwrap_err();
468 assert_eq!(
469 err.kind,
470 crate::ErrorKind::SerializationFailed,
471 "Error should be SerializationFailed, got: {:?}",
472 err.kind
473 );
474 }
475
476 #[test]
477 fn from_html_no_script_tag_returns_serialization_failed() {
478 let wrapper = test_wrapper();
479 let result = wrapper.from_html("<html><body>No jacs data here</body></html>");
480 assert!(
481 result.is_err(),
482 "from_html should fail without jacs-data tag"
483 );
484 let err = result.unwrap_err();
485 assert_eq!(
486 err.kind,
487 crate::ErrorKind::SerializationFailed,
488 "Error should be SerializationFailed, got: {:?}",
489 err.kind
490 );
491 }
492}