1use anyhow::{Context, Result};
8
9pub const TEXTURE_TYPE_SPRITE: u32 = 8;
12
13pub const SPRITE_MODE_SINGLE: u32 = 1;
16
17#[derive(Debug, Clone, Default)]
19pub struct MetaInfo {
20 pub guid: u128,
22 pub sprite_sheet: Vec<(i64, String)>,
25 pub texture_type: Option<u32>,
27 pub sprite_mode: Option<u32>,
29}
30
31pub fn parse(text: &str) -> Result<MetaInfo> {
37 let guid = parse_guid(text).context("missing or malformed `guid:` in .meta")?;
38 let sprite_sheet = parse_sprite_sheet(text);
39 let texture_type = parse_u32_field(text, "textureType:");
40 let sprite_mode = parse_u32_field(text, "spriteMode:");
41 Ok(MetaInfo {
42 guid,
43 sprite_sheet,
44 texture_type,
45 sprite_mode,
46 })
47}
48
49fn parse_u32_field(text: &str, key: &str) -> Option<u32> {
54 for line in text.lines() {
55 let line = line.trim_start();
56 if let Some(rest) = line.strip_prefix(key) {
57 return rest.trim().parse().ok();
58 }
59 }
60 None
61}
62
63fn parse_guid(text: &str) -> Option<u128> {
64 for line in text.lines() {
65 let line = line.trim_start();
66 if let Some(rest) = line.strip_prefix("guid:") {
67 let hex = rest.trim();
68 if hex.len() == 32 {
69 return u128::from_str_radix(hex, 16).ok();
70 }
71 }
72 }
73 None
74}
75
76fn parse_sprite_sheet(text: &str) -> Vec<(i64, String)> {
84 let mut out: Vec<(i64, String)> = Vec::new();
85 let mut in_sprites = false;
86 let mut cur_name: Option<String> = None;
87 let mut cur_id: Option<i64> = None;
88
89 for line in text.lines() {
90 let trimmed = line.trim();
91
92 if trimmed == "sprites:" {
93 in_sprites = true;
94 continue;
95 }
96 if !in_sprites {
97 continue;
98 }
99
100 if !line.starts_with(' ') && !line.starts_with('\t') && !trimmed.is_empty() {
103 break;
104 }
105
106 if let Some(after_dash) = trimmed.strip_prefix("- ") {
110 flush(&mut out, &mut cur_name, &mut cur_id);
111 absorb_kv(after_dash, &mut cur_name, &mut cur_id);
112 continue;
113 }
114
115 absorb_kv(trimmed, &mut cur_name, &mut cur_id);
116 }
117 flush(&mut out, &mut cur_name, &mut cur_id);
118 out
119}
120
121fn absorb_kv(line: &str, cur_name: &mut Option<String>, cur_id: &mut Option<i64>) {
122 if let Some(rest) = line.strip_prefix("name:") {
123 let s = rest.trim();
124 if cur_name.is_none() && !s.is_empty() {
125 *cur_name = Some(s.to_string());
126 }
127 } else if let Some(rest) = line.strip_prefix("internalID:") {
128 *cur_id = rest.trim().parse().ok();
129 }
130}
131
132fn flush(out: &mut Vec<(i64, String)>, name: &mut Option<String>, id: &mut Option<i64>) {
133 if let (Some(n), Some(i)) = (name.take(), id.take())
134 && !n.is_empty()
135 {
136 out.push((i, n));
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn parses_simple_guid() {
146 let text = "fileFormatVersion: 2\nguid: 7d602c2080b53413fa393df6b2c0af43\n";
147 let info = parse(text).unwrap();
148 assert_eq!(info.guid, 0x7d602c2080b53413fa393df6b2c0af43_u128);
149 assert!(info.sprite_sheet.is_empty());
150 }
151
152 #[test]
153 fn rejects_short_guid() {
154 let text = "guid: deadbeef\n";
155 assert!(parse(text).is_err());
156 }
157
158 #[test]
159 fn parses_sprite_sheet() {
160 let text = "fileFormatVersion: 2
161guid: 7d602c2080b53413fa393df6b2c0af43
162TextureImporter:
163 spriteSheet:
164 sprites:
165 - serializedVersion: 2
166 name: spr_a
167 internalID: 11111
168 rect:
169 serializedVersion: 2
170 - serializedVersion: 2
171 name: spr_b
172 internalID: 22222
173 spritePackingTag:
174";
175 let info = parse(text).unwrap();
176 assert_eq!(
177 info.sprite_sheet,
178 vec![(11111, "spr_a".to_string()), (22222, "spr_b".to_string()),]
179 );
180 }
181
182 #[test]
183 fn parses_texture_type_and_sprite_mode() {
184 let text = "fileFormatVersion: 2
185guid: 7d602c2080b53413fa393df6b2c0af43
186TextureImporter:
187 textureType: 8
188 spriteMode: 1
189 spriteSheet:
190 sprites: []
191";
192 let info = parse(text).unwrap();
193 assert_eq!(info.texture_type, Some(8));
194 assert_eq!(info.sprite_mode, Some(1));
195 }
196
197 #[test]
198 fn missing_texture_fields_are_none() {
199 let text = "fileFormatVersion: 2\nguid: 7d602c2080b53413fa393df6b2c0af43\n";
200 let info = parse(text).unwrap();
201 assert_eq!(info.texture_type, None);
202 assert_eq!(info.sprite_mode, None);
203 }
204}