1use serde::Serialize;
2use serde_json::Value;
3use std::collections::BTreeMap;
4
5use crate::SwapType;
6
7#[derive(Clone, Debug, Serialize)]
14#[serde(rename_all = "camelCase")]
15pub struct HxLocation {
16 path: String,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 target: Option<String>,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 source: Option<String>,
21 #[serde(skip_serializing_if = "Option::is_none")]
22 event: Option<String>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 swap: Option<String>,
25 #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
26 headers: BTreeMap<String, String>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 values: Option<Value>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 handler: Option<String>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 select: Option<String>,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 push: Option<Value>,
35 #[serde(skip_serializing_if = "Option::is_none")]
36 replace: Option<String>,
37}
38
39impl HxLocation {
40 pub fn new(path: impl Into<String>) -> Self {
42 HxLocation {
43 path: path.into(),
44 target: None,
45 source: None,
46 event: None,
47 swap: None,
48 headers: BTreeMap::new(),
49 values: None,
50 handler: None,
51 select: None,
52 push: None,
53 replace: None,
54 }
55 }
56
57 pub fn target(mut self, selector: impl Into<String>) -> Self {
59 self.target = Some(selector.into());
60 self
61 }
62
63 pub fn source(mut self, selector: impl Into<String>) -> Self {
65 self.source = Some(selector.into());
66 self
67 }
68
69 pub fn event(mut self, event: impl Into<String>) -> Self {
71 self.event = Some(event.into());
72 self
73 }
74
75 pub fn swap(mut self, swap: SwapType) -> Self {
77 self.swap = Some(swap.to_string());
78 self
79 }
80
81 pub fn handler(mut self, handler: impl Into<String>) -> Self {
83 self.handler = Some(handler.into());
84 self
85 }
86
87 pub fn select(mut self, selector: impl Into<String>) -> Self {
89 self.select = Some(selector.into());
90 self
91 }
92
93 pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
95 self.headers.insert(name.into(), value.into());
96 self
97 }
98
99 pub fn headers<I, K, V>(mut self, headers: I) -> Self
101 where
102 I: IntoIterator<Item = (K, V)>,
103 K: Into<String>,
104 V: Into<String>,
105 {
106 self.headers
107 .extend(headers.into_iter().map(|(k, v)| (k.into(), v.into())));
108 self
109 }
110
111 pub fn values<T>(mut self, values: T) -> serde_json::Result<Self>
113 where
114 T: Serialize,
115 {
116 self.values = Some(serde_json::to_value(values)?);
117 Ok(self)
118 }
119
120 pub fn disable_push(mut self) -> Self {
122 self.push = Some(Value::Bool(false));
123 self
124 }
125
126 pub fn push_path(mut self, path: impl Into<String>) -> Self {
128 self.push = Some(Value::String(path.into()));
129 self
130 }
131
132 pub fn replace(mut self, path: impl Into<String>) -> Self {
134 self.replace = Some(path.into());
135 self
136 }
137
138 pub(crate) fn into_header_value(self) -> String {
139 serde_json::to_string(&self).expect("HxLocation serialization failed")
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[derive(Serialize)]
148 struct Payload<'a> {
149 id: u32,
150 name: &'a str,
151 }
152
153 #[test]
154 fn values_accepts_serializable_types() {
155 let location = HxLocation::new("/path")
156 .values(Payload {
157 id: 7,
158 name: "demo",
159 })
160 .expect("serialization should succeed");
161
162 let serialized = location.into_header_value();
163 let parsed: Value = serde_json::from_str(&serialized).unwrap();
164
165 assert_eq!(parsed["path"], "/path");
166 assert_eq!(parsed["values"]["id"], 7);
167 assert_eq!(parsed["values"]["name"], "demo");
168 }
169}