initiative_core/world/place/
view.rs

1use 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}