initiative_core/world/place/
view.rs1use crate::world::place::{PlaceData, PlaceRelations, PlaceType};
2use std::fmt;
3use uuid::Uuid;
4
5pub struct NameView<'a>(&'a PlaceData);
6
7pub struct SummaryView<'a>(&'a PlaceData);
8
9pub struct DescriptionView<'a>(&'a PlaceData);
10
11pub struct DetailsView<'a> {
12 place: &'a PlaceData,
13 uuid: Uuid,
14 relations: PlaceRelations,
15}
16
17impl<'a> NameView<'a> {
18 pub fn new(place: &'a PlaceData) -> Self {
19 Self(place)
20 }
21}
22
23impl<'a> SummaryView<'a> {
24 pub fn new(place: &'a PlaceData) -> Self {
25 Self(place)
26 }
27}
28
29impl<'a> DescriptionView<'a> {
30 pub fn new(place: &'a PlaceData) -> Self {
31 Self(place)
32 }
33}
34
35impl<'a> DetailsView<'a> {
36 pub fn new(place: &'a PlaceData, uuid: Uuid, relations: PlaceRelations) -> Self {
37 Self {
38 place,
39 uuid,
40 relations,
41 }
42 }
43}
44
45impl fmt::Display for NameView<'_> {
46 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
47 let place = self.0;
48
49 if let Some(name) = place.name.value() {
50 write!(
51 f,
52 "{} `{}`",
53 place.subtype.value().unwrap_or(&PlaceType::Any).get_emoji(),
54 name,
55 )
56 } else {
57 Ok(())
58 }
59 }
60}
61
62impl fmt::Display for SummaryView<'_> {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 let place = self.0;
65
66 match (place.subtype.value(), place.name.is_some()) {
67 (Some(subtype), true) => write!(f, "{} ({})", place.display_name(), subtype),
68 (Some(subtype), false) => write!(f, "{} {}", subtype.get_emoji(), subtype),
69 (None, true) => write!(f, "{} (place)", place.display_name()),
70 (None, false) => write!(f, "{} place", PlaceType::Any.get_emoji()),
71 }
72 }
73}
74
75impl fmt::Display for DescriptionView<'_> {
76 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77 if let Some(subtype) = self.0.subtype.value() {
78 write!(f, "{}", subtype)
79 } else {
80 write!(f, "place")
81 }
82 }
83}
84
85impl fmt::Display for DetailsView<'_> {
86 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87 let Self {
88 place,
89 uuid,
90 relations,
91 } = self;
92
93 writeln!(
94 f,
95 "<div class=\"thing-box place\" data-uuid=\"{}\">\n",
96 uuid
97 )?;
98
99 place
100 .name
101 .value()
102 .map(|name| write!(f, "# {}", name))
103 .unwrap_or_else(|| write!(f, "# Unnamed {}", place.display_description()))?;
104
105 write!(f, "\n*{}*", place.display_description())?;
106
107 relations
108 .location
109 .as_ref()
110 .map(|(parent, grandparent)| {
111 if let Some(grandparent) = grandparent {
112 write!(
113 f,
114 "\n\n**Location:** {}, {}",
115 parent.display_name(),
116 grandparent.display_name(),
117 )
118 } else {
119 write!(f, "\n\n**Location:** {}", parent.display_summary())
120 }
121 })
122 .transpose()?;
123
124 place
125 .description
126 .value()
127 .map(|description| write!(f, "\n\n{}", description))
128 .transpose()?;
129
130 write!(f, "\n\n</div>")?;
131
132 Ok(())
133 }
134}
135
136#[cfg(test)]
137mod test {
138 use super::*;
139 use crate::test_utils as test;
140
141 #[test]
142 fn view_test_empty() {
143 let place = PlaceData::default();
144 assert_eq!("", format!("{}", place.display_name()));
145 assert_eq!("📍 place", format!("{}", place.display_summary()));
146 assert_eq!("place", format!("{}", place.display_description()));
147 assert_eq!(
148 r#"<div class="thing-box place" data-uuid="00000000-0000-0000-0000-000000000000">
149
150# Unnamed place
151*place*
152
153</div>"#,
154 place
155 .display_details(Uuid::nil(), PlaceRelations::default())
156 .to_string(),
157 );
158 }
159
160 #[test]
161 fn view_test_name_only() {
162 let place = test::place().name("Olympus").build();
163 assert_eq!("📍 `Olympus`", format!("{}", place.display_name()));
164 assert_eq!(
165 "📍 `Olympus` (place)",
166 format!("{}", place.display_summary()),
167 );
168 assert_eq!("place", format!("{}", place.display_description()));
169 assert_eq!(
170 r#"<div class="thing-box place" data-uuid="00000000-0000-0000-0000-000000000000">
171
172# Olympus
173*place*
174
175</div>"#,
176 place
177 .display_details(Uuid::nil(), PlaceRelations::default())
178 .to_string(),
179 );
180 }
181
182 #[test]
183 fn view_test_subtype_only() {
184 let place = test::place()
185 .subtype("inn".parse::<PlaceType>().unwrap())
186 .build();
187 assert_eq!("", format!("{}", place.display_name()));
188 assert_eq!("🏨 inn", format!("{}", place.display_summary()));
189 assert_eq!("inn", format!("{}", place.display_description()));
190 assert_eq!(
191 r#"<div class="thing-box place" data-uuid="00000000-0000-0000-0000-000000000000">
192
193# Unnamed inn
194*inn*
195
196</div>"#,
197 place
198 .display_details(Uuid::nil(), PlaceRelations::default())
199 .to_string(),
200 );
201 }
202
203 #[test]
204 fn view_test_description_only() {
205 let place = test::place().description("A street with no name.").build();
206 assert_eq!("", format!("{}", place.display_name()));
207 assert_eq!("📍 place", format!("{}", place.display_summary()));
208 assert_eq!("place", format!("{}", place.display_description()));
209 assert_eq!(
210 r#"<div class="thing-box place" data-uuid="00000000-0000-0000-0000-000000000000">
211
212# Unnamed place
213*place*
214
215A street with no name.
216
217</div>"#,
218 place
219 .display_details(Uuid::nil(), PlaceRelations::default())
220 .to_string(),
221 );
222 }
223
224 #[test]
225 fn view_test_name_subtype() {
226 let place = test::place::ithaca();
227 assert_eq!("🏝 `Ithaca`", format!("{}", place.display_name()));
228 assert_eq!(
229 "🏝 `Ithaca` (island)",
230 format!("{}", place.display_summary()),
231 );
232 assert_eq!("island", format!("{}", place.display_description()));
233 assert_eq!(
234 r#"<div class="thing-box place" data-uuid="00000000-0000-0000-0000-000000000001">
235
236# Ithaca
237*island*
238
239</div>"#,
240 place.display_details(PlaceRelations::default()).to_string(),
241 );
242 }
243
244 #[test]
245 fn view_test_name_description() {
246 let place = test::place()
247 .name("The Invulnerable Vagrant")
248 .description("Come in and see me, and me, and me!")
249 .build();
250 assert_eq!(
251 "📍 `The Invulnerable Vagrant`",
252 format!("{}", place.display_name()),
253 );
254 assert_eq!(
255 "📍 `The Invulnerable Vagrant` (place)",
256 format!("{}", place.display_summary()),
257 );
258 assert_eq!("place", format!("{}", place.display_description()));
259 assert_eq!(
260 r#"<div class="thing-box place" data-uuid="00000000-0000-0000-0000-000000000000">
261
262# The Invulnerable Vagrant
263*place*
264
265Come in and see me, and me, and me!
266
267</div>"#,
268 place
269 .display_details(Uuid::nil(), PlaceRelations::default())
270 .to_string(),
271 );
272 }
273
274 #[test]
275 fn view_test_subtype_description() {
276 let place = test::place()
277 .subtype("inn".parse::<PlaceType>().unwrap())
278 .description("You can check out any time you like.")
279 .build();
280 assert_eq!("", format!("{}", place.display_name()));
281 assert_eq!("🏨 inn", format!("{}", place.display_summary()));
282 assert_eq!("inn", format!("{}", place.display_description()));
283 assert_eq!(
284 r#"<div class="thing-box place" data-uuid="00000000-0000-0000-0000-000000000000">
285
286# Unnamed inn
287*inn*
288
289You can check out any time you like.
290
291</div>"#,
292 place
293 .display_details(Uuid::nil(), PlaceRelations::default())
294 .to_string(),
295 );
296 }
297
298 #[test]
299 fn view_test_name_subtype_description() {
300 let place = test::place::greece();
301 assert_eq!("👑 `Greece`", format!("{}", place.display_name()));
302 assert_eq!(
303 "👑 `Greece` (territory)",
304 format!("{}", place.display_summary()),
305 );
306 assert_eq!("territory", format!("{}", place.display_description()));
307 assert_eq!(
308 r#"<div class="thing-box place" data-uuid="00000000-0000-0000-0000-000000000002">
309
310# Greece
311*territory*
312
313You're cruisin' for a bruisin'.
314
315</div>"#,
316 place.display_details(PlaceRelations::default()).to_string(),
317 );
318 }
319
320 #[test]
321 fn details_view_test_with_parent_location() {
322 assert_eq!(
323 r#"<div class="thing-box place" data-uuid="00000000-0000-0000-0000-000000000001">
324
325# Ithaca
326*island*
327
328**Location:** 👑 `Greece` (territory)
329
330</div>"#,
331 test::place::ithaca()
332 .display_details(test::place::ithaca::relations())
333 .to_string(),
334 );
335 }
336
337 #[test]
338 fn details_view_test_with_grandparent_location() {
339 assert_eq!(
340 r#"<div class="thing-box place" data-uuid="00000000-0000-0000-0000-000000000000">
341
342# Chez Penelope
343*castle*
344
345**Location:** 🏝 `Ithaca`, 👑 `Greece`
346
347</div>"#,
348 test::place()
349 .name("Chez Penelope")
350 .subtype("castle".parse::<PlaceType>().unwrap())
351 .build()
352 .display_details(
353 Uuid::nil(),
354 test::place::relations()
355 .location(test::place::ithaca())
356 .location(test::place::greece())
357 .build()
358 )
359 .to_string(),
360 );
361 }
362}