arrow-schema/src/extension/canonical/opaque.rs (41 lines of code) (raw):
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//! Opaque
//!
//! <https://arrow.apache.org/docs/format/CanonicalExtensions.html#opaque>
use serde::{Deserialize, Serialize};
use crate::{extension::ExtensionType, ArrowError, DataType};
/// The extension type for `Opaque`.
///
/// Extension name: `arrow.opaque`.
///
/// Opaque represents a type that an Arrow-based system received from an
/// external (often non-Arrow) system, but that it cannot interpret. In this
/// case, it can pass on Opaque to its clients to at least show that a field
/// exists and preserve metadata about the type from the other system.
///
/// The storage type of this extension is any type. If there is no underlying
/// data, the storage type should be Null.
#[derive(Debug, Clone, PartialEq)]
pub struct Opaque(OpaqueMetadata);
impl Opaque {
/// Returns a new `Opaque` extension type.
pub fn new(type_name: impl Into<String>, vendor_name: impl Into<String>) -> Self {
Self(OpaqueMetadata::new(type_name, vendor_name))
}
/// Returns the name of the unknown type in the external system.
pub fn type_name(&self) -> &str {
self.0.type_name()
}
/// Returns the name of the external system.
pub fn vendor_name(&self) -> &str {
self.0.vendor_name()
}
}
impl From<OpaqueMetadata> for Opaque {
fn from(value: OpaqueMetadata) -> Self {
Self(value)
}
}
/// Extension type metadata for [`Opaque`].
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct OpaqueMetadata {
/// Name of the unknown type in the external system.
type_name: String,
/// Name of the external system.
vendor_name: String,
}
impl OpaqueMetadata {
/// Returns a new `OpaqueMetadata`.
pub fn new(type_name: impl Into<String>, vendor_name: impl Into<String>) -> Self {
OpaqueMetadata {
type_name: type_name.into(),
vendor_name: vendor_name.into(),
}
}
/// Returns the name of the unknown type in the external system.
pub fn type_name(&self) -> &str {
&self.type_name
}
/// Returns the name of the external system.
pub fn vendor_name(&self) -> &str {
&self.vendor_name
}
}
impl ExtensionType for Opaque {
const NAME: &'static str = "arrow.opaque";
type Metadata = OpaqueMetadata;
fn metadata(&self) -> &Self::Metadata {
&self.0
}
fn serialize_metadata(&self) -> Option<String> {
Some(serde_json::to_string(self.metadata()).expect("metadata serialization"))
}
fn deserialize_metadata(metadata: Option<&str>) -> Result<Self::Metadata, ArrowError> {
metadata.map_or_else(
|| {
Err(ArrowError::InvalidArgumentError(
"Opaque extension types requires metadata".to_owned(),
))
},
|value| {
serde_json::from_str(value).map_err(|e| {
ArrowError::InvalidArgumentError(format!(
"Opaque metadata deserialization failed: {e}"
))
})
},
)
}
fn supports_data_type(&self, _data_type: &DataType) -> Result<(), ArrowError> {
// Any type
Ok(())
}
fn try_new(_data_type: &DataType, metadata: Self::Metadata) -> Result<Self, ArrowError> {
Ok(Self::from(metadata))
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "canonical_extension_types")]
use crate::extension::CanonicalExtensionType;
use crate::{
extension::{EXTENSION_TYPE_METADATA_KEY, EXTENSION_TYPE_NAME_KEY},
Field,
};
use super::*;
#[test]
fn valid() -> Result<(), ArrowError> {
let opaque = Opaque::new("name", "vendor");
let mut field = Field::new("", DataType::Null, false);
field.try_with_extension_type(opaque.clone())?;
assert_eq!(field.try_extension_type::<Opaque>()?, opaque);
#[cfg(feature = "canonical_extension_types")]
assert_eq!(
field.try_canonical_extension_type()?,
CanonicalExtensionType::Opaque(opaque)
);
Ok(())
}
#[test]
#[should_panic(expected = "Field extension type name missing")]
fn missing_name() {
let field = Field::new("", DataType::Null, false).with_metadata(
[(
EXTENSION_TYPE_METADATA_KEY.to_owned(),
r#"{ "type_name": "type", "vendor_name": "vendor" }"#.to_owned(),
)]
.into_iter()
.collect(),
);
field.extension_type::<Opaque>();
}
#[test]
#[should_panic(expected = "Opaque extension types requires metadata")]
fn missing_metadata() {
let field = Field::new("", DataType::Null, false).with_metadata(
[(EXTENSION_TYPE_NAME_KEY.to_owned(), Opaque::NAME.to_owned())]
.into_iter()
.collect(),
);
field.extension_type::<Opaque>();
}
#[test]
#[should_panic(
expected = "Opaque metadata deserialization failed: missing field `vendor_name`"
)]
fn invalid_metadata() {
let field = Field::new("", DataType::Null, false).with_metadata(
[
(EXTENSION_TYPE_NAME_KEY.to_owned(), Opaque::NAME.to_owned()),
(
EXTENSION_TYPE_METADATA_KEY.to_owned(),
r#"{ "type_name": "no-vendor" }"#.to_owned(),
),
]
.into_iter()
.collect(),
);
field.extension_type::<Opaque>();
}
}