1use crate::api::conn::Command;
2use crate::api::err::Error;
3use crate::api::method::BoxFuture;
4use crate::api::method::Content;
5use crate::api::opt::Resource;
6use crate::api::Connection;
7use crate::api::Result;
8use crate::method::OnceLockExt;
9use crate::Surreal;
10use crate::Value;
11use serde::de::DeserializeOwned;
12use serde::Serialize;
13use std::borrow::Cow;
14use std::future::IntoFuture;
15use std::marker::PhantomData;
16use surrealdb_core::sql::{to_value as to_core_value, Object as CoreObject, Value as CoreValue};
17
18use super::insert_relation::InsertRelation;
19
20#[derive(Debug)]
22#[must_use = "futures do nothing unless you `.await` or poll them"]
23pub struct Insert<'r, C: Connection, R> {
24 pub(super) client: Cow<'r, Surreal<C>>,
25 pub(super) resource: Result<Resource>,
26 pub(super) response_type: PhantomData<R>,
27}
28
29impl<C, R> Insert<'_, C, R>
30where
31 C: Connection,
32{
33 pub fn into_owned(self) -> Insert<'static, C, R> {
35 Insert {
36 client: Cow::Owned(self.client.into_owned()),
37 ..self
38 }
39 }
40}
41
42macro_rules! into_future {
43 ($method:ident) => {
44 fn into_future(self) -> Self::IntoFuture {
45 let Insert {
46 client,
47 resource,
48 ..
49 } = self;
50 Box::pin(async move {
51 let (table, data) = match resource? {
52 Resource::Table(table) => (table.into(), CoreObject::default()),
53 Resource::RecordId(record_id) => {
54 let record_id = record_id.into_inner();
55 let mut map = CoreObject::default();
56 map.insert("id".to_string(), record_id.id.into());
57 (record_id.tb, map)
58 }
59 Resource::Object(_) => return Err(Error::InsertOnObject.into()),
60 Resource::Array(_) => return Err(Error::InsertOnArray.into()),
61 Resource::Edge {
62 ..
63 } => return Err(Error::InsertOnEdges.into()),
64 Resource::Range {
65 ..
66 } => return Err(Error::InsertOnRange.into()),
67 Resource::Unspecified => return Err(Error::InsertOnUnspecified.into()),
68 };
69 let cmd = Command::Insert {
70 what: Some(table.to_string()),
71 data: data.into(),
72 };
73
74 let router = client.router.extract()?;
75 router.$method(cmd).await
76 })
77 }
78 };
79}
80
81impl<'r, Client> IntoFuture for Insert<'r, Client, Value>
82where
83 Client: Connection,
84{
85 type Output = Result<Value>;
86 type IntoFuture = BoxFuture<'r, Self::Output>;
87
88 into_future! {execute_value}
89}
90
91impl<'r, Client, R> IntoFuture for Insert<'r, Client, Option<R>>
92where
93 Client: Connection,
94 R: DeserializeOwned,
95{
96 type Output = Result<Option<R>>;
97 type IntoFuture = BoxFuture<'r, Self::Output>;
98
99 into_future! {execute_opt}
100}
101
102impl<'r, Client, R> IntoFuture for Insert<'r, Client, Vec<R>>
103where
104 Client: Connection,
105 R: DeserializeOwned,
106{
107 type Output = Result<Vec<R>>;
108 type IntoFuture = BoxFuture<'r, Self::Output>;
109
110 into_future! {execute_vec}
111}
112
113impl<'r, C, R> Insert<'r, C, R>
114where
115 C: Connection,
116 R: DeserializeOwned,
117{
118 pub fn content<D>(self, data: D) -> Content<'r, C, R>
120 where
121 D: Serialize + 'static,
122 {
123 Content::from_closure(self.client, || {
124 let mut data = to_core_value(data)?;
125 match self.resource? {
126 Resource::Table(table) => Ok(Command::Insert {
127 what: Some(table),
128 data,
129 }),
130 Resource::RecordId(thing) => {
131 if data.is_array() {
132 Err(Error::InvalidParams(
133 "Tried to insert multiple records on a record ID".to_owned(),
134 )
135 .into())
136 } else {
137 let thing = thing.into_inner();
138 if let CoreValue::Object(ref mut x) = data {
139 x.insert("id".to_string(), thing.id.into());
140 }
141
142 Ok(Command::Insert {
143 what: Some(thing.tb),
144 data,
145 })
146 }
147 }
148 Resource::Object(_) => Err(Error::InsertOnObject.into()),
149 Resource::Array(_) => Err(Error::InsertOnArray.into()),
150 Resource::Edge(_) => Err(Error::InsertOnEdges.into()),
151 Resource::Range(_) => Err(Error::InsertOnRange.into()),
152 Resource::Unspecified => Ok(Command::Insert {
153 what: None,
154 data,
155 }),
156 }
157 })
158 }
159}
160
161impl<'r, C, R> Insert<'r, C, R>
162where
163 C: Connection,
164 R: DeserializeOwned,
165{
166 pub fn relation<D>(self, data: D) -> InsertRelation<'r, C, R>
168 where
169 D: Serialize + 'static,
170 {
171 InsertRelation::from_closure(self.client, || {
172 let mut data = to_core_value(data)?;
173 match self.resource? {
174 Resource::Table(table) => Ok(Command::InsertRelation {
175 what: Some(table),
176 data,
177 }),
178 Resource::RecordId(thing) => {
179 if data.is_array() {
180 Err(Error::InvalidParams(
181 "Tried to insert multiple records on a record ID".to_owned(),
182 )
183 .into())
184 } else {
185 let thing = thing.into_inner();
186 if let CoreValue::Object(ref mut x) = data {
187 x.insert("id".to_string(), thing.id.into());
188 }
189
190 Ok(Command::InsertRelation {
191 what: Some(thing.tb),
192 data,
193 })
194 }
195 }
196 Resource::Unspecified => Ok(Command::InsertRelation {
197 what: None,
198 data,
199 }),
200 Resource::Object(_) => Err(Error::InsertOnObject.into()),
201 Resource::Array(_) => Err(Error::InsertOnArray.into()),
202 Resource::Edge(_) => Err(Error::InsertOnEdges.into()),
203 Resource::Range(_) => Err(Error::InsertOnRange.into()),
204 }
205 })
206 }
207}