substrait/parse/proto/extensions/
simple_extension_uri.rs1use thiserror::Error;
6use url::Url;
7
8use crate::{
9 parse::{context::ContextError, Anchor, Context, Parse},
10 proto,
11};
12
13#[derive(Clone, Debug, PartialEq)]
15pub struct SimpleExtensionUri {
16 uri: Url,
18
19 anchor: Anchor<Self>,
21}
22
23impl SimpleExtensionUri {
24 pub fn uri(&self) -> &Url {
28 &self.uri
29 }
30
31 pub fn anchor(&self) -> Anchor<Self> {
35 self.anchor
36 }
37}
38
39#[derive(Debug, Error, PartialEq)]
41pub enum SimpleExtensionUriError {
42 #[error("invalid URI: {0}")]
44 InvalidURI(#[from] url::ParseError),
45
46 #[error(transparent)]
48 Context(#[from] ContextError),
49}
50
51impl<C: Context> Parse<C> for proto::extensions::SimpleExtensionUri {
52 type Parsed = SimpleExtensionUri;
53 type Error = SimpleExtensionUriError;
54
55 fn parse(self, ctx: &mut C) -> Result<Self::Parsed, Self::Error> {
56 let proto::extensions::SimpleExtensionUri {
57 extension_uri_anchor: anchor,
58 uri,
59 } = self;
60
61 let uri = Url::parse(&uri)?;
63
64 let simple_extension_uri = SimpleExtensionUri {
66 uri,
67 anchor: Anchor::new(anchor),
68 };
69
70 ctx.add_simple_extension_uri(&simple_extension_uri)?;
73
74 Ok(simple_extension_uri)
75 }
76}
77
78impl From<SimpleExtensionUri> for proto::extensions::SimpleExtensionUri {
79 fn from(simple_extension_uri: SimpleExtensionUri) -> Self {
80 let SimpleExtensionUri { uri, anchor } = simple_extension_uri;
81 proto::extensions::SimpleExtensionUri {
82 uri: uri.to_string(),
83 extension_uri_anchor: anchor.into_inner(),
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use crate::parse::{context::tests::Context, Context as _};
92
93 #[test]
94 fn parse() -> Result<(), SimpleExtensionUriError> {
95 let simple_extension_uri = proto::extensions::SimpleExtensionUri {
96 extension_uri_anchor: 1,
97 uri: "https://substrait.io".to_string(),
98 };
99 let simple_extension_uri = simple_extension_uri.parse(&mut Context::default())?;
100 assert_eq!(simple_extension_uri.anchor(), Anchor::new(1));
101 assert_eq!(simple_extension_uri.uri().as_str(), "https://substrait.io/");
102 Ok(())
103 }
104
105 #[test]
106 fn invalid_uri() {
107 let simple_extension_uri = proto::extensions::SimpleExtensionUri::default();
108 assert_eq!(
109 simple_extension_uri.parse(&mut Context::default()),
110 Err(SimpleExtensionUriError::InvalidURI(
111 url::ParseError::RelativeUrlWithoutBase
112 ))
113 );
114 let simple_extension_uri = proto::extensions::SimpleExtensionUri {
115 extension_uri_anchor: 1,
116 uri: "http://".to_string(),
117 };
118 assert_eq!(
119 simple_extension_uri.parse(&mut Context::default()),
120 Err(SimpleExtensionUriError::InvalidURI(
121 url::ParseError::EmptyHost
122 ))
123 );
124 }
125
126 #[test]
127 fn duplicate_simple_extension() {
128 let mut ctx = Context::default();
129 let simple_extension_uri = proto::extensions::SimpleExtensionUri {
130 extension_uri_anchor: 1,
131 uri: "https://substrait.io".to_string(),
132 };
133 assert!(ctx.parse(simple_extension_uri.clone()).is_ok());
134 assert_eq!(
135 ctx.parse(simple_extension_uri),
136 Err(SimpleExtensionUriError::Context(
137 ContextError::DuplicateSimpleExtension(Anchor::new(1))
138 ))
139 );
140 }
141
142 #[test]
143 fn unsupported_uri() {
144 let simple_extension_uri = proto::extensions::SimpleExtensionUri {
145 extension_uri_anchor: 1,
146 uri: "ftp://substrait.io".to_string(),
147 };
148 assert_eq!(
149 simple_extension_uri.parse(&mut Context::default()),
150 Err(SimpleExtensionUriError::Context(
151 ContextError::UnsupportedURI("`ftp` scheme not supported".to_string())
152 ))
153 );
154 }
155}