@@ -140,85 +140,211 @@ pub fn contains(This(this): This<Value>, arg: Value) -> Result<Value> {
140
140
. into ( ) )
141
141
}
142
142
143
- // Performs a type conversion on the target. The following conversions are currently
144
- // supported:
145
- // * `string` - Returns a copy of the target string.
146
- // * `timestamp` - Returns the timestamp in RFC3339 format.
147
- // * `duration` - Returns the duration in a string formatted like "72h3m0.5s".
148
- // * `int` - Returns the integer value of the target.
149
- // * `uint` - Returns the unsigned integer value of the target.
150
- // * `float` - Returns the float value of the target.
151
- // * `bytes` - Converts bytes to string using from_utf8_lossy.
143
+ macro_rules! return_external_conversion {
144
+ ( $v: ident as $ty: ident) => { {
145
+ let $v = unsafe { $v. as_value_unchecked( ) } ;
146
+ if let Some ( it) = $v. to_value( ValueType :: $ty) {
147
+ if matches!( it, Value :: $ty( _) ) {
148
+ return Ok ( it) ;
149
+ }
150
+ }
151
+ } } ;
152
+ }
153
+
154
+ /// Converts provided value into a [`string`][Value::String].
155
+ ///
156
+ /// # Supported Conversions
157
+ ///
158
+ /// - [`string`][Value::String] - will be returned as is.
159
+ /// - [`timestamp`][Value::Timestamp] - will be formatted into RFC3339 format.
160
+ /// - [`duration`][Value::Duration] - will be formatted to string representation
161
+ /// (e.g. "72h3m0.5s").
162
+ /// - [`int`][Value::Int] - will be formatted into a string.
163
+ /// - [`uint`][Value::UInt] - will be formatted into a string.
164
+ /// - [`float`][Value::Float] - will be formatted into a string.
165
+ /// - [`bytes`][Value::Bytes] - will be converted to string using
166
+ /// [`from_utf8_lossy`][String::from_utf8_lossy].
167
+ /// - [`external`][Value::External] - will be converted to
168
+ /// [`string`][Value::String] if it's not opaque (i.e. implements
169
+ /// [`AsValue`][crate::external::AsValue]), and value returned by
170
+ /// [`to_value(ValueType::String)`][crate::external::AsValue::to_value] is
171
+ /// [`string`][Value::String]. [`AsValue`][crate::external::AsValue]) and the
172
+ /// returned value is a [`string`][Value::String].
152
173
pub fn string ( ftx : & FunctionContext , This ( this) : This < Value > ) -> Result < Value > {
153
- Ok ( match this {
154
- Value :: String ( v) => Value :: String ( v. clone ( ) ) ,
155
- Value :: Timestamp ( t) => Value :: String ( t. to_rfc3339 ( ) . into ( ) ) ,
156
- Value :: Duration ( v) => Value :: String ( format_duration ( & v) . into ( ) ) ,
157
- Value :: Int ( v) => Value :: String ( v. to_string ( ) . into ( ) ) ,
158
- Value :: UInt ( v) => Value :: String ( v. to_string ( ) . into ( ) ) ,
159
- Value :: Float ( v) => Value :: String ( v. to_string ( ) . into ( ) ) ,
160
- Value :: Bytes ( v) => Value :: String ( Arc :: new ( String :: from_utf8_lossy ( v. as_slice ( ) ) . into ( ) ) ) ,
161
- v => return Err ( ftx. error ( format ! ( "cannot convert {:?} to string" , v) ) ) ,
162
- } )
174
+ match & this {
175
+ Value :: String ( v) => return Ok ( Value :: String ( v. clone ( ) ) ) ,
176
+ Value :: Timestamp ( t) => return Ok ( Value :: String ( t. to_rfc3339 ( ) . into ( ) ) ) ,
177
+ Value :: Duration ( v) => return Ok ( Value :: String ( format_duration ( & v) . into ( ) ) ) ,
178
+ Value :: Int ( v) => return Ok ( Value :: String ( v. to_string ( ) . into ( ) ) ) ,
179
+ Value :: UInt ( v) => return Ok ( Value :: String ( v. to_string ( ) . into ( ) ) ) ,
180
+ Value :: Float ( v) => return Ok ( Value :: String ( v. to_string ( ) . into ( ) ) ) ,
181
+ Value :: Bytes ( v) => {
182
+ return Ok ( Value :: String ( Arc :: new (
183
+ String :: from_utf8_lossy ( v. as_slice ( ) ) . into ( ) ,
184
+ ) ) )
185
+ }
186
+ Value :: External ( v) if !v. is_opaque ( ) => {
187
+ return_external_conversion ! ( v as String ) ;
188
+ }
189
+ _ => { }
190
+ }
191
+
192
+ Err ( ftx. error ( format ! ( "cannot convert {:?} to string" , this) ) )
163
193
}
164
194
165
- pub fn bytes ( value : Arc < String > ) -> Result < Value > {
166
- Ok ( Value :: Bytes ( value. as_bytes ( ) . to_vec ( ) . into ( ) ) )
195
+ /// Converts provided value into [`bytes`][Value::Bytes].
196
+ ///
197
+ /// # Supported Conversions
198
+ ///
199
+ /// - [`bytes`][Value::Bytes] - will be returned as is.
200
+ /// - [`string`][Value::String] - will return UTF8 bytes.
201
+ /// - [`external`][Value::External] - will be converted to
202
+ /// [`bytes`][Value::Bytes] if it's not opaque (i.e. implements
203
+ /// [`AsValue`][crate::external::AsValue]), and value returned by
204
+ /// [`to_value(ValueType::Bytes)`][crate::external::AsValue::to_value] is
205
+ /// [`bytes`][Value::Bytes].
206
+ pub fn bytes ( Arguments ( args) : Arguments ) -> Result < Value > {
207
+ if args. len ( ) > 1 {
208
+ return Err ( ExecutionError :: InvalidArgumentCount {
209
+ expected : 1 ,
210
+ actual : args. len ( ) ,
211
+ } ) ;
212
+ }
213
+ let value = match args. get ( 0 ) {
214
+ Some ( it) => it,
215
+ None => return Err ( ExecutionError :: MissingArgumentOrTarget ) ,
216
+ } ;
217
+ match value {
218
+ Value :: Bytes ( v) => return Ok ( Value :: Bytes ( v. clone ( ) ) ) ,
219
+ Value :: String ( v) => return Ok ( Value :: Bytes ( v. as_bytes ( ) . to_vec ( ) . into ( ) ) ) ,
220
+ Value :: External ( v) if !v. is_opaque ( ) => {
221
+ return_external_conversion ! ( v as Bytes ) ;
222
+ }
223
+ _ => { }
224
+ }
225
+
226
+ Err ( ExecutionError :: UnexpectedType {
227
+ got : value. type_of ( ) . to_string ( ) ,
228
+ want : "`string` or `external` convertible to `bytes`" . to_string ( ) ,
229
+ } )
167
230
}
168
231
169
- // Performs a type conversion on the target.
232
+ /// Converts provided value into [`float`][Value::Float].
233
+ ///
234
+ /// # Supported Conversions
235
+ ///
236
+ /// - [`string`][Value::String] - will be parsed as `f64`.
237
+ /// - [`float`][Value::Float] - will be returned as is.
238
+ /// - [`int`][Value::Int] - will be cast into a float.
239
+ /// - [`uint`][Value::UInt] - will be cast into a float.
240
+ /// - [`external`][Value::External] - will be converted to
241
+ /// [`float`][Value::Float] if it's not opaque (i.e. implements
242
+ /// [`AsValue`][crate::external::AsValue]), and value returned by
243
+ /// [`to_value(ValueType::Float)`][crate::external::AsValue::to_value] is
244
+ /// a [`float`][Value::Float].
170
245
pub fn double ( ftx : & FunctionContext , This ( this) : This < Value > ) -> Result < Value > {
171
- Ok ( match this {
172
- Value :: String ( v) => v
173
- . parse :: < f64 > ( )
174
- . map ( Value :: Float )
175
- . map_err ( |e| ftx. error ( format ! ( "string parse error: {e}" ) ) ) ?,
176
- Value :: Float ( v) => Value :: Float ( v) ,
177
- Value :: Int ( v) => Value :: Float ( v as f64 ) ,
178
- Value :: UInt ( v) => Value :: Float ( v as f64 ) ,
179
- v => return Err ( ftx. error ( format ! ( "cannot convert {:?} to double" , v) ) ) ,
180
- } )
246
+ match & this {
247
+ Value :: Float ( v) => return Ok ( Value :: Float ( * v) ) ,
248
+ Value :: String ( v) => {
249
+ return v
250
+ . parse :: < f64 > ( )
251
+ . map ( Value :: Float )
252
+ . map_err ( |e| ftx. error ( format ! ( "string parse error: {e}" ) ) )
253
+ }
254
+ Value :: Int ( v) => return Ok ( Value :: Float ( * v as f64 ) ) ,
255
+ Value :: UInt ( v) => return Ok ( Value :: Float ( * v as f64 ) ) ,
256
+ Value :: External ( v) if !v. is_opaque ( ) => {
257
+ return_external_conversion ! ( v as Float ) ;
258
+ }
259
+ _ => { }
260
+ }
261
+
262
+ Err ( ftx. error ( format ! ( "cannot convert {:?} to float" , this) ) )
181
263
}
182
264
183
- // Performs a type conversion on the target.
265
+ /// Converts provided value into [`uint`][Value::UInt].
266
+ ///
267
+ /// # Supported Conversions
268
+ ///
269
+ /// - [`string`][Value::String] - will be parsed as `u64`.
270
+ /// - [`float`][Value::Float] - will be cast if there's no overflow.
271
+ /// - [`int`][Value::Int] - will be cast if there's no overflow.
272
+ /// - [`uint`][Value::UInt] - will be returned as is.
273
+ /// - [`external`][Value::External] - will be converted to [`uint`][Value::UInt]
274
+ /// if it's not opaque (i.e. implements
275
+ /// [`AsValue`][crate::external::AsValue]), and value returned by
276
+ /// [`to_value(ValueType::Float)`][crate::external::AsValue::to_value] is
277
+ /// an [`uint`][Value::UInt].
184
278
pub fn uint ( ftx : & FunctionContext , This ( this) : This < Value > ) -> Result < Value > {
185
- Ok ( match this {
186
- Value :: String ( v) => v
187
- . parse :: < u64 > ( )
188
- . map ( Value :: UInt )
189
- . map_err ( |e| ftx. error ( format ! ( "string parse error: {e}" ) ) ) ?,
279
+ match & this {
280
+ Value :: UInt ( v) => return Ok ( Value :: UInt ( * v) ) ,
281
+ Value :: String ( v) => {
282
+ return v
283
+ . parse :: < u64 > ( )
284
+ . map ( Value :: UInt )
285
+ . map_err ( |e| ftx. error ( format ! ( "string parse error: {e}" ) ) )
286
+ }
190
287
Value :: Float ( v) => {
191
- if v > u64:: MAX as f64 || v < u64:: MIN as f64 {
288
+ if * v > u64:: MAX as f64 || * v < u64:: MIN as f64 {
192
289
return Err ( ftx. error ( "unsigned integer overflow" ) ) ;
193
290
}
194
- Value :: UInt ( v as u64 )
291
+ return Ok ( Value :: UInt ( * v as u64 ) ) ;
195
292
}
196
- Value :: Int ( v) => Value :: UInt (
197
- v. try_into ( )
198
- . map_err ( |_| ftx. error ( "unsigned integer overflow" ) ) ?,
199
- ) ,
200
- Value :: UInt ( v) => Value :: UInt ( v) ,
201
- v => return Err ( ftx. error ( format ! ( "cannot convert {:?} to uint" , v) ) ) ,
202
- } )
293
+ Value :: Int ( v) => {
294
+ return ( * v)
295
+ . try_into ( )
296
+ . map ( Value :: UInt )
297
+ . map_err ( |_| ftx. error ( "unsigned integer overflow" ) )
298
+ }
299
+ Value :: External ( v) if !v. is_opaque ( ) => {
300
+ return_external_conversion ! ( v as UInt ) ;
301
+ }
302
+ _ => { }
303
+ }
304
+
305
+ Err ( ftx. error ( format ! ( "cannot convert {:?} to uint" , this) ) )
203
306
}
204
307
205
- // Performs a type conversion on the target.
308
+ /// Converts provided value into [`int`][Value::Int].
309
+ ///
310
+ /// # Supported Conversions
311
+ ///
312
+ /// - [`string`][Value::String] - will be parsed as `i64`.
313
+ /// - [`float`][Value::Float] - will be cast if there's no overflow.
314
+ /// - [`int`][Value::Int] - will be returned as is.
315
+ /// - [`uint`][Value::UInt] - will be cast if there's no overflow.
316
+ /// - [`external`][Value::External] - Will be converted to [`int`][Value::Int]
317
+ /// if it's not opaque (i.e. implements
318
+ /// [`AsValue`][crate::external::AsValue]), and value returned by
319
+ /// [`to_value(ValueType::Int)`][crate::external::AsValue::to_value] is
320
+ /// an [`int`][Value::Int].
206
321
pub fn int ( ftx : & FunctionContext , This ( this) : This < Value > ) -> Result < Value > {
207
- Ok ( match this {
208
- Value :: String ( v) => v
209
- . parse :: < i64 > ( )
210
- . map ( Value :: Int )
211
- . map_err ( |e| ftx. error ( format ! ( "string parse error: {e}" ) ) ) ?,
322
+ match & this {
323
+ Value :: Int ( v) => return Ok ( Value :: Int ( * v) ) ,
324
+ Value :: String ( v) => {
325
+ return v
326
+ . parse :: < i64 > ( )
327
+ . map ( Value :: Int )
328
+ . map_err ( |e| ftx. error ( format ! ( "string parse error: {e}" ) ) )
329
+ }
212
330
Value :: Float ( v) => {
213
- if v > i64:: MAX as f64 || v < i64:: MIN as f64 {
331
+ if * v > i64:: MAX as f64 || * v < i64:: MIN as f64 {
214
332
return Err ( ftx. error ( "integer overflow" ) ) ;
215
333
}
216
- Value :: Int ( v as i64 )
334
+ return Ok ( Value :: Int ( * v as i64 ) ) ;
217
335
}
218
- Value :: Int ( v) => Value :: Int ( v) ,
219
- Value :: UInt ( v) => Value :: Int ( v. try_into ( ) . map_err ( |_| ftx. error ( "integer overflow" ) ) ?) ,
220
- v => return Err ( ftx. error ( format ! ( "cannot convert {:?} to int" , v) ) ) ,
221
- } )
336
+ Value :: UInt ( v) => {
337
+ return TryInto :: < i64 > :: try_into ( * v)
338
+ . map ( Value :: Int )
339
+ . map_err ( |_| ftx. error ( "integer overflow" ) )
340
+ }
341
+ Value :: External ( v) if !v. is_opaque ( ) => {
342
+ return_external_conversion ! ( v as Int ) ;
343
+ }
344
+ _ => { }
345
+ }
346
+
347
+ Err ( ftx. error ( format ! ( "cannot convert {:?} to int" , this) ) )
222
348
}
223
349
224
350
/// Returns true if a string starts with another string.
@@ -485,12 +611,21 @@ pub fn exists_one(
485
611
}
486
612
}
487
613
488
- /// Duration parses the provided argument into a [`Value::Duration`] value.
489
- /// The argument must be string, and must be in the format of a duration. See
490
- /// the [`parse_duration`] documentation for more information on the supported
491
- /// formats.
614
+ /// Duration converts the provided argument into a
615
+ /// [`duration`][Value::Duration].
616
+ ///
617
+ /// # Supported Conversions
618
+ ///
619
+ /// - [`duration`][Value::Duration] - will be returned as is.
620
+ /// - [`string`][Value::String] - will be parsed with [`parse_duration`].
621
+ /// - [`external`][Value::External] - will be converted to bytes if it's not
622
+ /// opaque (i.e. implements [`AsValue`][crate::external::AsValue]), and value
623
+ /// returned by
624
+ /// [`to_value(ValueType::Duration)`][crate::external::AsValue::to_value] is a
625
+ /// [`duration`][Value::Duration].
626
+ ///
627
+ /// # Parsing Result Examples
492
628
///
493
- /// # Examples
494
629
/// - `1h` parses as 1 hour
495
630
/// - `1.5h` parses as 1 hour and 30 minutes
496
631
/// - `1h30m` parses as 1 hour and 30 minutes
@@ -499,17 +634,74 @@ pub fn exists_one(
499
634
/// - `1.5ms` parses as 1 millisecond and 500 microseconds
500
635
/// - `1ns` parses as 1 nanosecond
501
636
/// - `1.5ns` parses as 1 nanosecond (sub-nanosecond durations not supported)
502
- pub fn duration ( value : Arc < String > ) -> Result < Value > {
503
- Ok ( Value :: Duration ( _duration ( value. as_str ( ) ) ?) )
637
+ pub fn duration ( Arguments ( args) : Arguments ) -> Result < Value > {
638
+ if args. len ( ) > 1 {
639
+ return Err ( ExecutionError :: InvalidArgumentCount {
640
+ expected : 1 ,
641
+ actual : args. len ( ) ,
642
+ } ) ;
643
+ }
644
+ let value = match args. get ( 0 ) {
645
+ Some ( it) => it,
646
+ None => return Err ( ExecutionError :: MissingArgumentOrTarget ) ,
647
+ } ;
648
+ match value {
649
+ Value :: Duration ( value) => return Ok ( Value :: Duration ( * value) ) ,
650
+ Value :: String ( value) => return Ok ( Value :: Duration ( _duration ( value. as_str ( ) ) ?) ) ,
651
+ Value :: External ( external) if !external. is_opaque ( ) => {
652
+ return_external_conversion ! ( external as Duration ) ;
653
+ }
654
+ _ => { }
655
+ }
656
+
657
+ Err ( ExecutionError :: UnexpectedType {
658
+ got : value. type_of ( ) . to_string ( ) ,
659
+ want : "`string` or `external` convertible to `duration`" . to_string ( ) ,
660
+ } )
504
661
}
505
662
506
- /// Timestamp parses the provided argument into a [`Value::Timestamp`] value.
507
- /// The
508
- pub fn timestamp ( value : Arc < String > ) -> Result < Value > {
509
- Ok ( Value :: Timestamp (
510
- DateTime :: parse_from_rfc3339 ( value. as_str ( ) )
511
- . map_err ( |e| ExecutionError :: function_error ( "timestamp" , e. to_string ( ) . as_str ( ) ) ) ?,
512
- ) )
663
+ /// Timestamp converts the provided argument into a
664
+ /// [`timestamp`][Value::Timestamp].
665
+ ///
666
+ /// # Supported Conversions
667
+ ///
668
+ /// - [`timestamp`][Value::Timestamp] - will be returned as is.
669
+ /// - [`string`][Value::String] - will be parsed as RFC3339 formatted timestamp.
670
+ /// - [`external`][Value::External] - will be converted to
671
+ /// [`timestamp`][Value::Timestamp] if it's not opaque (i.e. implements
672
+ /// [`AsValue`][crate::external::AsValue]), and value returned by
673
+ /// [`to_value(ValueType::Timestamp)`][crate::external::AsValue::to_value] is
674
+ /// a [`timestamp`][Value::Timestamp].
675
+ pub fn timestamp ( Arguments ( args) : Arguments ) -> Result < Value > {
676
+ if args. len ( ) > 1 {
677
+ return Err ( ExecutionError :: InvalidArgumentCount {
678
+ expected : 1 ,
679
+ actual : args. len ( ) ,
680
+ } ) ;
681
+ }
682
+ let value = match args. get ( 0 ) {
683
+ Some ( it) => it,
684
+ None => return Err ( ExecutionError :: MissingArgumentOrTarget ) ,
685
+ } ;
686
+ match value {
687
+ Value :: Timestamp ( value) => return Ok ( Value :: Timestamp ( * value) ) ,
688
+ Value :: String ( value) => {
689
+ return Ok ( Value :: Timestamp (
690
+ DateTime :: parse_from_rfc3339 ( value. as_str ( ) ) . map_err ( |e| {
691
+ ExecutionError :: function_error ( "timestamp" , e. to_string ( ) . as_str ( ) )
692
+ } ) ?,
693
+ ) )
694
+ }
695
+ Value :: External ( external) if !external. is_opaque ( ) => {
696
+ return_external_conversion ! ( external as Timestamp ) ;
697
+ }
698
+ _ => { }
699
+ }
700
+
701
+ Err ( ExecutionError :: UnexpectedType {
702
+ got : value. type_of ( ) . to_string ( ) ,
703
+ want : "`string` or `external` convertible to `timestamp`" . to_string ( ) ,
704
+ } )
513
705
}
514
706
515
707
pub fn max ( Arguments ( args) : Arguments ) -> Result < Value > {
0 commit comments