Skip to main content

google_cloud_bigquery/query/
row.rs

1// Copyright 2026 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::error::{ConvertError, RowError};
16use crate::query::from_sql::SqlValueInner;
17use crate::query::{FromSql, Schema};
18use google_cloud_bigquery_v2::model::TableFieldSchema;
19use std::sync::Arc;
20use wkt::{ListValue, Struct, Value};
21
22pub type Result<T> = std::result::Result<T, RowError>;
23
24/// A container for a single row within a query result set.
25///
26/// [`RowIterator::next()`](crate::query::RowIterator::next) yields a `Row`.
27///
28/// Each `Row` contains parsed cell values and a reference to the table schema.
29///
30/// # Zero-Copy Struct Mapping via Derive Macros
31///
32/// Define typed structs with `#[derive(FromRow)]` to convert rows directly into
33/// your domain types using `TryFrom<Row>` without unnecessary allocations:
34///
35/// ```
36/// # use google_cloud_bigquery::query::{Row, FromRow};
37/// #[derive(FromRow, Debug)]
38/// struct UserStats {
39///     name: String,
40///     count: i64,
41/// }
42///
43/// # fn sample(row: Row) -> anyhow::Result<()> {
44/// let user: UserStats = row.try_into()?;
45/// println!("{}: {}", user.name, user.count);
46/// # Ok(())
47/// # }
48/// ```
49///
50/// # Field Extraction by Name or Index
51///
52/// Retrieve individual cell values by column name (`&str`) or index (`usize`)
53/// using [`get()`](Row::get) or [`take()`](Row::take):
54///
55/// ```
56/// # use google_cloud_bigquery::query::Row;
57/// # fn sample(row: Row) -> anyhow::Result<()> {
58/// let name: String = row.get("name")?;
59/// let age: i64 = row.get(1)?;
60/// println!("{name} is {age} years old");
61/// # Ok(())
62/// # }
63/// ```
64#[derive(Clone, Debug)]
65pub struct Row {
66    pub(crate) values: Vec<SqlValueInner>,
67    pub(crate) schema: Arc<Schema>,
68}
69
70mod sealed {
71    use super::{Row, SqlValueInner};
72    use crate::error::ConvertError;
73    use crate::query::SqlValue;
74
75    /// A sealed trait to prevent external implementation of `ColumnIndex`.
76    pub trait ColumnIndex {
77        /// Returns the index of the column in the given row, if it exists.
78        fn index(&self, row: &Row) -> Option<usize>;
79
80        /// Takes a value by column index or field name from `SqlValue`.
81        fn take_sql_value(
82            &self,
83            value: &mut SqlValue,
84        ) -> std::result::Result<SqlValue, ConvertError>;
85    }
86
87    impl ColumnIndex for usize {
88        fn index(&self, row: &Row) -> Option<usize> {
89            row.schema.get_field_by_index(*self).map(|_| *self)
90        }
91
92        fn take_sql_value(
93            &self,
94            value: &mut SqlValue,
95        ) -> std::result::Result<SqlValue, ConvertError> {
96            match &mut value.inner {
97                SqlValueInner::Struct(entries) => {
98                    let (_, slot) = entries
99                        .get_mut(*self)
100                        .ok_or_else(|| ConvertError::MissingField(self.to_string()))?;
101                    Ok(SqlValue::from_inner(std::mem::replace(
102                        slot,
103                        SqlValueInner::Null,
104                    )))
105                }
106                SqlValueInner::Array(arr) => {
107                    let slot = arr
108                        .get_mut(*self)
109                        .ok_or_else(|| ConvertError::MissingField(self.to_string()))?;
110                    Ok(SqlValue::from_inner(std::mem::replace(
111                        slot,
112                        SqlValueInner::Null,
113                    )))
114                }
115                SqlValueInner::String(s) => {
116                    let arr: Vec<wkt::Value> =
117                        serde_json::from_str(s).map_err(|e| ConvertError::Convert(Box::new(e)))?;
118                    value.inner = SqlValueInner::from_wkt(wkt::Value::Array(arr));
119                    self.take_sql_value(value)
120                }
121                SqlValueInner::Null => Err(ConvertError::NotNull),
122                other => Err(ConvertError::type_mismatch(
123                    "struct, array, or string",
124                    other,
125                )),
126            }
127        }
128    }
129
130    impl ColumnIndex for &str {
131        fn index(&self, row: &Row) -> Option<usize> {
132            row.schema.get_field_index_by_name(self)
133        }
134
135        fn take_sql_value(
136            &self,
137            value: &mut SqlValue,
138        ) -> std::result::Result<SqlValue, ConvertError> {
139            match &mut value.inner {
140                SqlValueInner::Struct(entries) => {
141                    let (_, slot) = entries
142                        .iter_mut()
143                        .find(|(name, _)| name == *self)
144                        .ok_or_else(|| ConvertError::MissingField((*self).to_string()))?;
145                    Ok(SqlValue::from_inner(std::mem::replace(
146                        slot,
147                        SqlValueInner::Null,
148                    )))
149                }
150                SqlValueInner::String(s) => {
151                    let obj: wkt::Struct =
152                        serde_json::from_str(s).map_err(|e| ConvertError::Convert(Box::new(e)))?;
153                    value.inner = SqlValueInner::from_wkt(wkt::Value::Object(obj));
154                    self.take_sql_value(value)
155                }
156                SqlValueInner::Null => Err(ConvertError::NotNull),
157                other => Err(ConvertError::type_mismatch("object or string", other)),
158            }
159        }
160    }
161
162    impl ColumnIndex for String {
163        fn index(&self, row: &Row) -> Option<usize> {
164            <&str as ColumnIndex>::index(&self.as_str(), row)
165        }
166
167        fn take_sql_value(
168            &self,
169            value: &mut SqlValue,
170        ) -> std::result::Result<SqlValue, ConvertError> {
171            self.as_str().take_sql_value(value)
172        }
173    }
174}
175
176/// A trait for types that can be used to index into a [`Row`] or [`SqlValue`](crate::query::SqlValue).
177///
178/// This trait is sealed and cannot be implemented for types outside of this crate.
179pub trait ColumnIndex: sealed::ColumnIndex + std::fmt::Display {}
180
181impl ColumnIndex for usize {}
182impl ColumnIndex for &str {}
183impl ColumnIndex for String {}
184
185impl Row {
186    pub(crate) fn try_new(row: Struct, schema: &Arc<Schema>) -> Result<Self> {
187        let values = convert_row(row, schema.fields())?;
188
189        Ok(Self {
190            values,
191            schema: schema.clone(),
192        })
193    }
194
195    fn resolve_index<I: ColumnIndex>(&self, col: &I) -> Result<usize> {
196        sealed::ColumnIndex::index(col, self)
197            .ok_or_else(|| RowError::ColumnNotFound(format!("{col}")))
198    }
199
200    fn convert_value_at<T: FromSql>(&self, idx: usize, val: SqlValueInner) -> Result<T> {
201        T::from_value(crate::query::SqlValue::from_inner(val)).map_err(|e| {
202            let (column, sql_type) = self
203                .schema
204                .get_field_by_index(idx)
205                .map(|f| (f.name.clone(), f.r#type.clone()))
206                .unwrap_or_else(|| (idx.to_string(), "UNKNOWN".to_string()));
207            RowError::TypeConversion {
208                column,
209                sql_type,
210                source: e,
211            }
212        })
213    }
214
215    /// Retrieves a value from the row by column name or zero-based index.
216    ///
217    /// The return type must implement [`FromSql`](crate::query::FromSql).
218    ///
219    /// The cell value is cloned from the row data without modifying `self`. If
220    /// you want to take ownership and avoid cloning large values, see
221    /// [`take()`](Row::take).
222    ///
223    /// # Example
224    ///
225    /// ```
226    /// # use google_cloud_bigquery::query::Row;
227    /// # fn sample(row: Row) -> anyhow::Result<()> {
228    /// let msg: String = row.get("msg")?;
229    /// println!("Value: {msg}");
230    /// # Ok(())
231    /// # }
232    /// ```
233    pub fn get<T: FromSql, I: ColumnIndex>(&self, index: I) -> Result<T> {
234        let idx = self.resolve_index(&index)?;
235        let val = self
236            .values
237            .get(idx)
238            .ok_or_else(|| RowError::IndexOutOfRange {
239                index: idx,
240                len: self.schema.len(),
241            })?;
242
243        self.convert_value_at(idx, val.clone())
244    }
245
246    /// Takes ownership of a value from the row by column name or zero-based
247    /// index.
248    ///
249    /// This method mutates `self` by extracting the cell value in-place to
250    /// avoid cloning. The extracted cell in the row is replaced with `NULL`.
251    /// Subsequent attempts to read or take the column will treat it as `NULL`
252    /// (returning `Ok(None)` when reading into `Option<T>`, or returning a
253    /// type conversion error for non-nullable types).
254    ///
255    /// <div class="warning">
256    ///
257    /// `take()` removes the value from `self` before converting it to `T`. If
258    /// type conversion fails and returns an error, the original value has
259    /// already been consumed and cannot be recovered from the row.
260    ///
261    /// </div>
262    ///
263    /// If you are not sure of the column's type or need to read the value
264    /// multiple times, use [`get()`](Row::get) instead of `take()`. Use `take()`
265    /// when you are confident of the type and want to avoid cloning large values
266    /// (such as strings, byte buffers, or nested records).
267    ///
268    /// # Example
269    ///
270    /// ```
271    /// # use google_cloud_bigquery::query::Row;
272    /// # fn sample(mut row: Row) -> anyhow::Result<()> {
273    /// let text: String = row.take("big_text")?;
274    /// println!("Length: {}", text.len());
275    ///
276    /// // Subsequent reads treat the column as NULL:
277    /// assert_eq!(row.get::<Option<String>, _>("big_text")?, None);
278    ///
279    /// // Attempting to read or take again as a non-nullable type fails:
280    /// assert!(row.take::<String, _>("big_text").is_err());
281    /// # Ok(())
282    /// # }
283    /// ```
284    pub fn take<T: FromSql, I: ColumnIndex>(&mut self, index: I) -> Result<T> {
285        let idx = self.resolve_index(&index)?;
286
287        let val = self
288            .values
289            .get_mut(idx)
290            .ok_or_else(|| RowError::IndexOutOfRange {
291                index: idx,
292                len: self.schema.len(),
293            })?;
294
295        // swap out the value in-place to avoid clones
296        let owned_val = std::mem::replace(val, SqlValueInner::Null);
297        self.convert_value_at(idx, owned_val)
298    }
299}
300
301fn convert_row(row: Struct, fields: &[TableFieldSchema]) -> Result<Vec<SqlValueInner>> {
302    let field_list = get_field_list(row)?;
303
304    if field_list.len() != fields.len() {
305        return Err(RowError::InvalidRowFormat(format!(
306            "schema and row cell mismatch (expected {}, got {})",
307            fields.len(),
308            field_list.len()
309        )));
310    }
311
312    field_list
313        .into_iter()
314        .zip(fields)
315        .map(|(cell, field)| convert_value(get_field_value(cell)?, field))
316        .collect()
317}
318
319fn get_field_list(mut row: Struct) -> Result<Vec<Value>> {
320    match row.remove("f") {
321        Some(Value::Array(arr)) => Ok(arr),
322        Some(_) => Err(RowError::InvalidRowFormat("invalid field values".into())),
323        None => Err(RowError::InvalidRowFormat("missing field values".into())),
324    }
325}
326
327fn get_field_value(value: Value) -> Result<Value> {
328    match value {
329        Value::Object(mut obj) => match obj.remove("v") {
330            Some(val) => Ok(val),
331            None => Err(RowError::InvalidRowFormat("missing field value".into())),
332        },
333        _ => Err(RowError::InvalidRowFormat("invalid field value".into())),
334    }
335}
336
337fn convert_value(value: Value, field: &TableFieldSchema) -> Result<SqlValueInner> {
338    match value {
339        Value::Null => Ok(SqlValueInner::Null),
340        Value::String(v) => convert_basic_type(v, &field.name, &field.r#type),
341        Value::Object(v) => convert_nested(v, &field.fields),
342        Value::Array(v) => convert_repeated(v, field),
343        _ => Err(RowError::InvalidRowFormat(format!(
344            "cell value is not an object: value={:?}, field_type={:?}",
345            value, field.r#type
346        ))),
347    }
348}
349
350fn convert_repeated(value: ListValue, field: &TableFieldSchema) -> Result<SqlValueInner> {
351    let arr = value
352        .into_iter()
353        .map(|cell| {
354            // each cell contains a single entry, keyed by "v"
355            let val = get_field_value(cell)?;
356            convert_value(val, field)
357        })
358        .collect::<Result<Vec<_>>>()?;
359    Ok(SqlValueInner::Array(arr))
360}
361
362fn convert_nested(value: Struct, fields: &[TableFieldSchema]) -> Result<SqlValueInner> {
363    let values = convert_row(value, fields)?;
364    let entries = fields
365        .iter()
366        .zip(values)
367        .map(|(field, value)| (field.name.clone(), value))
368        .collect();
369    Ok(SqlValueInner::Struct(entries))
370}
371
372fn convert_basic_type(value: String, field_name: &str, field_type: &str) -> Result<SqlValueInner> {
373    match field_type {
374        "STRING" | "BYTES" | "TIMESTAMP" | "DATE" | "TIME" | "DATETIME" | "NUMERIC"
375        | "BIGNUMERIC" | "BIGINT" | "GEOGRAPHY" | "JSON" | "INTERVAL" | "RANGE" => {
376            Ok(SqlValueInner::String(value))
377        }
378        "INTEGER" | "INT64" => {
379            let num = value.parse::<i64>().map_err(|e| RowError::TypeConversion {
380                column: field_name.to_string(),
381                sql_type: field_type.to_string(),
382                source: ConvertError::Convert(Box::new(e)),
383            })?;
384            Ok(SqlValueInner::Number(serde_json::Number::from(num)))
385        }
386        "FLOAT" | "FLOAT64" => {
387            let num = value.parse::<f64>().map_err(|e| RowError::TypeConversion {
388                column: field_name.to_string(),
389                sql_type: field_type.to_string(),
390                source: ConvertError::Convert(Box::new(e)),
391            })?;
392            match serde_json::Number::from_f64(num) {
393                Some(n) => Ok(SqlValueInner::Number(n)),
394                None => Ok(SqlValueInner::String(value)),
395            }
396        }
397        "BOOLEAN" | "BOOL" => {
398            let b = if value.eq_ignore_ascii_case("true") {
399                true
400            } else if value.eq_ignore_ascii_case("false") {
401                false
402            } else {
403                return Err(RowError::TypeConversion {
404                    column: field_name.to_string(),
405                    sql_type: field_type.to_string(),
406                    source: ConvertError::Convert(
407                        "provided string was not `true` or `false`".into(),
408                    ),
409                });
410            };
411            Ok(SqlValueInner::Bool(b))
412        }
413        _ => Err(RowError::InvalidRowFormat(format!(
414            "unknown field type: {} at column {}",
415            field_type, field_name
416        ))),
417    }
418}
419
420#[cfg(test)]
421mod tests {
422    use super::*;
423    use crate as google_cloud_bigquery;
424    use crate::query::FromRow;
425    use google_cloud_bigquery_v2::model::{TableFieldSchema, TableSchema};
426    use google_cloud_type::model::Decimal;
427    use rust_decimal::Decimal as RustDecimal;
428    use serde_json::{Map, json};
429    use test_case::test_case;
430
431    type TestResult = anyhow::Result<()>;
432
433    #[tokio::test]
434    async fn convert_basic_types_from_row() -> TestResult {
435        let raw_row = Map::from_iter([(
436            "f".to_string(),
437            json!([
438                { "v": "James" },
439                { "v": "272793" },
440                { "v": "TRUE" },
441                { "v": null },
442                { "v": "64.0" },
443            ]),
444        )]);
445        let schema = TableSchema::new().set_fields([
446            TableFieldSchema::new()
447                .set_name("name")
448                .set_type("STRING")
449                .set_mode("NULLABLE"),
450            TableFieldSchema::new()
451                .set_name("some_int")
452                .set_type("INTEGER")
453                .set_mode("NULLABLE"),
454            TableFieldSchema::new()
455                .set_name("some_bool")
456                .set_type("BOOLEAN")
457                .set_mode("NULLABLE"),
458            TableFieldSchema::new()
459                .set_name("some_null")
460                .set_type("INTEGER")
461                .set_mode("NULLABLE"),
462            TableFieldSchema::new()
463                .set_name("some_float")
464                .set_type("FLOAT64")
465                .set_mode("NULLABLE"),
466        ]);
467        let schema = Arc::new(Schema::new(schema));
468        let mut row = Row::try_new(raw_row, &schema)?;
469
470        assert_eq!(row.get::<String, _>(0)?, "James");
471        assert_eq!(row.get::<String, _>("name")?, "James");
472
473        assert_eq!(row.get::<i32, _>(1)?, 272793);
474        assert_eq!(row.get::<i32, _>("some_int")?, 272793);
475        assert_eq!(row.get::<i64, _>(1)?, 272793);
476        assert_eq!(row.get::<i64, _>("some_int")?, 272793);
477
478        assert!(row.get::<bool, _>(2)?);
479        assert!(row.get::<bool, _>("some_bool")?);
480
481        assert_eq!(row.get::<Option<i64>, _>(3)?, None);
482        assert_eq!(row.get::<Option<i64>, _>("some_null")?, None);
483
484        assert_eq!(row.get::<f32, _>(4)?, 64.0);
485        assert_eq!(row.get::<f32, _>("some_float")?, 64.0);
486        assert_eq!(row.get::<f64, _>(4)?, 64.0);
487        assert_eq!(row.get::<f64, _>("some_float")?, 64.0);
488
489        assert_eq!(row.take::<String, _>(0)?, "James");
490        assert_eq!(row.get::<Option<String>, _>(0)?, None);
491
492        assert_eq!(row.take::<i32, _>(1)?, 272793);
493        assert_eq!(row.get::<Option<i32>, _>(1)?, None);
494
495        assert!(row.take::<bool, _>(2)?);
496        assert_eq!(row.get::<Option<bool>, _>(2)?, None);
497
498        assert_eq!(row.take::<Option<i64>, _>(3)?, None);
499        assert_eq!(row.get::<Option<i64>, _>(3)?, None);
500
501        assert_eq!(row.take::<f32, _>(4)?, 64.0);
502        assert_eq!(row.get::<Option<f32>, _>(4)?, None);
503
504        Ok(())
505    }
506
507    #[tokio::test]
508    async fn convert_numeric_from_row() -> TestResult {
509        let raw_row = Map::from_iter([(
510            "f".to_string(),
511            json!([
512                { "v": "123.456" },
513                { "v": "99999999999999999999.123456789" },
514                { "v": "99999999999999999999999999999999.123" },
515            ]),
516        )]);
517        let schema = TableSchema::new().set_fields([
518            TableFieldSchema::new()
519                .set_name("price")
520                .set_type("NUMERIC")
521                .set_mode("NULLABLE"),
522            TableFieldSchema::new()
523                .set_name("big_amount")
524                .set_type("BIGNUMERIC")
525                .set_mode("NULLABLE"),
526            TableFieldSchema::new()
527                .set_name("overflow_amount")
528                .set_type("BIGNUMERIC")
529                .set_mode("NULLABLE"),
530        ]);
531        let schema = Arc::new(Schema::new(schema));
532        let mut row = Row::try_new(raw_row, &schema)?;
533
534        assert_eq!(
535            row.get::<Decimal, _>(0)?,
536            Decimal::new().set_value("123.456")
537        );
538        assert_eq!(
539            row.get::<Decimal, _>("price")?,
540            Decimal::new().set_value("123.456")
541        );
542
543        assert_eq!(
544            row.get::<Decimal, _>(1)?,
545            Decimal::new().set_value("99999999999999999999.123456789")
546        );
547        assert_eq!(
548            row.get::<Decimal, _>("big_amount")?,
549            Decimal::new().set_value("99999999999999999999.123456789")
550        );
551
552        assert_eq!(
553            row.get::<RustDecimal, _>(0)?,
554            "123.456".parse().expect("valid decimal")
555        );
556        assert_eq!(
557            row.get::<RustDecimal, _>("price")?,
558            "123.456".parse().expect("valid decimal")
559        );
560
561        assert_eq!(
562            row.get::<RustDecimal, _>(1)?,
563            "99999999999999999999.123456789"
564                .parse()
565                .expect("valid decimal")
566        );
567        assert_eq!(
568            row.get::<RustDecimal, _>("big_amount")?,
569            "99999999999999999999.123456789"
570                .parse()
571                .expect("valid decimal")
572        );
573
574        assert!(row.get::<RustDecimal, _>(2).is_err());
575        assert!(row.get::<RustDecimal, _>("overflow_amount").is_err());
576
577        assert_eq!(
578            row.take::<Decimal, _>(0)?,
579            Decimal::new().set_value("123.456")
580        );
581        assert_eq!(row.get::<Option<Decimal>, _>(0)?, None);
582
583        assert_eq!(
584            row.take::<RustDecimal, _>(1)?,
585            "99999999999999999999.123456789".parse()?
586        );
587        assert_eq!(row.get::<Option<RustDecimal>, _>(1)?, None);
588
589        Ok(())
590    }
591
592    #[tokio::test]
593    async fn convert_bytes_from_row() -> TestResult {
594        let raw_row = Map::from_iter([(
595            "f".to_string(),
596            json!([
597                { "v": "AQIDBA==" },
598                { "v": "SGVsbG8=" },
599                { "v": null },
600            ]),
601        )]);
602        let schema = TableSchema::new().set_fields([
603            TableFieldSchema::new()
604                .set_name("payload_vec")
605                .set_type("BYTES")
606                .set_mode("NULLABLE"),
607            TableFieldSchema::new()
608                .set_name("payload_bytes")
609                .set_type("BYTES")
610                .set_mode("NULLABLE"),
611            TableFieldSchema::new()
612                .set_name("null_bytes")
613                .set_type("BYTES")
614                .set_mode("NULLABLE"),
615        ]);
616        let schema = Arc::new(Schema::new(schema));
617        let mut row = Row::try_new(raw_row, &schema)?;
618
619        assert_eq!(row.get::<Vec<u8>, _>(0)?, vec![1, 2, 3, 4]);
620        assert_eq!(row.get::<Vec<u8>, _>("payload_vec")?, vec![1, 2, 3, 4]);
621
622        assert_eq!(
623            row.get::<bytes::Bytes, _>(1)?,
624            bytes::Bytes::from_static(b"Hello")
625        );
626        assert_eq!(
627            row.get::<bytes::Bytes, _>("payload_bytes")?,
628            bytes::Bytes::from_static(b"Hello")
629        );
630
631        assert_eq!(row.get::<Option<Vec<u8>>, _>(2)?, None);
632        assert_eq!(row.get::<Option<bytes::Bytes>, _>("null_bytes")?, None);
633
634        assert_eq!(row.take::<Vec<u8>, _>(0)?, vec![1, 2, 3, 4]);
635        assert_eq!(row.get::<Option<Vec<u8>>, _>(0)?, None);
636
637        assert_eq!(
638            row.take::<bytes::Bytes, _>(1)?,
639            bytes::Bytes::from_static(b"Hello")
640        );
641        assert_eq!(row.get::<Option<bytes::Bytes>, _>(1)?, None);
642
643        Ok(())
644    }
645
646    #[tokio::test]
647    async fn convert_record_from_row() -> TestResult {
648        let raw_row = Map::from_iter([(
649            "f".to_string(),
650            json!([
651                {
652                    "v": {
653                        "f": [
654                            { "v": "Alice" },
655                            { "v": "25" }
656                        ]
657                    }
658                }
659            ]),
660        )]);
661        let schema = TableSchema::new().set_fields([TableFieldSchema::new()
662            .set_name("user")
663            .set_type("RECORD")
664            .set_mode("NULLABLE")
665            .set_fields([
666                TableFieldSchema::new()
667                    .set_name("name")
668                    .set_type("STRING")
669                    .set_mode("NULLABLE"),
670                TableFieldSchema::new()
671                    .set_name("age")
672                    .set_type("INTEGER")
673                    .set_mode("NULLABLE"),
674            ])]);
675        let schema = Arc::new(Schema::new(schema));
676        let mut row = Row::try_new(raw_row, &schema)?;
677
678        let expected: Struct = serde_json::from_value(json!({
679            "name": "Alice",
680            "age": 25,
681        }))?;
682        assert_eq!(row.get::<Struct, _>(0)?, expected);
683        assert_eq!(row.get::<Struct, _>("user")?, expected);
684        assert_eq!(row.get::<Struct, _>("user".to_string())?, expected);
685        assert_eq!(row.take::<Struct, _>("user")?, expected);
686        assert_eq!(row.get::<Option<Struct>, _>("user")?, None);
687
688        Ok(())
689    }
690
691    #[derive(crate::query::FromSql, Debug, PartialEq)]
692    struct JsonPayload {
693        name: String,
694        age: i64,
695    }
696
697    #[tokio::test]
698    async fn convert_json_from_row() -> TestResult {
699        let json_str = json!({"name": "Alice", "age": 30}).to_string();
700        let raw_row = Map::from_iter([(
701            "f".to_string(),
702            json!([
703                { "v": json_str },
704                { "v": null },
705            ]),
706        )]);
707        let schema = TableSchema::new().set_fields([
708            TableFieldSchema::new()
709                .set_name("json_obj")
710                .set_type("JSON")
711                .set_mode("NULLABLE"),
712            TableFieldSchema::new()
713                .set_name("json_null")
714                .set_type("JSON")
715                .set_mode("NULLABLE"),
716        ]);
717        let schema = Arc::new(Schema::new(schema));
718        let row = Row::try_new(raw_row, &schema)?;
719
720        let expected_struct: Struct = serde_json::from_value(json!({
721            "name": "Alice",
722            "age": 30,
723        }))?;
724        assert_eq!(row.get::<String, _>("json_obj")?, json_str);
725        assert_eq!(row.get::<Struct, _>("json_obj")?, expected_struct);
726        assert_eq!(
727            row.get::<JsonPayload, _>("json_obj")?,
728            JsonPayload {
729                name: "Alice".to_string(),
730                age: 30,
731            }
732        );
733        assert_eq!(row.get::<Option<Struct>, _>("json_null")?, None);
734        assert_eq!(row.get::<Option<JsonPayload>, _>("json_null")?, None);
735
736        Ok(())
737    }
738
739    #[tokio::test]
740    async fn convert_repeated_from_row() -> TestResult {
741        let raw_row = Map::from_iter([(
742            "f".to_string(),
743            json!([
744                {
745                    "v": [
746                        { "v": "1" },
747                        { "v": "2" },
748                        { "v": "3" }
749                    ]
750                }
751            ]),
752        )]);
753        let schema = TableSchema::new().set_fields([TableFieldSchema::new()
754            .set_name("numbers")
755            .set_type("INTEGER")
756            .set_mode("REPEATED")]);
757        let schema = Arc::new(Schema::new(schema));
758        let mut row = Row::try_new(raw_row, &schema)?;
759
760        assert_eq!(row.get::<Vec<i64>, _>(0)?, vec![1, 2, 3]);
761        assert_eq!(row.get::<Vec<i64>, _>("numbers")?, vec![1, 2, 3]);
762        assert_eq!(row.take::<Vec<i64>, _>("numbers")?, vec![1, 2, 3]);
763        assert_eq!(row.get::<Option<Vec<i64>>, _>("numbers")?, None);
764
765        Ok(())
766    }
767
768    #[tokio::test]
769    async fn convert_repeated_record_from_row() -> TestResult {
770        let raw_row = Map::from_iter([(
771            "f".to_string(),
772            json!([
773                {
774                    "v": [
775                        {
776                            "v": {
777                                "f": [
778                                    { "v": "Bob" },
779                                    { "v": "28" }
780                                ]
781                            }
782                        },
783                        {
784                            "v": {
785                                "f": [
786                                    { "v": "Charlie" },
787                                    { "v": "31" }
788                                ]
789                            }
790                        }
791                    ]
792                }
793            ]),
794        )]);
795        let schema = TableSchema::new().set_fields([TableFieldSchema::new()
796            .set_name("users")
797            .set_type("RECORD")
798            .set_mode("REPEATED")
799            .set_fields([
800                TableFieldSchema::new()
801                    .set_name("name")
802                    .set_type("STRING")
803                    .set_mode("NULLABLE"),
804                TableFieldSchema::new()
805                    .set_name("age")
806                    .set_type("INTEGER")
807                    .set_mode("NULLABLE"),
808            ])]);
809        let schema = Arc::new(Schema::new(schema));
810        let mut row = Row::try_new(raw_row, &schema)?;
811
812        let expected: Vec<Struct> = serde_json::from_value(json!([
813            {
814                "name": "Bob",
815                "age": 28,
816            },
817            {
818                "name": "Charlie",
819                "age": 31,
820            },
821        ]))?;
822        assert_eq!(row.get::<Vec<Struct>, _>(0)?, expected);
823        assert_eq!(row.get::<Vec<Struct>, _>("users")?, expected);
824        assert_eq!(row.take::<Vec<Struct>, _>("users")?, expected);
825        assert_eq!(row.get::<Option<Vec<Struct>>, _>("users")?, None);
826
827        Ok(())
828    }
829
830    #[test_case("INTEGER", "123", Value::Number(123.into()); "integer positive")]
831    #[test_case("INTEGER", "-456", Value::Number((-456).into()); "integer negative")]
832    #[test_case("INT64", "9223372036854775807", Value::Number(9223372036854775807_i64.into()); "int64 max")]
833    #[test_case("FLOAT", "123.45", Value::Number(serde_json::Number::from_f64(123.45).unwrap()); "float success")]
834    #[test_case("FLOAT64", "NaN", Value::String("NaN".to_string()); "float NaN")]
835    #[test_case("FLOAT64", "+inf", Value::String("+inf".to_string()); "float positive infinity")]
836    #[test_case("FLOAT64", "-inf", Value::String("-inf".to_string()); "float negative infinity")]
837    #[test_case("BOOLEAN", "true", Value::Bool(true); "boolean true lowercase")]
838    #[test_case("BOOLEAN", "TRUE", Value::Bool(true); "boolean true uppercase")]
839    #[test_case("BOOL", "false", Value::Bool(false); "bool false")]
840    #[test_case("JSON", r#"{"a":1}"#, Value::String(r#"{"a":1}"#.to_string()); "json string")]
841    fn convert_basic_type_cases_success(field_type: &str, value: &str, expected: Value) {
842        let res = convert_basic_type(value.to_string(), "test_col", field_type);
843        let value = res.expect("should succeed");
844        assert_eq!(
845            value,
846            crate::query::from_sql::SqlValueInner::from_wkt(expected)
847        );
848    }
849
850    #[test_case("INTEGER", "abc"; "integer invalid")]
851    #[test_case("INT64", "9223372036854775808"; "int64 overflow")]
852    #[test_case("FLOAT", "abc"; "float invalid")]
853    #[test_case("BOOL", "invalid"; "bool invalid")]
854    fn convert_basic_type_cases_conversion_fail(field_type: &str, value: &str) {
855        let res = convert_basic_type(value.to_string(), "test_col", field_type);
856        let err = res.unwrap_err();
857        assert!(matches!(err, RowError::TypeConversion { .. }));
858    }
859
860    #[test]
861    fn convert_basic_type_invalid_row_format() {
862        let res = convert_basic_type("value".to_string(), "test_col", "UNKNOWN");
863        let err = res.unwrap_err();
864        assert!(matches!(err, RowError::InvalidRowFormat(_)));
865    }
866
867    #[test]
868    fn convert_value_unsupported_value() {
869        let field = TableFieldSchema::new()
870            .set_name("test_col")
871            .set_type("BOOLEAN")
872            .set_mode("NULLABLE");
873        let res = convert_value(Value::Bool(true), &field);
874        let err = res.unwrap_err();
875        assert!(matches!(err, RowError::InvalidRowFormat(_)));
876    }
877
878    #[derive(FromRow, Debug, PartialEq)]
879    struct TestRow {
880        name: String,
881        #[bigquery(rename = "custom_int")]
882        some_int: i64,
883        some_bool: bool,
884        some_null: Option<i64>,
885    }
886
887    #[tokio::test]
888    async fn derive_from_row_success() -> TestResult {
889        let raw_row = Map::from_iter([(
890            "f".to_string(),
891            json!([
892                { "v": "James" },
893                { "v": "272793" },
894                { "v": "TRUE" },
895                { "v": null },
896            ]),
897        )]);
898        let schema = TableSchema::new().set_fields([
899            TableFieldSchema::new()
900                .set_name("name")
901                .set_type("STRING")
902                .set_mode("NULLABLE"),
903            TableFieldSchema::new()
904                .set_name("custom_int")
905                .set_type("INTEGER")
906                .set_mode("NULLABLE"),
907            TableFieldSchema::new()
908                .set_name("some_bool")
909                .set_type("BOOLEAN")
910                .set_mode("NULLABLE"),
911            TableFieldSchema::new()
912                .set_name("some_null")
913                .set_type("INTEGER")
914                .set_mode("NULLABLE"),
915        ]);
916        let schema = Arc::new(Schema::new(schema));
917        let row = Row::try_new(raw_row, &schema)?;
918
919        let converted_row = TestRow::try_from(row)?;
920        assert_eq!(
921            converted_row,
922            TestRow {
923                name: "James".to_string(),
924                some_int: 272793,
925                some_bool: true,
926                some_null: None,
927            }
928        );
929
930        Ok(())
931    }
932
933    #[derive(FromRow, Debug, PartialEq)]
934    struct RawIdentRow {
935        r#type: String,
936        r#match: i64,
937    }
938
939    #[tokio::test]
940    async fn derive_from_row_raw_identifier() -> TestResult {
941        let raw_row = Map::from_iter([(
942            "f".to_string(),
943            json!([
944                { "v": "click" },
945                { "v": "7" },
946            ]),
947        )]);
948        let schema = TableSchema::new().set_fields([
949            TableFieldSchema::new()
950                .set_name("type")
951                .set_type("STRING")
952                .set_mode("NULLABLE"),
953            TableFieldSchema::new()
954                .set_name("match")
955                .set_type("INTEGER")
956                .set_mode("NULLABLE"),
957        ]);
958        let schema = Arc::new(Schema::new(schema));
959        let row = Row::try_new(raw_row, &schema)?;
960
961        let converted = RawIdentRow::try_from(row)?;
962        assert_eq!(
963            converted,
964            RawIdentRow {
965                r#type: "click".to_string(),
966                r#match: 7,
967            }
968        );
969        Ok(())
970    }
971
972    #[tokio::test]
973    async fn derive_from_row_missing_column() -> TestResult {
974        let raw_row = Map::from_iter([(
975            "f".to_string(),
976            json!([
977                { "v": "James" },
978                { "v": "123" },
979                { "v": "TRUE" },
980                { "v": null },
981            ]),
982        )]);
983        let schema = TableSchema::new().set_fields([
984            TableFieldSchema::new()
985                .set_name("name")
986                .set_type("STRING")
987                .set_mode("NULLABLE"),
988            TableFieldSchema::new()
989                .set_name("wrong_col")
990                .set_type("INTEGER")
991                .set_mode("NULLABLE"),
992            TableFieldSchema::new()
993                .set_name("some_bool")
994                .set_type("BOOLEAN")
995                .set_mode("NULLABLE"),
996            TableFieldSchema::new()
997                .set_name("some_null")
998                .set_type("INTEGER")
999                .set_mode("NULLABLE"),
1000        ]);
1001        let schema = Arc::new(Schema::new(schema));
1002        let row = Row::try_new(raw_row, &schema)?;
1003
1004        let err = TestRow::try_from(row).unwrap_err();
1005        assert!(matches!(err, RowError::ColumnNotFound(col) if col == "custom_int"));
1006        Ok(())
1007    }
1008
1009    #[derive(FromRow, Debug, PartialEq)]
1010    struct ShadowedFieldNamesRow {
1011        row: i64,
1012        name: String,
1013    }
1014
1015    #[tokio::test]
1016    async fn derive_from_row_shadowing_field_names() -> TestResult {
1017        let raw_row = Map::from_iter([(
1018            "f".to_string(),
1019            json!([
1020                { "v": "42" },
1021                { "v": "Alice" },
1022            ]),
1023        )]);
1024        let schema = TableSchema::new().set_fields([
1025            TableFieldSchema::new()
1026                .set_name("row")
1027                .set_type("INTEGER")
1028                .set_mode("NULLABLE"),
1029            TableFieldSchema::new()
1030                .set_name("name")
1031                .set_type("STRING")
1032                .set_mode("NULLABLE"),
1033        ]);
1034        let schema = Arc::new(Schema::new(schema));
1035        let row = Row::try_new(raw_row, &schema)?;
1036
1037        let converted = ShadowedFieldNamesRow::try_from(row)?;
1038        assert_eq!(
1039            converted,
1040            ShadowedFieldNamesRow {
1041                row: 42,
1042                name: "Alice".to_string(),
1043            }
1044        );
1045        Ok(())
1046    }
1047
1048    #[derive(FromRow, Debug, PartialEq)]
1049    struct TupleRow(i64, String);
1050
1051    #[derive(crate::query::FromSql, Debug, PartialEq)]
1052    struct AnonTriple(i64, String, bool);
1053
1054    #[derive(crate::query::FromSql, Debug, PartialEq)]
1055    struct NamedZThenA {
1056        z: i64,
1057        a: i64,
1058    }
1059
1060    #[derive(crate::query::FromSql, Debug, PartialEq)]
1061    struct PositionalPair(i64, i64);
1062
1063    #[derive(crate::query::FromSql, Debug, PartialEq)]
1064    struct DupIdNamed {
1065        id: i64,
1066    }
1067
1068    #[tokio::test]
1069    async fn anonymous_struct_preserves_all_fields() -> TestResult {
1070        // Simulates `SELECT STRUCT(10, 'hello', true) AS anon`, where BigQuery
1071        // sets `field.name = ""` for all three subfields.
1072        let raw_row = Map::from_iter([(
1073            "f".to_string(),
1074            json!([
1075                {
1076                    "v": {
1077                        "f": [
1078                            { "v": "10" },
1079                            { "v": "hello" },
1080                            { "v": "true" }
1081                        ]
1082                    }
1083                }
1084            ]),
1085        )]);
1086        let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1087            .set_name("anon")
1088            .set_type("RECORD")
1089            .set_mode("NULLABLE")
1090            .set_fields([
1091                TableFieldSchema::new()
1092                    .set_name("")
1093                    .set_type("INT64")
1094                    .set_mode("NULLABLE"),
1095                TableFieldSchema::new()
1096                    .set_name("")
1097                    .set_type("STRING")
1098                    .set_mode("NULLABLE"),
1099                TableFieldSchema::new()
1100                    .set_name("")
1101                    .set_type("BOOL")
1102                    .set_mode("NULLABLE"),
1103            ])]);
1104        let schema = Arc::new(Schema::new(schema));
1105        let mut row = Row::try_new(raw_row, &schema)?;
1106
1107        let anon: AnonTriple = row.take("anon")?;
1108        assert_eq!(anon, AnonTriple(10, "hello".to_string(), true));
1109        Ok(())
1110    }
1111
1112    #[tokio::test]
1113    async fn struct_preserves_sql_declaration_order_over_alphabetical_order() -> TestResult {
1114        // Simulates `SELECT STRUCT(1 AS z, 2 AS a) AS pair`.
1115        // A BTreeMap would sort `"a"` before `"z"`, scrambling positional order.
1116        let raw_row = Map::from_iter([(
1117            "f".to_string(),
1118            json!([
1119                {
1120                    "v": {
1121                        "f": [
1122                            { "v": "1" },
1123                            { "v": "2" }
1124                        ]
1125                    }
1126                }
1127            ]),
1128        )]);
1129        let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1130            .set_name("pair")
1131            .set_type("RECORD")
1132            .set_mode("NULLABLE")
1133            .set_fields([
1134                TableFieldSchema::new()
1135                    .set_name("z")
1136                    .set_type("INT64")
1137                    .set_mode("NULLABLE"),
1138                TableFieldSchema::new()
1139                    .set_name("a")
1140                    .set_type("INT64")
1141                    .set_mode("NULLABLE"),
1142            ])]);
1143        let schema = Arc::new(Schema::new(schema));
1144        let row = Row::try_new(raw_row, &schema)?;
1145
1146        // Named extraction gets z=1, a=2
1147        let by_name: NamedZThenA = row.get("pair")?;
1148        assert_eq!(by_name, NamedZThenA { z: 1, a: 2 });
1149
1150        // Positional extraction on the same struct gets (1, 2) in SQL order (z then a)
1151        let by_pos: PositionalPair = row.get("pair")?;
1152        assert_eq!(by_pos, PositionalPair(1, 2));
1153        Ok(())
1154    }
1155
1156    #[tokio::test]
1157    async fn duplicate_struct_field_names_match_row_behavior() -> TestResult {
1158        // Simulates `SELECT STRUCT(100 AS id, 200 AS id) AS dup`.
1159        // Name lookup must return the first `"id"` (100), while positional lookup
1160        // can access both `(100, 200)`.
1161        let raw_row = Map::from_iter([(
1162            "f".to_string(),
1163            json!([
1164                {
1165                    "v": {
1166                        "f": [
1167                            { "v": "100" },
1168                            { "v": "200" }
1169                        ]
1170                    }
1171                }
1172            ]),
1173        )]);
1174        let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1175            .set_name("dup")
1176            .set_type("RECORD")
1177            .set_mode("NULLABLE")
1178            .set_fields([
1179                TableFieldSchema::new()
1180                    .set_name("id")
1181                    .set_type("INT64")
1182                    .set_mode("NULLABLE"),
1183                TableFieldSchema::new()
1184                    .set_name("id")
1185                    .set_type("INT64")
1186                    .set_mode("NULLABLE"),
1187            ])]);
1188        let schema = Arc::new(Schema::new(schema));
1189        let row = Row::try_new(raw_row, &schema)?;
1190
1191        let first_id: DupIdNamed = row.get("dup")?;
1192        assert_eq!(first_id, DupIdNamed { id: 100 });
1193
1194        let both_ids: PositionalPair = row.get("dup")?;
1195        assert_eq!(both_ids, PositionalPair(100, 200));
1196        Ok(())
1197    }
1198
1199    #[tokio::test]
1200    async fn derive_from_row_tuple_struct() -> TestResult {
1201        // Simulates `SELECT 42, 'world'` where top-level columns have generated names.
1202        let raw_row = Map::from_iter([(
1203            "f".to_string(),
1204            json!([
1205            { "v": "42" },
1206            { "v": "world" },
1207              ]),
1208        )]);
1209        let schema = TableSchema::new().set_fields([
1210            TableFieldSchema::new()
1211                .set_name("_f0")
1212                .set_type("INT64")
1213                .set_mode("NULLABLE"),
1214            TableFieldSchema::new()
1215                .set_name("_f1")
1216                .set_type("STRING")
1217                .set_mode("NULLABLE"),
1218        ]);
1219        let schema = Arc::new(Schema::new(schema));
1220        let row = Row::try_new(raw_row, &schema)?;
1221
1222        let converted = TupleRow::try_from(row)?;
1223        assert_eq!(converted, TupleRow(42, "world".to_string()));
1224
1225        // here
1226        Ok(())
1227    }
1228
1229    #[derive(FromSql, Debug, PartialEq)]
1230    struct NestedGeneric<U> {
1231        inner_val: U,
1232    }
1233
1234    #[derive(FromRow, Debug, PartialEq)]
1235    struct GenericRow<T: Clone + Default, U: std::fmt::Debug> {
1236        #[bigquery(rename = "custom_val")]
1237        single: T,
1238        optional: Option<T>,
1239        list: Vec<T>,
1240        nested: NestedGeneric<U>,
1241        common: i64,
1242    }
1243
1244    #[tokio::test]
1245    async fn derive_from_row_generic() -> TestResult {
1246        let raw_row = Map::from_iter([(
1247            "f".to_string(),
1248            json!([
1249                { "v": "100" },
1250                { "v": null },
1251                { "v": [{ "v": "1" }, { "v": "2" }, { "v": "3" }] },
1252                {
1253                    "v": {
1254                        "f": [
1255                            { "v": "nested_value" }
1256                        ]
1257                    }
1258                },
1259                { "v": "1" },
1260            ]),
1261        )]);
1262        let schema = TableSchema::new().set_fields([
1263            TableFieldSchema::new()
1264                .set_name("custom_val")
1265                .set_type("INTEGER")
1266                .set_mode("NULLABLE"),
1267            TableFieldSchema::new()
1268                .set_name("optional")
1269                .set_type("INTEGER")
1270                .set_mode("NULLABLE"),
1271            TableFieldSchema::new()
1272                .set_name("list")
1273                .set_type("INTEGER")
1274                .set_mode("REPEATED"),
1275            TableFieldSchema::new()
1276                .set_name("nested")
1277                .set_type("RECORD")
1278                .set_mode("NULLABLE")
1279                .set_fields([TableFieldSchema::new()
1280                    .set_name("inner_val")
1281                    .set_type("STRING")
1282                    .set_mode("NULLABLE")]),
1283            TableFieldSchema::new()
1284                .set_name("common")
1285                .set_type("INTEGER")
1286                .set_mode("NULLABLE"),
1287        ]);
1288        let schema = Arc::new(Schema::new(schema));
1289        let row = Row::try_new(raw_row, &schema)?;
1290
1291        let converted = GenericRow::<i64, String>::try_from(row)?;
1292        assert_eq!(
1293            converted,
1294            GenericRow {
1295                single: 100,
1296                optional: None,
1297                list: vec![1, 2, 3],
1298                nested: NestedGeneric {
1299                    inner_val: "nested_value".to_string(),
1300                },
1301                common: 1,
1302            }
1303        );
1304        Ok(())
1305    }
1306
1307    #[derive(FromRow, Debug, PartialEq)]
1308    struct GenericRowWhere<T>
1309    where
1310        T: std::fmt::Debug + Clone,
1311    {
1312        val: T,
1313    }
1314
1315    #[tokio::test]
1316    async fn derive_from_row_generic_where_clause() -> TestResult {
1317        let raw_row = Map::from_iter([("f".to_string(), json!([{ "v": "hello" }]))]);
1318        let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1319            .set_name("val")
1320            .set_type("STRING")
1321            .set_mode("NULLABLE")]);
1322        let schema = Arc::new(Schema::new(schema));
1323        let row = Row::try_new(raw_row, &schema)?;
1324
1325        let converted = GenericRowWhere::<String>::try_from(row)?;
1326        assert_eq!(
1327            converted,
1328            GenericRowWhere {
1329                val: "hello".to_string(),
1330            }
1331        );
1332        Ok(())
1333    }
1334
1335    #[derive(FromRow, Debug, PartialEq)]
1336    struct GenericRowDefault<T = i64> {
1337        val: T,
1338    }
1339
1340    #[tokio::test]
1341    async fn derive_from_row_generic_default_param() -> TestResult {
1342        let raw_row = Map::from_iter([("f".to_string(), json!([{ "v": "42" }]))]);
1343        let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1344            .set_name("val")
1345            .set_type("INTEGER")
1346            .set_mode("NULLABLE")]);
1347        let schema = Arc::new(Schema::new(schema));
1348        let row = Row::try_new(raw_row, &schema)?;
1349
1350        let converted = GenericRowDefault::try_from(row)?;
1351        assert_eq!(converted, GenericRowDefault { val: 42 });
1352        Ok(())
1353    }
1354
1355    #[derive(FromRow, Debug, PartialEq)]
1356    struct GenericTupleRow<T, U>(T, Option<T>, U);
1357
1358    #[tokio::test]
1359    async fn derive_from_row_generic_tuple_struct() -> TestResult {
1360        let raw_row = Map::from_iter([(
1361            "f".to_string(),
1362            json!([
1363                { "v": "42" },
1364                { "v": null },
1365                { "v": "hello" },
1366            ]),
1367        )]);
1368        let schema = TableSchema::new().set_fields([
1369            TableFieldSchema::new()
1370                .set_name("_f0")
1371                .set_type("INT64")
1372                .set_mode("NULLABLE"),
1373            TableFieldSchema::new()
1374                .set_name("_f1")
1375                .set_type("INT64")
1376                .set_mode("NULLABLE"),
1377            TableFieldSchema::new()
1378                .set_name("_f2")
1379                .set_type("STRING")
1380                .set_mode("NULLABLE"),
1381        ]);
1382        let schema = Arc::new(Schema::new(schema));
1383        let row = Row::try_new(raw_row, &schema)?;
1384
1385        let converted = GenericTupleRow::<i64, String>::try_from(row)?;
1386        assert_eq!(converted, GenericTupleRow(42, None, "hello".to_string()));
1387        Ok(())
1388    }
1389}