arrow_schema/extension/canonical/
opaque.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Opaque
19//!
20//! <https://arrow.apache.org/docs/format/CanonicalExtensions.html#opaque>
21
22use serde::{Deserialize, Serialize};
23
24use crate::{extension::ExtensionType, ArrowError, DataType};
25
26/// The extension type for `Opaque`.
27///
28/// Extension name: `arrow.opaque`.
29///
30/// Opaque represents a type that an Arrow-based system received from an
31/// external (often non-Arrow) system, but that it cannot interpret. In this
32/// case, it can pass on Opaque to its clients to at least show that a field
33/// exists and preserve metadata about the type from the other system.
34///
35/// The storage type of this extension is any type. If there is no underlying
36/// data, the storage type should be Null.
37#[derive(Debug, Clone, PartialEq)]
38pub struct Opaque(OpaqueMetadata);
39
40impl Opaque {
41    /// Returns a new `Opaque` extension type.
42    pub fn new(type_name: impl Into<String>, vendor_name: impl Into<String>) -> Self {
43        Self(OpaqueMetadata::new(type_name, vendor_name))
44    }
45
46    /// Returns the name of the unknown type in the external system.
47    pub fn type_name(&self) -> &str {
48        self.0.type_name()
49    }
50
51    /// Returns the name of the external system.
52    pub fn vendor_name(&self) -> &str {
53        self.0.vendor_name()
54    }
55}
56
57impl From<OpaqueMetadata> for Opaque {
58    fn from(value: OpaqueMetadata) -> Self {
59        Self(value)
60    }
61}
62
63/// Extension type metadata for [`Opaque`].
64#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
65pub struct OpaqueMetadata {
66    /// Name of the unknown type in the external system.
67    type_name: String,
68
69    /// Name of the external system.
70    vendor_name: String,
71}
72
73impl OpaqueMetadata {
74    /// Returns a new `OpaqueMetadata`.
75    pub fn new(type_name: impl Into<String>, vendor_name: impl Into<String>) -> Self {
76        OpaqueMetadata {
77            type_name: type_name.into(),
78            vendor_name: vendor_name.into(),
79        }
80    }
81
82    /// Returns the name of the unknown type in the external system.
83    pub fn type_name(&self) -> &str {
84        &self.type_name
85    }
86
87    /// Returns the name of the external system.
88    pub fn vendor_name(&self) -> &str {
89        &self.vendor_name
90    }
91}
92
93impl ExtensionType for Opaque {
94    const NAME: &'static str = "arrow.opaque";
95
96    type Metadata = OpaqueMetadata;
97
98    fn metadata(&self) -> &Self::Metadata {
99        &self.0
100    }
101
102    fn serialize_metadata(&self) -> Option<String> {
103        Some(serde_json::to_string(self.metadata()).expect("metadata serialization"))
104    }
105
106    fn deserialize_metadata(metadata: Option<&str>) -> Result<Self::Metadata, ArrowError> {
107        metadata.map_or_else(
108            || {
109                Err(ArrowError::InvalidArgumentError(
110                    "Opaque extension types requires metadata".to_owned(),
111                ))
112            },
113            |value| {
114                serde_json::from_str(value).map_err(|e| {
115                    ArrowError::InvalidArgumentError(format!(
116                        "Opaque metadata deserialization failed: {e}"
117                    ))
118                })
119            },
120        )
121    }
122
123    fn supports_data_type(&self, _data_type: &DataType) -> Result<(), ArrowError> {
124        // Any type
125        Ok(())
126    }
127
128    fn try_new(_data_type: &DataType, metadata: Self::Metadata) -> Result<Self, ArrowError> {
129        Ok(Self::from(metadata))
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    #[cfg(feature = "canonical_extension_types")]
136    use crate::extension::CanonicalExtensionType;
137    use crate::{
138        extension::{EXTENSION_TYPE_METADATA_KEY, EXTENSION_TYPE_NAME_KEY},
139        Field,
140    };
141
142    use super::*;
143
144    #[test]
145    fn valid() -> Result<(), ArrowError> {
146        let opaque = Opaque::new("name", "vendor");
147        let mut field = Field::new("", DataType::Null, false);
148        field.try_with_extension_type(opaque.clone())?;
149        assert_eq!(field.try_extension_type::<Opaque>()?, opaque);
150        #[cfg(feature = "canonical_extension_types")]
151        assert_eq!(
152            field.try_canonical_extension_type()?,
153            CanonicalExtensionType::Opaque(opaque)
154        );
155        Ok(())
156    }
157
158    #[test]
159    #[should_panic(expected = "Field extension type name missing")]
160    fn missing_name() {
161        let field = Field::new("", DataType::Null, false).with_metadata(
162            [(
163                EXTENSION_TYPE_METADATA_KEY.to_owned(),
164                r#"{ "type_name": "type", "vendor_name": "vendor" }"#.to_owned(),
165            )]
166            .into_iter()
167            .collect(),
168        );
169        field.extension_type::<Opaque>();
170    }
171
172    #[test]
173    #[should_panic(expected = "Opaque extension types requires metadata")]
174    fn missing_metadata() {
175        let field = Field::new("", DataType::Null, false).with_metadata(
176            [(EXTENSION_TYPE_NAME_KEY.to_owned(), Opaque::NAME.to_owned())]
177                .into_iter()
178                .collect(),
179        );
180        field.extension_type::<Opaque>();
181    }
182
183    #[test]
184    #[should_panic(
185        expected = "Opaque metadata deserialization failed: missing field `vendor_name`"
186    )]
187    fn invalid_metadata() {
188        let field = Field::new("", DataType::Null, false).with_metadata(
189            [
190                (EXTENSION_TYPE_NAME_KEY.to_owned(), Opaque::NAME.to_owned()),
191                (
192                    EXTENSION_TYPE_METADATA_KEY.to_owned(),
193                    r#"{ "type_name": "no-vendor" }"#.to_owned(),
194                ),
195            ]
196            .into_iter()
197            .collect(),
198        );
199        field.extension_type::<Opaque>();
200    }
201}