human_readable_map16/
lib.rs1use std::{ffi::{CStr, CString}, path::{Path, PathBuf}};
16
17use thiserror::Error;
18
19mod ffi {
20 use std::ffi::{c_char, c_int};
21
22 unsafe extern "C" {
23 pub(crate) fn from_map16(input_file: *const c_char, output_folder: *const c_char) -> c_int;
24 pub(crate) fn to_map16(input_folder: *const c_char, output_file: *const c_char) -> c_int;
25
26 pub(crate) fn get_last_error() -> *const c_char;
27 }
28}
29
30fn last_error() -> String {
31 let cstr = unsafe {
32 CStr::from_ptr(ffi::get_last_error())
33 };
34
35 cstr.to_string_lossy().into_owned()
36}
37
38pub type Result<T> = std::result::Result<T, Error>;
39
40#[derive(Debug, Error)]
41pub enum Error {
42 #[error(transparent)]
43 Io(#[from] std::io::Error),
44
45 #[error(transparent)]
46 NulError(#[from] std::ffi::NulError),
47
48 #[error("Map16 conversion error: {0}")]
49 Conversion(String),
50
51 #[error("Failed to convert {0} to str")]
52 PathConversion(PathBuf),
53}
54
55fn convert_to_cstring(path: &Path) -> Result<CString> {
56 let s = path.to_str().ok_or(Error::PathConversion(path.to_path_buf()))?;
57
58 Ok(CString::new(s)?)
59}
60
61pub fn from_map16<I: AsRef<Path>, O: AsRef<Path>>(input_file: I, output_folder: O) -> Result<()> {
84 let input = convert_to_cstring(input_file.as_ref())?;
85 let output = convert_to_cstring(output_folder.as_ref())?;
86
87 let return_code = unsafe { ffi::from_map16(input.as_ptr(), output.as_ptr()) };
88
89 if return_code == 0 {
90 Ok(())
91 } else {
92 Err(Error::Conversion(last_error()))
93 }
94}
95
96pub fn to_map16<I: AsRef<Path>, O: AsRef<Path>>(input_folder: I, output_file: O) -> Result<()> {
119 let input = convert_to_cstring(input_folder.as_ref())?;
120 let output = convert_to_cstring(output_file.as_ref())?;
121
122 let return_code = unsafe { ffi::to_map16(input.as_ptr(), output.as_ptr()) };
123
124 if return_code == 0 {
125 Ok(())
126 } else {
127 Err(Error::Conversion(last_error()))
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use dir_assert::assert_paths;
134
135 use crate::{from_map16, to_map16};
136
137 #[test]
138 fn from_map16_works() {
139 from_map16("test_data/all.map16", "test_data/test").unwrap();
140
141 assert_paths!("test_data/all/global_pages", "test_data/test/global_pages");
142 assert_paths!("test_data/all/pipe_tiles", "test_data/test/pipe_tiles");
143 assert_paths!("test_data/all/tileset_group_specific_tiles", "test_data/test/tileset_group_specific_tiles");
144 }
145
146 #[test]
147 fn to_map16_works() {
148 to_map16("test_data/all", "test_data/test.map16").unwrap();
149 }
150
151 #[test]
152 #[should_panic]
153 fn to_map16_detects_errors() {
154 to_map16("test_data/incorrect", "test_data/incorrect.map16").unwrap();
155 }
156}