Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(parse): add proto::extensions::SimpleExtensionDeclaration parser #170

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d1ecc3e
Save work
ilikepi63 Apr 4, 2024
68babd8
Update src/parse/proto/extensions/simple_extension_declaration/extens…
ilikepi63 Apr 8, 2024
759847f
Update src/parse/proto/extensions/simple_extension_declaration/extens…
ilikepi63 Apr 8, 2024
0d143a3
Update src/parse/proto/extensions/simple_extension_declaration/extens…
ilikepi63 Apr 8, 2024
cfe8c77
Update src/parse/proto/extensions/simple_extension_declaration/extens…
ilikepi63 Apr 8, 2024
91d7724
Update src/parse/proto/extensions/simple_extension_declaration/extens…
ilikepi63 Apr 8, 2024
c6e7e2c
Update src/parse/proto/extensions/simple_extension_declaration/extens…
ilikepi63 Apr 8, 2024
165568c
Update src/parse/proto/extensions/simple_extension_declaration/extens…
ilikepi63 Apr 8, 2024
f05ea37
Update src/parse/proto/extensions/simple_extension_declaration/mappin…
ilikepi63 Apr 8, 2024
204bc94
Update src/parse/proto/extensions/simple_extension_declaration/simple…
ilikepi63 Apr 8, 2024
dd1d796
Update src/parse/proto/extensions/simple_extension_declaration/mod.rs
ilikepi63 Apr 8, 2024
4f56530
Save work
ilikepi63 Apr 8, 2024
b62e0e3
Merge branch 'main' into main
ilikepi63 Apr 9, 2024
a983a4c
Merge branch 'main' into main
ilikepi63 May 2, 2024
b424a1e
Update src/parse/proto/extensions/simple_extension_declaration/simple…
ilikepi63 May 2, 2024
00aa50d
Update src/parse/proto/extensions/simple_extension_declaration/simple…
ilikepi63 May 2, 2024
fcc9797
Save work
ilikepi63 May 2, 2024
013b50c
Last bits of tests
ilikepi63 May 5, 2024
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
Loading