1use std::fmt::Display;
2use std::io::Cursor;
3
4use radix_fmt::radix_36;
5use loe::{process, Config, TransformMode};
6
7pub mod colour;
8pub mod dialogue;
9pub mod ending;
10pub mod error;
11pub mod exit;
12pub mod game;
13pub mod image;
14pub mod item;
15pub mod mock;
16pub mod palette;
17pub mod position;
18pub mod room;
19pub mod sprite;
20pub mod text;
21pub mod tile;
22pub mod variable;
23pub mod test_omnibus;
24
25pub use colour::Colour;
26pub use dialogue::Dialogue;
27pub use ending::Ending;
28pub use error::Error;
29pub use exit::*;
30pub use game::*;
31pub use image::Image;
32pub use item::Item;
33pub use palette::Palette;
34pub use position::Position;
35pub use room::Room;
36pub use sprite::Sprite;
37pub use text::*;
38pub use tile::Tile;
39pub use variable::Variable;
40
41#[derive(Clone, Debug, Eq, PartialEq)]
42pub struct Instance {
43 position: Position,
44 id: String, }
46
47#[derive(Clone, Debug, Eq, PartialEq)]
51pub struct ExitInstance {
52 position: Position,
53 exit: Exit,
54 transition: Option<Transition>,
55 dialogue_id: Option<String>,
56}
57
58pub trait AnimationFrames {
59 fn to_string(&self) -> String;
60}
61
62impl AnimationFrames for Vec<Image> {
63 fn to_string(&self) -> String {
64 let mut string = String::new();
65 let last_frame = self.len() - 1;
66
67 for (i, frame) in self.iter().enumerate() {
68 string.push_str(&frame.to_string());
69
70 if i < last_frame {
71 string.push_str(&"\n>\n".to_string());
72 }
73 }
74
75 string
76 }
77}
78
79fn to_base36(int: u64) -> String {
81 format!("{}", radix_36(int))
82}
83
84pub trait ToBase36 {
85 fn to_base36(&self) -> String;
86}
87
88impl ToBase36 for u64 {
89 fn to_base36(&self) -> String {
90 to_base36(*self)
91 }
92}
93
94fn optional_data_line<T: Display>(label: &str, item: Option<T>) -> String {
96 if item.is_some() {
97 format!("\n{} {}", label, item.unwrap())
98 } else {
99 "".to_string()
100 }
101}
102
103fn transform_line_endings(input: String, mode: TransformMode) -> String {
104 let mut input = Cursor::new(input);
105 let mut output = Cursor::new(Vec::new());
106
107 process(&mut input, &mut output, Config::default().transform(mode)).unwrap();
108 String::from_utf8(output.into_inner()).unwrap()
109}
110
111fn segments_from_str(str: &str) -> Vec<String> {
112 let string = str.replace("\n\nNAME", "\n\"\"\"\n\"\"\"\nNAME");
116
117 let mut output:Vec<String> = Vec::new();
118 let mut inside_escaped_block = false;
120 let mut current_segment : Vec<String> = Vec::new();
121
122 for line in string.lines() {
123 if line == "\"\"\"" {
124 inside_escaped_block = ! inside_escaped_block;
125 }
126
127 if line == "" && !inside_escaped_block {
128 output.push(current_segment.join("\n"));
129 current_segment = Vec::new();
130 } else {
131 current_segment.push(line.to_string());
132 }
133 }
134
135 output.push(current_segment.join("\n"));
136
137 output
138}
139
140fn try_id(ids: &Vec<String>, id: &String) -> String {
144 let id = id.clone();
145 let ids = ids.clone();
146 if is_id_available(&ids, &id) {
147 id
148 } else {
149 new_unique_id(ids)
150 }
151}
152
153fn is_id_available(ids: &Vec<String>, id: &String) -> bool {
154 ! ids.contains(id)
155}
156
157fn new_unique_id(ids: Vec<String>) -> String {
159 let mut new_id: u64 = 0;
160
161 while ids.contains(&new_id.to_base36()) {
162 new_id += 1;
163 }
164
165 to_base36(new_id)
166}
167
168pub trait Quote {
169 fn quote(&self) -> String;
170}
171
172impl Quote for String {
173 fn quote(&self) -> String {
174 format!("\"\"\"\n{}\n\"\"\"", self)
175 }
176}
177
178pub trait Unquote {
179 fn unquote(&self) -> String;
180}
181
182impl Unquote for String {
183 fn unquote(&self) -> String {
184 self.trim_matches('\"').trim_matches('\n').to_string()
185 }
186}
187
188#[cfg(test)]
189mod test {
190 use crate::{ToBase36, optional_data_line, mock, segments_from_str, Quote, Unquote, new_unique_id, try_id};
191
192 #[test]
193 fn to_base36() {
194 assert_eq!((37 as u64).to_base36(), "11");
195 }
196
197 #[test]
198 fn test_optional_data_line() {
199 let output = optional_data_line("NAME", mock::item().name);
200 assert_eq!(output, "\nNAME door");
201 }
202
203 #[test]
204 fn string_to_segments() {
205 let output = segments_from_str(include_str!("./test-resources/segments"));
206
207 let expected = vec![
208 "\"\"\"\nthe first segment is a long bit of text\n\n\nit contains empty lines\n\n\"\"\"".to_string(),
209 "this is a new segment\nthis is still the second segment\nblah\nblah".to_string(),
210 "DLG SEGMENT_3\n\"\"\"\nthis is a short \"long\" bit of text\n\"\"\"".to_string(),
211 "this is the last segment".to_string(),
212 ];
213
214 assert_eq!(output, expected);
215 }
216
217 #[test]
218 fn quote() {
219 let output = "this is a string.\nIt has 2 lines".to_string().quote();
220 let expected = "\"\"\"\nthis is a string.\nIt has 2 lines\n\"\"\"";
221 assert_eq!(output, expected);
222 }
223
224 #[test]
225 fn unquote() {
226 let output = "\"\"\"\nwho the fuck is scraeming \"LOG OFF\" at my house.\nshow yourself, coward.\ni will never log off\n\"\"\"".to_string().unquote();
227 let expected = "who the fuck is scraeming \"LOG OFF\" at my house.\nshow yourself, coward.\ni will never log off";
228 assert_eq!(output, expected);
229 }
230
231 #[test]
232 fn test_try_id() {
233 assert_eq!(
235 try_id(&vec!["0".to_string(), "1".to_string()], &"1".to_string()),
236 "2"
237 );
238 assert_eq!(
240 try_id(&vec!["0".to_string(), "1".to_string()], &"3".to_string()),
241 "3"
242 );
243 }
244
245 #[test]
246 fn test_new_unique_id() {
247 assert_eq!(new_unique_id(vec!["1".to_string(), "z".to_string()]), "0".to_string());
249 assert_eq!(new_unique_id(vec!["0".to_string(), "2".to_string()]), "1".to_string());
251 assert_eq!(new_unique_id(vec!["0".to_string(), "1".to_string()]), "2".to_string());
253 assert_eq!(new_unique_id(vec!["1".to_string(), "0".to_string()]), "2".to_string());
255 assert_eq!(
257 new_unique_id(vec!["0".to_string(), "0".to_string(), "1".to_string()]),
258 "2".to_string()
259 );
260 }
261}