1use crate::errors::ConvertError;
4use crate::types::{CardEntry, CardKind, HttpConfig};
5use serde::Deserialize;
6
7#[derive(Debug, Deserialize)]
8struct RawEntry {
9 id: String,
10 json: serde_json::Value,
11}
12
13pub fn parse_cards(contents_json: &str) -> Result<Vec<CardEntry>, ConvertError> {
14 let raw: Vec<RawEntry> =
15 serde_json::from_str(contents_json).map_err(|e| ConvertError::Parse(e.to_string()))?;
16
17 let mut out = Vec::with_capacity(raw.len());
18 for entry in raw {
19 let kind = classify(&entry)?;
20 out.push(CardEntry { id: entry.id, kind });
21 }
22 Ok(out)
23}
24
25fn classify(entry: &RawEntry) -> Result<CardKind, ConvertError> {
26 let ty = entry
27 .json
28 .get("type")
29 .and_then(|v| v.as_str())
30 .unwrap_or("");
31 if ty == "http" {
32 let config_value = entry
33 .json
34 .get("config")
35 .cloned()
36 .unwrap_or(serde_json::json!({}));
37 let cfg: HttpConfig =
38 serde_json::from_value(config_value).map_err(|e| ConvertError::InvalidHttp {
39 id: entry.id.clone(),
40 msg: e.to_string(),
41 })?;
42 Ok(CardKind::Http(cfg))
43 } else {
44 Ok(CardKind::AdaptiveCard(entry.json.clone()))
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn parses_mixed_card_and_http() {
54 let raw = r#"[
55 {"id":"welcome","json":{"type":"AdaptiveCard","version":"1.5"}},
56 {"id":"api_x","json":{"type":"http","config":{"url":"http://x","method":"GET","next_entry_id":"after"}}}
57 ]"#;
58 let cards = parse_cards(raw).unwrap();
59 assert_eq!(cards.len(), 2);
60 assert_eq!(cards[0].id, "welcome");
61 assert!(matches!(cards[0].kind, CardKind::AdaptiveCard(_)));
62 assert!(matches!(cards[1].kind, CardKind::Http(_)));
63 }
64
65 #[test]
66 fn rejects_invalid_http_config() {
67 let raw = r#"[{"id":"bad","json":{"type":"http","config":{"url":42}}}]"#;
68 let err = parse_cards(raw).unwrap_err();
69 assert_eq!(err.code(), "E_INVALID_HTTP");
70 }
71
72 #[test]
73 fn rejects_invalid_json() {
74 let err = parse_cards("not json").unwrap_err();
75 assert_eq!(err.code(), "E_PARSE");
76 }
77}