surrealdb/api/method/
insert.rs

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/// An insert future
21#[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	/// Converts to an owned type which can easily be moved to a different thread
34	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	/// Specifies the data to insert into the table
119	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	/// Specifies the data to insert into the table
167	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}