surrealdb_core/expr/
ident.rs

1use std::fmt::{self, Display, Formatter};
2use std::ops::Deref;
3use std::str;
4
5use anyhow::Result;
6use revision::revisioned;
7use serde::{Deserialize, Serialize};
8
9use crate::expr::Value;
10use crate::expr::escape::EscapeIdent;
11use crate::expr::statements::info::InfoStructure;
12use crate::val::strand::no_nul_bytes;
13use crate::val::{Strand, Table};
14
15#[revisioned(revision = 1)]
16#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
17#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
18pub struct Ident(#[serde(with = "no_nul_bytes")] String);
19
20impl Ident {
21	/// Create a new identifier
22	///
23	/// This function checks if the string has a null byte, returns None if it
24	/// has.
25	pub fn new(str: String) -> Option<Self> {
26		if str.contains('\0') {
27			return None;
28		}
29		Some(Ident(str))
30	}
31
32	/// Create a new identifier
33	///
34	/// This function checks if the string has a null byte, returns an error if it
35	/// has.
36	pub fn try_new(str: String) -> Result<Self> {
37		if str.contains('\0') {
38			return Err(anyhow::anyhow!("String contains null byte"));
39		}
40		Ok(Ident(str))
41	}
42
43	/// Create a new identifier
44	///
45	/// # Safety
46	/// Caller should ensure that the string does not contain a null byte.
47	pub unsafe fn new_unchecked(s: String) -> Self {
48		debug_assert!(!s.as_bytes().contains(&0));
49		Ident(s)
50	}
51
52	pub fn from_strand(str: Strand) -> Self {
53		Ident(str.into_string())
54	}
55
56	/// Convert ident into a strand.
57	pub fn into_strand(self) -> Strand {
58		// Safety: both ident and Strand uphold the no-null byte invariant.
59		unsafe { Strand::new_unchecked(self.0) }
60	}
61
62	// Convert into a string.
63	pub fn into_string(self) -> String {
64		self.0
65	}
66
67	/// Returns the slice of the underlying string.
68	pub fn as_str(&self) -> &str {
69		self.0.as_str()
70	}
71
72	/// Convert the Ident to a raw String
73	pub fn as_raw_string(&self) -> String {
74		self.0.clone()
75	}
76
77	/// Checks if this field is the `type` field
78	pub(crate) fn is_type(&self) -> bool {
79		self.0.as_str() == "type"
80	}
81	/// Checks if this field is the `coordinates` field
82	pub(crate) fn is_coordinates(&self) -> bool {
83		self.0.as_str() == "coordinates"
84	}
85	/// Checks if this field is the `geometries` field
86	pub(crate) fn is_geometries(&self) -> bool {
87		self.0.as_str() == "geometries"
88	}
89}
90
91impl Deref for Ident {
92	type Target = str;
93	fn deref(&self) -> &Self::Target {
94		&self.0
95	}
96}
97
98impl Display for Ident {
99	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
100		EscapeIdent(&self.0).fmt(f)
101	}
102}
103
104impl From<Table> for Ident {
105	fn from(value: Table) -> Self {
106		Ident(value.into_string())
107	}
108}
109
110impl InfoStructure for Ident {
111	fn structure(self) -> Value {
112		self.as_raw_string().into()
113	}
114}