Coverage Report

Created: 2026-09-19 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/surrealdb-protocol-0.10.1/rust/rpc_methods.rs
Line
Count
Source
1
use std::{collections::VecDeque, fmt::Display, pin::Pin};
2
3
use crate::{
4
    convert::TryFromValue,
5
    proto::{
6
        rpc::v1::{QueryError, QueryResponse, export_sql_request},
7
        v1::Value,
8
    },
9
};
10
use anyhow::{Result, anyhow};
11
use futures::{Stream, StreamExt};
12
use tonic::Streaming;
13
use tonic::async_trait;
14
15
impl QueryResponse {
16
    /// Returns the first value from the query response.
17
0
    pub fn first(mut self) -> Result<Value, anyhow::Error> {
18
0
        if let Some(err) = self.error {
19
0
            return Err(anyhow!("{}", err.message));
20
0
        }
21
22
0
        if self.values.is_empty() {
23
0
            return Err(anyhow!("No values returned"));
24
0
        }
25
26
0
        let value = self.values.remove(0);
27
28
0
        Ok(value)
29
0
    }
30
31
    /// Returns all values from the query response.
32
0
    pub fn values(self) -> Result<Vec<Value>, anyhow::Error> {
33
0
        if let Some(err) = self.error {
34
0
            return Err(anyhow!("{}", err.message));
35
0
        }
36
37
0
        Ok(self.values)
38
0
    }
39
}
40
41
/// Consumes a stream of query responses and returns a stream of values.
42
pub struct QueryResponseValueStream {
43
    stream: Streaming<QueryResponse>,
44
    current_values: VecDeque<Value>,
45
}
46
47
impl QueryResponseValueStream {
48
    /// Creates a new `QueryResponseValueStream` from a stream of query responses.
49
0
    pub fn new(stream: Streaming<QueryResponse>) -> Self {
50
0
        Self {
51
0
            stream,
52
0
            current_values: VecDeque::new(),
53
0
        }
54
0
    }
55
}
56
57
impl Stream for QueryResponseValueStream {
58
    type Item = Result<Value, anyhow::Error>;
59
60
0
    fn poll_next(
61
0
        mut self: Pin<&mut Self>,
62
0
        cx: &mut std::task::Context<'_>,
63
0
    ) -> std::task::Poll<Option<Self::Item>> {
64
        use std::task::Poll;
65
66
0
        let this = self.as_mut().get_mut();
67
68
        // If we have values in the current batch, yield the next one
69
0
        if let Some(value) = this.current_values.pop_front() {
70
0
            return Poll::Ready(Some(Ok(value)));
71
0
        }
72
73
        // Otherwise, try to get the next batch from the stream
74
0
        match this.stream.poll_next_unpin(cx) {
75
0
            Poll::Ready(Some(Ok(response))) => {
76
                // Check for errors in the response
77
0
                if let Some(err) = response.error {
78
0
                    return Poll::Ready(Some(Err(anyhow!("{}", err.message))));
79
0
                }
80
81
                // Extract values from the response and add them to the queue
82
0
                this.current_values.extend(response.values);
83
84
                // Yield the first value from this batch
85
0
                if let Some(value) = this.current_values.pop_front() {
86
0
                    Poll::Ready(Some(Ok(value)))
87
                } else {
88
                    // Empty batch, try again
89
0
                    self.poll_next(cx)
90
                }
91
            }
92
0
            Poll::Ready(Some(Err(e))) => {
93
                // Stream error
94
0
                Poll::Ready(Some(Err(anyhow!("Stream error: {}", e))))
95
            }
96
            Poll::Ready(None) => {
97
                // Stream ended
98
0
                Poll::Ready(None)
99
            }
100
            Poll::Pending => {
101
                // Stream not ready yet
102
0
                Poll::Pending
103
            }
104
        }
105
0
    }
106
}
107
108
impl From<bool> for export_sql_request::Tables {
109
0
    fn from(value: bool) -> Self {
110
0
        if value {
111
0
            export_sql_request::Tables {
112
0
                selection: Some(export_sql_request::tables::Selection::All(
113
0
                    Default::default(),
114
0
                )),
115
0
            }
116
        } else {
117
0
            export_sql_request::Tables {
118
0
                selection: Some(export_sql_request::tables::Selection::None(
119
0
                    Default::default(),
120
0
                )),
121
0
            }
122
        }
123
0
    }
124
}
125
126
impl QueryError {
127
    /// Creates a new `QueryError` with the given code and message.
128
0
    pub fn new(code: i64, message: String) -> Self {
129
0
        Self { code, message }
130
0
    }
131
}
132
133
impl Display for QueryError {
134
0
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135
0
        write!(f, "({}): {}", self.code, self.message)
136
0
    }
137
}
138
139
impl std::error::Error for QueryError {}
140
141
/// A trait for converting a stream of query responses into a specific type.
142
#[async_trait]
143
pub trait TryFromQueryStream {
144
    /// Converts a stream of query responses into a specific type.
145
    async fn try_from_query_stream(stream: Streaming<QueryResponse>) -> Result<Self, anyhow::Error>
146
    where
147
        Self: Sized;
148
}
149
150
#[async_trait]
151
impl<T> TryFromQueryStream for Option<T>
152
where
153
    T: TryFromValue + Send,
154
{
155
    async fn try_from_query_stream(
156
        stream: Streaming<QueryResponse>,
157
0
    ) -> Result<Self, anyhow::Error> {
158
        let mut stream = QueryResponseValueStream::new(stream);
159
        let value = match stream.next().await {
160
            Some(Ok(value)) => value,
161
            Some(Err(e)) => return Err(e),
162
            None => return Ok(None),
163
        };
164
        let value = T::try_from_value(value)?;
165
        Ok(Some(value))
166
0
    }
167
}
168
169
#[async_trait]
170
impl<T> TryFromQueryStream for Vec<T>
171
where
172
    T: TryFromValue + Send,
173
{
174
    async fn try_from_query_stream(
175
        stream: Streaming<QueryResponse>,
176
0
    ) -> Result<Self, anyhow::Error> {
177
        let mut stream = QueryResponseValueStream::new(stream);
178
        let mut values = Vec::new();
179
        while let Some(value) = stream.next().await {
180
            let value = T::try_from_value(value?)?;
181
            values.push(value);
182
        }
183
        Ok(values)
184
0
    }
185
}
186
187
impl From<Vec<&str>> for export_sql_request::Tables {
188
0
    fn from(values: Vec<&str>) -> Self {
189
0
        let mut selected_tables = export_sql_request::SelectedTables::default();
190
0
        for v in values {
191
0
            selected_tables.tables.push(v.to_string());
192
0
        }
193
0
        export_sql_request::Tables {
194
0
            selection: Some(export_sql_request::tables::Selection::Selected(
195
0
                selected_tables,
196
0
            )),
197
0
        }
198
0
    }
199
}