polars_io/parquet/
encryption.rs1use std::fmt::Debug;
4use std::hash::Hash;
5use std::sync::Arc;
6
7use polars_error::PolarsResult;
8use polars_parquet::parquet::encryption::decrypt::FileDecryptionProperties;
9use polars_parquet::parquet::encryption::encrypt::{
10 FileEncryptionProperties, validate_encrypted_column_names,
11};
12use polars_parquet::parquet::metadata::SchemaDescriptor;
13
14pub trait EncryptionPropertiesFactory: Send + Sync {
16 fn file_encryption_properties(&self) -> PolarsResult<Arc<FileEncryptionProperties>>;
18
19 fn encrypted_column_names(&self) -> Vec<String>;
24}
25
26pub trait DecryptionPropertiesFactory: Send + Sync {
28 fn file_decryption_properties(&self) -> PolarsResult<Arc<FileDecryptionProperties>>;
30}
31
32impl EncryptionPropertiesFactory for Arc<FileEncryptionProperties> {
34 fn file_encryption_properties(&self) -> PolarsResult<Arc<FileEncryptionProperties>> {
35 Ok(Arc::clone(self))
36 }
37
38 fn encrypted_column_names(&self) -> Vec<String> {
39 FileEncryptionProperties::encrypted_column_names(self)
40 .map(str::to_string)
41 .collect()
42 }
43}
44
45impl DecryptionPropertiesFactory for Arc<FileDecryptionProperties> {
47 fn file_decryption_properties(&self) -> PolarsResult<Arc<FileDecryptionProperties>> {
48 Ok(Arc::clone(self))
49 }
50}
51
52#[derive(Clone)]
57pub struct ParquetEncryption(Arc<dyn EncryptionPropertiesFactory>);
58
59impl ParquetEncryption {
60 pub fn new(factory: impl EncryptionPropertiesFactory + 'static) -> Self {
62 Self(Arc::new(factory))
63 }
64
65 pub fn file_properties(&self) -> PolarsResult<Arc<FileEncryptionProperties>> {
67 self.0.file_encryption_properties()
68 }
69
70 pub fn encrypted_column_names(&self) -> Vec<String> {
73 self.0.encrypted_column_names()
74 }
75
76 pub fn validate_encrypted_column_names(&self, schema: &SchemaDescriptor) -> PolarsResult<()> {
79 let encrypted_columns = self.encrypted_column_names();
80 validate_encrypted_column_names(encrypted_columns.iter().map(String::as_str), schema)?;
81 Ok(())
82 }
83}
84
85impl From<Arc<FileEncryptionProperties>> for ParquetEncryption {
86 fn from(properties: Arc<FileEncryptionProperties>) -> Self {
87 Self::new(properties)
88 }
89}
90
91#[derive(Clone)]
97pub struct ParquetDecryption(Arc<Box<dyn DecryptionPropertiesFactory>>);
98
99impl ParquetDecryption {
100 pub fn new(factory: impl DecryptionPropertiesFactory + 'static) -> Self {
102 Self(Arc::new(Box::new(factory)))
103 }
104
105 pub fn file_properties(&self) -> PolarsResult<Arc<FileDecryptionProperties>> {
107 self.0.file_decryption_properties()
108 }
109}
110
111impl From<Arc<FileDecryptionProperties>> for ParquetDecryption {
112 fn from(properties: Arc<FileDecryptionProperties>) -> Self {
113 Self::new(properties)
114 }
115}
116
117pub fn file_decryption_properties(
119 decryption: Option<&ParquetDecryption>,
120) -> PolarsResult<Option<Arc<FileDecryptionProperties>>> {
121 decryption
122 .map(ParquetDecryption::file_properties)
123 .transpose()
124}
125
126macro_rules! impl_opaque_traits {
128 ($t:ident, $description:literal) => {
129 impl Debug for $t {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 f.write_str(stringify!($t))
132 }
133 }
134
135 impl Eq for $t {}
136
137 impl PartialEq for $t {
138 fn eq(&self, other: &Self) -> bool {
139 Arc::ptr_eq(&self.0, &other.0)
140 }
141 }
142
143 impl Hash for $t {
144 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
145 state.write_usize(Arc::as_ptr(&self.0).cast::<()>() as usize)
146 }
147 }
148
149 #[cfg(feature = "serde")]
150 impl<'de> serde::Deserialize<'de> for $t {
151 fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
152 where
153 D: serde::Deserializer<'de>,
154 {
155 use serde::de::Error;
156 Err(D::Error::custom(concat!(
157 "cannot deserialize ",
158 $description
159 )))
160 }
161 }
162
163 #[cfg(feature = "serde")]
164 impl serde::Serialize for $t {
165 fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
166 where
167 S: serde::Serializer,
168 {
169 use serde::ser::Error;
170 Err(S::Error::custom(concat!("cannot serialize ", $description)))
171 }
172 }
173
174 #[cfg(feature = "dsl-schema")]
175 impl schemars::JsonSchema for $t {
176 fn schema_name() -> std::borrow::Cow<'static, str> {
177 stringify!($t).into()
178 }
179
180 fn schema_id() -> std::borrow::Cow<'static, str> {
181 std::borrow::Cow::Borrowed(concat!(module_path!(), "::", stringify!($t)))
182 }
183
184 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
185 Vec::<u8>::json_schema(generator)
186 }
187 }
188 };
189}
190
191impl_opaque_traits!(ParquetEncryption, "parquet encryption properties");
192impl_opaque_traits!(ParquetDecryption, "parquet decryption properties");
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 struct Factory;
199
200 impl DecryptionPropertiesFactory for Factory {
201 fn file_decryption_properties(&self) -> PolarsResult<Arc<FileDecryptionProperties>> {
202 Ok(FileDecryptionProperties::builder(b"0123456789012345".to_vec()).build()?)
203 }
204 }
205
206 #[test]
207 fn test_pointer_equality() {
208 let properties = FileDecryptionProperties::builder(b"0123456789012345".to_vec())
209 .build()
210 .unwrap();
211 let a = ParquetDecryption::from(Arc::clone(&properties));
212 assert_eq!(a, a.clone());
213 assert_ne!(a, ParquetDecryption::from(properties));
214
215 let b = ParquetDecryption::new(Factory);
216 assert_eq!(b, b.clone());
217 assert_ne!(b, ParquetDecryption::new(Factory));
218 }
219
220 #[test]
221 fn test_file_properties() {
222 let decryption = ParquetDecryption::new(Factory);
223 let a = decryption.file_properties().unwrap();
224 let b = decryption.file_properties().unwrap();
225 assert!(!Arc::ptr_eq(&a, &b));
226
227 let decryption = ParquetDecryption::from(Arc::clone(&a));
228 assert!(Arc::ptr_eq(&decryption.file_properties().unwrap(), &a));
229 }
230}