1use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::str::FromStr;
6
7use super::fields::ParseEnumError;
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum CodeAction {
12 Create,
13 Append,
14 Prepend,
15 Replace,
16 InsertBefore,
17 InsertAfter,
18 Full,
19}
20
21impl fmt::Display for CodeAction {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 match self {
24 Self::Create => write!(f, "create"),
25 Self::Append => write!(f, "append"),
26 Self::Prepend => write!(f, "prepend"),
27 Self::Replace => write!(f, "replace"),
28 Self::InsertBefore => write!(f, "insert_before"),
29 Self::InsertAfter => write!(f, "insert_after"),
30 Self::Full => write!(f, "full"),
31 }
32 }
33}
34
35impl FromStr for CodeAction {
36 type Err = ParseEnumError;
37
38 fn from_str(s: &str) -> Result<Self, Self::Err> {
39 match s {
40 "create" => Ok(Self::Create),
41 "append" => Ok(Self::Append),
42 "prepend" => Ok(Self::Prepend),
43 "replace" => Ok(Self::Replace),
44 "insert_before" => Ok(Self::InsertBefore),
45 "insert_after" => Ok(Self::InsertAfter),
46 "full" => Ok(Self::Full),
47 _ => Err(ParseEnumError {
48 type_name: "CodeAction",
49 value: s.to_owned(),
50 }),
51 }
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
56pub struct CodeBlock {
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub lang: Option<String>,
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub target: Option<String>,
61 pub action: CodeAction,
62 pub body: String,
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub anchor: Option<String>,
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub old: Option<String>,
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn test_code_action_from_str_valid_returns_ok() {
75 assert_eq!("create".parse::<CodeAction>().unwrap(), CodeAction::Create);
76 assert_eq!("append".parse::<CodeAction>().unwrap(), CodeAction::Append);
77 assert_eq!(
78 "prepend".parse::<CodeAction>().unwrap(),
79 CodeAction::Prepend
80 );
81 assert_eq!(
82 "replace".parse::<CodeAction>().unwrap(),
83 CodeAction::Replace
84 );
85 assert_eq!(
86 "insert_before".parse::<CodeAction>().unwrap(),
87 CodeAction::InsertBefore
88 );
89 assert_eq!(
90 "insert_after".parse::<CodeAction>().unwrap(),
91 CodeAction::InsertAfter
92 );
93 assert_eq!("full".parse::<CodeAction>().unwrap(), CodeAction::Full);
94 }
95
96 #[test]
97 fn test_code_action_from_str_invalid_returns_error() {
98 let err = "overwrite".parse::<CodeAction>().unwrap_err();
99 assert_eq!(err.type_name, "CodeAction");
100 assert_eq!(err.value, "overwrite");
101 }
102
103 #[test]
104 fn test_code_action_display_roundtrip() {
105 for a in [
106 CodeAction::Create,
107 CodeAction::Append,
108 CodeAction::Prepend,
109 CodeAction::Replace,
110 CodeAction::InsertBefore,
111 CodeAction::InsertAfter,
112 CodeAction::Full,
113 ] {
114 let s = a.to_string();
115 assert_eq!(s.parse::<CodeAction>().unwrap(), a);
116 }
117 }
118
119 #[test]
120 fn test_code_block_serde_roundtrip() {
121 let cb = CodeBlock {
122 lang: Some("rust".to_owned()),
123 target: Some("src/main.rs".to_owned()),
124 action: CodeAction::Append,
125 body: "fn main() {}".to_owned(),
126 anchor: None,
127 old: None,
128 };
129 let json = serde_json::to_string(&cb).unwrap();
130 let back: CodeBlock = serde_json::from_str(&json).unwrap();
131 assert_eq!(cb, back);
132 }
133
134 #[test]
135 fn test_code_block_with_replace_fields() {
136 let cb = CodeBlock {
137 lang: Some("rust".to_owned()),
138 target: Some("src/lib.rs".to_owned()),
139 action: CodeAction::Replace,
140 body: "fn new_impl() {}".to_owned(),
141 anchor: None,
142 old: Some("fn old_impl() {}".to_owned()),
143 };
144 let json = serde_json::to_string(&cb).unwrap();
145 assert!(json.contains("old_impl"));
146 let back: CodeBlock = serde_json::from_str(&json).unwrap();
147 assert_eq!(cb, back);
148 }
149
150 #[test]
151 fn test_code_block_with_anchor() {
152 let cb = CodeBlock {
153 lang: Some("rust".to_owned()),
154 target: Some("src/lib.rs".to_owned()),
155 action: CodeAction::InsertAfter,
156 body: " pub new_field: String,".to_owned(),
157 anchor: Some("pub struct Foo {".to_owned()),
158 old: None,
159 };
160 let json = serde_json::to_string(&cb).unwrap();
161 assert!(json.contains("anchor"));
162 let back: CodeBlock = serde_json::from_str(&json).unwrap();
163 assert_eq!(cb, back);
164 }
165}