aviutl2\generic\binding/
project.rs1pub struct ProjectFile {
3 pub(crate) internal: *mut aviutl2_sys::plugin2::PROJECT_FILE,
4}
5
6#[derive(thiserror::Error, Debug)]
8pub enum ProjectFileError {
9 #[error("key contains null byte: {0}")]
10 KeyContainsNull(std::ffi::NulError),
11 #[error("data retrieval failed for key {0}")]
12 RetrievalFailed(String),
13 #[error("data length exceeds 4096 bytes, got {0} bytes")]
14 DataTooLarge(usize),
15 #[error("value contains null byte: {0}")]
16 ValueContainsNull(std::ffi::NulError),
17}
18
19impl ProjectFile {
20 pub unsafe fn from_raw(raw: *mut aviutl2_sys::plugin2::PROJECT_FILE) -> Self {
26 Self { internal: raw }
27 }
28
29 pub fn get_param_string(&self, key: &str) -> Result<String, ProjectFileError> {
36 let c_key = std::ffi::CString::new(key).map_err(ProjectFileError::KeyContainsNull)?;
37 unsafe {
38 let raw_str = ((*self.internal).get_param_string)(c_key.as_ptr() as _);
39 if raw_str.is_null() {
40 return Err(ProjectFileError::RetrievalFailed(key.to_string()));
41 }
42 Ok(std::ffi::CStr::from_ptr(raw_str)
43 .to_string_lossy()
44 .into_owned())
45 }
46 }
47
48 pub fn get_param_binary(&self, key: &str, data: &mut [u8]) -> Result<(), ProjectFileError> {
56 let success = unsafe {
57 let key = std::ffi::CString::new(key).map_err(ProjectFileError::KeyContainsNull)?;
58 ((*self.internal).get_param_binary)(
59 key.as_ptr() as _,
60 data.as_mut_ptr() as _,
61 data.len() as _,
62 )
63 };
64 if !success {
65 return Err(ProjectFileError::RetrievalFailed(key.to_string()));
66 }
67 Ok(())
68 }
69
70 pub fn set_param_string(&mut self, key: &str, value: &str) -> Result<(), ProjectFileError> {
76 let key_cstr = std::ffi::CString::new(key).map_err(ProjectFileError::KeyContainsNull)?;
77 let value_cstr =
78 std::ffi::CString::new(value).map_err(ProjectFileError::ValueContainsNull)?;
79 unsafe {
80 ((*self.internal).set_param_string)(key_cstr.as_ptr() as _, value_cstr.as_ptr() as _);
81 }
82 Ok(())
83 }
84
85 pub fn set_param_binary(&mut self, key: &str, data: &[u8]) -> Result<(), ProjectFileError> {
92 if data.len() > 4096 {
93 return Err(ProjectFileError::DataTooLarge(data.len()));
94 }
95 unsafe {
96 let key = std::ffi::CString::new(key).map_err(ProjectFileError::KeyContainsNull)?;
97 ((*self.internal).set_param_binary)(
98 key.as_ptr() as _,
99 data.as_ptr() as _,
100 data.len() as _,
101 );
102 }
103 Ok(())
104 }
105
106 pub fn clear_params(&mut self) {
108 unsafe { ((*self.internal).clear_params)() }
109 }
110}
111
112#[cfg(feature = "serde")]
113static NAMESPACE: &str = "--aviutl2-rs";
114
115#[cfg(feature = "serde")]
116impl ProjectFile {
117 pub fn serialize<T: serde::Serialize>(&mut self, key: &str, value: &T) -> crate::AnyResult<()> {
128 let bytes = rmp_serde::to_vec_named(value)?;
129 let bytes = zstd::encode_all(&bytes[..], 0)?;
130 let num_bytes = bytes.len();
131 self.set_param_string(key, &format!("{NAMESPACE}:serde-zstd-v1:{}", num_bytes))?;
132 for (i, chunk) in bytes.chunks(4096).enumerate() {
133 let chunk_key = format!("{NAMESPACE}:serde-zstd-v1:chunk:{}:{}", key, i);
134 self.set_param_binary(&chunk_key, chunk)?;
135 }
136 Ok(())
137 }
138
139 pub fn deserialize<T: serde::de::DeserializeOwned>(&self, key: &str) -> crate::AnyResult<T> {
141 let header = self.get_param_string(key)?;
142 let header_prefix = format!("{NAMESPACE}:serde-zstd-v1:");
143 let num_bytes = header
144 .strip_prefix(&header_prefix)
145 .ok_or_else(|| anyhow::anyhow!("invalid header for key {}", key))?;
146 let num_bytes: usize = num_bytes.parse()?;
147 if num_bytes == 0 {
148 anyhow::bail!("invalid data length 0 for key {}", key);
149 }
150 let mut bytes = Vec::with_capacity(num_bytes);
151 let mut read_bytes = 0;
152 let mut chunk = vec![0u8; 4096];
153 for i in 0.. {
154 let chunk_key = format!("{NAMESPACE}:serde-zstd-v1:chunk:{}:{}", key, i);
155 let to_read = std::cmp::min(4096, num_bytes - read_bytes);
156 chunk.resize(to_read, 0);
157 match self.get_param_binary(&chunk_key, &mut chunk) {
158 Ok(()) => {
159 bytes.extend_from_slice(&chunk);
160 read_bytes += to_read;
161 if read_bytes >= num_bytes {
162 break;
163 }
164 }
165 Err(_) => break,
166 }
167 }
168 anyhow::ensure!(
169 read_bytes == num_bytes,
170 "incomplete data for key {}, expected {} bytes, got {} bytes",
171 key,
172 num_bytes,
173 read_bytes
174 );
175 let decompressed_bytes = zstd::decode_all(&bytes[..])?;
176 let value: T = rmp_serde::from_slice(&decompressed_bytes)?;
177 Ok(value)
178 }
179}