Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 104 additions & 2 deletions src/parse/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use crate::parse::{
proto::extensions::SimpleExtensionUri, text::simple_extensions::SimpleExtensions, Anchor, Parse,
};

use super::proto::extensions::{ExtensionFunction, ExtensionType, ExtensionTypeVariation};

/// A parse context.
///
/// Parsing Substrait data is context-sensitive. This trait provides methods
Expand Down Expand Up @@ -38,6 +40,33 @@ pub trait Context {
&self,
anchor: &Anchor<SimpleExtensionUri>,
) -> Result<&SimpleExtensions, ContextError>;

/// Add a [ExtensionFunction] to this context. Must return an error for duplicate
/// anchors, when the URI is not supported or if the definition does not contain a matching function with the given name.
///
/// This function must eagerly resolve and parse the simple extension, returning an
/// error if either fails.
fn add_extension_function(
&mut self,
extension_function: &ExtensionFunction,
) -> Result<(), ContextError>;

/// Add an [ExtensionType] to this context. Must return an error for duplicate
/// anchors, when the URI is not supported or if the definition does not contain a matching function with the given name.
///
/// This function must eagerly resolve and parse the simple extension, returning an
/// error if either fails.
fn add_extension_type(&mut self, extension: &ExtensionType) -> Result<(), ContextError>;

/// Add a [ExtensionTypeVariation] to this context. Must return an error for duplicate
/// anchors, when the URI is not supported or if the definition does not contain a matching function with the given name.
///
/// This function must eagerly resolve and parse the simple extension, returning an
/// error if either fails.
fn add_extension_type_variation(
&mut self,
extension_type_variation: &ExtensionTypeVariation,
) -> Result<(), ContextError>;
}

/// Parse context errors.
Expand All @@ -51,6 +80,18 @@ pub enum ContextError {
#[error("duplicate anchor `{0}` for simple extension")]
DuplicateSimpleExtension(Anchor<SimpleExtensionUri>),

/// Duplicate anchor for [ExtensionType].
#[error("duplicate anchor `{0}` for extension_type")]
DuplicateExtensionType(Anchor<ExtensionType>),

/// Duplicate anchor for [ExtensionFunction].
#[error("duplicate anchor `{0}` for extension_function")]
DuplicateExtensionFunction(Anchor<ExtensionFunction>),

/// Duplicate anchor for [ExtensionTypeVariation].
#[error("duplicate anchor `{0}` for extension_type_variation")]
DuplicateExtensionTypeVariation(Anchor<ExtensionTypeVariation>),

/// Unsupported simple extension URI.
#[error("unsupported simple extension URI: {0}")]
UnsupportedURI(String),
Expand All @@ -61,8 +102,12 @@ pub(crate) mod tests {
use std::collections::{hash_map::Entry, HashMap};

use crate::parse::{
context::ContextError, proto::extensions::SimpleExtensionUri,
text::simple_extensions::SimpleExtensions, Anchor,
context::ContextError,
proto::extensions::{
ExtensionFunction, ExtensionType, ExtensionTypeVariation, SimpleExtensionUri,
},
text::simple_extensions::SimpleExtensions,
Anchor,
};

/// A test context.
Expand All @@ -72,13 +117,19 @@ pub(crate) mod tests {
pub struct Context {
empty_simple_extensions: SimpleExtensions,
simple_extensions: HashMap<Anchor<SimpleExtensionUri>, SimpleExtensionUri>,
extension_types: HashMap<Anchor<ExtensionType>, ExtensionType>,
extension_functions: HashMap<Anchor<ExtensionFunction>, ExtensionFunction>,
extension_type_variations: HashMap<Anchor<ExtensionTypeVariation>, ExtensionTypeVariation>,
}

impl Default for Context {
fn default() -> Self {
Self {
empty_simple_extensions: SimpleExtensions {},
simple_extensions: Default::default(),
extension_types: Default::default(),
extension_functions: Default::default(),
extension_type_variations: Default::default(),
}
}
}
Expand Down Expand Up @@ -118,5 +169,56 @@ pub(crate) mod tests {
.then_some(&self.empty_simple_extensions)
.ok_or(ContextError::UndefinedSimpleExtension(*anchor))
}

fn add_extension_function(
&mut self,
extension_function: &crate::parse::proto::extensions::ExtensionFunction,
) -> Result<(), ContextError> {
match self.extension_functions.entry(extension_function.anchor()) {
Entry::Occupied(_) => Err(ContextError::DuplicateExtensionFunction(
extension_function.anchor(),
)),
Entry::Vacant(entry) => {
entry.insert(extension_function.clone());

Ok(())
}
}
}

fn add_extension_type(
&mut self,
extension_type: &crate::parse::proto::extensions::ExtensionType,
) -> Result<(), ContextError> {
match self.extension_types.entry(extension_type.anchor()) {
Entry::Occupied(_) => Err(ContextError::DuplicateExtensionType(
extension_type.anchor(),
)),
Entry::Vacant(entry) => {
entry.insert(extension_type.clone());

Ok(())
}
}
}

fn add_extension_type_variation(
&mut self,
extension_type_variation: &crate::parse::proto::extensions::ExtensionTypeVariation,
) -> Result<(), ContextError> {
match self
.extension_type_variations
.entry(extension_type_variation.anchor())
{
Entry::Occupied(_) => Err(ContextError::DuplicateExtensionTypeVariation(
extension_type_variation.anchor(),
)),
Entry::Vacant(entry) => {
entry.insert(extension_type_variation.clone());

Ok(())
}
}
}
}
}
7 changes: 7 additions & 0 deletions src/parse/proto/extensions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,12 @@

//! Parsing of [proto::extensions] types.

mod simple_extension_declaration;
mod simple_extension_uri;

pub use simple_extension_declaration::declaration::SimpleExtensionDeclaration;
pub use simple_extension_declaration::extension_function::ExtensionFunction;
pub use simple_extension_declaration::extension_type::ExtensionType;
pub use simple_extension_declaration::extension_type_variation::ExtensionTypeVariation;
pub use simple_extension_declaration::mapping_type::MappingType;
pub use simple_extension_uri::SimpleExtensionUri;
101 changes: 101 additions & 0 deletions src/parse/proto/extensions/simple_extension_declaration/declaration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use crate::{
parse::{context::ContextError, Context, Parse},
proto,
};
use thiserror::Error;

use super::mapping_type::MappingType;

/// A parsed [`SimpleExtensionDeclaration`].
#[derive(Clone, Debug, PartialEq)]
pub struct SimpleExtensionDeclaration {
/// The underlying mapping type of this extension declaration
pub mapping_type: MappingType,
}

#[derive(Debug, PartialEq, Error)]
pub enum SimpleExtensionDeclarationError {
#[error("No Mapping Type specified on Extension Declaration.")]
MissingMappingType,

/// Context error
#[error(transparent)]
Context(#[from] ContextError),
}

impl<C: Context> Parse<C> for proto::extensions::SimpleExtensionDeclaration {
type Parsed = SimpleExtensionDeclaration;
type Error = SimpleExtensionDeclarationError;

fn parse(self, ctx: &mut C) -> Result<Self::Parsed, Self::Error> {
let proto::extensions::SimpleExtensionDeclaration { mapping_type } = self;

Ok(SimpleExtensionDeclaration {
mapping_type: mapping_type
.ok_or(SimpleExtensionDeclarationError::MissingMappingType)?
.parse(ctx)?,
})
}
}

impl From<SimpleExtensionDeclaration> for proto::extensions::SimpleExtensionDeclaration {
fn from(declaration: SimpleExtensionDeclaration) -> Self {
let SimpleExtensionDeclaration { mapping_type } = declaration;

proto::extensions::SimpleExtensionDeclaration {
mapping_type: Some(mapping_type.into()),
}
}
}

#[cfg(test)]
mod test {

use super::*;
use crate::parse::{
context::tests::Context, proto::extensions::ExtensionFunction, typed::Name, Anchor,
};

#[test]
fn parse_from_protobuf() -> Result<(), SimpleExtensionDeclarationError> {
let declaration = proto::extensions::SimpleExtensionDeclaration {
mapping_type: Some(
proto::extensions::simple_extension_declaration::MappingType::ExtensionFunction(
proto::extensions::simple_extension_declaration::ExtensionFunction {
extension_uri_reference: 1,
function_anchor: 1,
name: "test_name".to_string(),
},
),
),
};
let simple_extension = declaration.parse(&mut Context::default())?;

assert!(matches!(
simple_extension.mapping_type,
MappingType::ExtensionFunction(_)
));

Ok(())
}

#[test]
fn convert_from_parsed() {
let declaration = SimpleExtensionDeclaration {
mapping_type: MappingType::ExtensionFunction(ExtensionFunction {
anchor: Anchor::new(1),
name: Name::new("test".to_string()),
extension_uri_reference: Anchor::new(1),
}),
};

let protobuf_declaration = proto::extensions::SimpleExtensionDeclaration::from(declaration);

assert!(matches!(
protobuf_declaration
.mapping_type
.expect("No mapping_type returned from declaration conversion."),
proto::extensions::simple_extension_declaration::MappingType::ExtensionFunction(_)
));
}
}
Loading