1use crate::diagnostic::CliReport;
2use crate::diagnostic::ToCliReport;
3use crate::parser::FileId;
4use crate::parser::LineColumn;
5use crate::parser::SourceMap;
6use crate::parser::SourceSpan;
7use crate::parser::TaggedFileId;
8use crate::schema::ComponentName;
9use crate::schema::ComponentOrigin;
10use rowan::TextRange;
11use std::fmt;
12use std::marker::PhantomData;
13use std::mem::size_of;
14use std::mem::ManuallyDrop;
15use std::ops::Range;
16use std::ptr::NonNull;
17use std::sync::Arc;
18
19#[macro_export]
40macro_rules! name {
41 ($value: ident) => {
42 $crate::name!(stringify!($value))
43 };
44 ($value: expr) => {{
45 const _: () = { assert!($crate::Name::is_valid_syntax($value)) };
46 $crate::Name::new_static_unchecked(&$value)
47 }};
48}
49
50pub struct Name {
60 ptr: NonNull<u8>,
63 len: u32,
64 start_offset: u32, tagged_file_id: TaggedFileId, phantom: PhantomData<UnpackedRepr>,
67}
68
69#[allow(dead_code)] enum UnpackedRepr {
71 Heap(Arc<str>),
72 Static(&'static str),
73}
74
75#[derive(Clone, Eq, PartialEq, thiserror::Error)]
78#[error("`{name}` is not a valid GraphQL name")]
79pub struct InvalidNameError {
80 pub name: String,
81 pub location: Option<SourceSpan>,
82}
83
84const TAG_ARC: bool = true;
85const TAG_STATIC: bool = false;
86
87const _: () = {
88 assert!(size_of::<Name>() == 24);
91 assert!(size_of::<Name>() == size_of::<Option<Name>>());
92
93 const fn assert_send_and_sync<T: Send + Sync>() {}
95 assert_send_and_sync::<(UnpackedRepr, u32, TaggedFileId)>();
96};
97
98unsafe impl Send for Name {}
99
100unsafe impl Sync for Name {}
101
102impl Name {
103 pub fn new(value: &str) -> Result<Self, InvalidNameError> {
105 Self::check_valid_syntax(value)?;
106 Ok(Self::new_unchecked(value))
107 }
108
109 pub fn new_static(value: &'static str) -> Result<Self, InvalidNameError> {
111 Self::check_valid_syntax(value)?;
112 Ok(Self::new_static_unchecked(value))
113 }
114
115 pub fn new_unchecked(value: &str) -> Self {
120 Self::from_arc_unchecked(value.into())
121 }
122
123 pub fn from_arc_unchecked(arc: Arc<str>) -> Self {
128 let len = Self::new_len(&arc);
129 let ptr = Arc::into_raw(arc).cast_mut().cast();
130 let ptr = unsafe { NonNull::new_unchecked(ptr) };
132 Self {
133 ptr,
134 len,
135 start_offset: 0,
136 tagged_file_id: TaggedFileId::pack(TAG_ARC, FileId::NONE),
137 phantom: PhantomData,
138 }
139 }
140
141 pub const fn new_static_unchecked(value: &'static str) -> Self {
147 let ptr = value.as_ptr().cast_mut();
148 let ptr = unsafe { NonNull::new_unchecked(ptr) };
150 Self {
151 ptr,
152 len: Self::new_len(value),
153 start_offset: 0,
154 tagged_file_id: TaggedFileId::pack(TAG_STATIC, FileId::NONE),
155 phantom: PhantomData,
156 }
157 }
158
159 pub fn with_location(mut self, location: SourceSpan) -> Self {
161 debug_assert_eq!(location.text_range.len(), self.len.into());
162 self.start_offset = location.text_range.start().into();
163 self.tagged_file_id = TaggedFileId::pack(self.tagged_file_id.tag(), location.file_id);
164 self
165 }
166
167 const fn new_len(value: &str) -> u32 {
168 let len = value.len();
169 if len >= (u32::MAX as usize) {
170 panic!("Name length overflows 4 GiB")
171 }
172 len as _
173 }
174
175 pub fn location(&self) -> Option<SourceSpan> {
178 let file_id = self.tagged_file_id.file_id();
179 if file_id != FileId::NONE {
180 Some(SourceSpan {
181 file_id,
182 text_range: TextRange::at(self.start_offset.into(), self.len.into()),
183 })
184 } else {
185 None
186 }
187 }
188
189 pub fn line_column_range(&self, sources: &SourceMap) -> Option<Range<LineColumn>> {
191 self.location()?.line_column_range(sources)
192 }
193
194 #[allow(clippy::len_without_is_empty)] #[inline]
196 pub fn len(&self) -> usize {
197 self.len as _
198 }
199
200 #[inline]
201 pub fn as_str(&self) -> &str {
202 let slice = NonNull::slice_from_raw_parts(self.ptr, self.len());
203 unsafe { std::str::from_utf8_unchecked(slice.as_ref()) }
206 }
207
208 pub fn as_static_str(&self) -> Option<&'static str> {
213 if self.tagged_file_id.tag() == TAG_STATIC {
214 let raw_slice = NonNull::slice_from_raw_parts(self.ptr, self.len());
215 Some(unsafe { std::str::from_utf8_unchecked(raw_slice.as_ref()) })
218 } else {
219 None
220 }
221 }
222
223 fn as_arc(&self) -> Option<ManuallyDrop<Arc<str>>> {
224 if self.tagged_file_id.tag() == TAG_ARC {
225 let raw_slice = NonNull::slice_from_raw_parts(self.ptr, self.len())
226 .as_ptr()
227 .cast_const();
228
229 Some(ManuallyDrop::new(unsafe {
235 Arc::from_raw(raw_slice as *const str)
236 }))
237 } else {
238 None
239 }
240 }
241
242 pub fn to_cloned_arc(&self) -> Option<Arc<str>> {
246 self.as_arc()
247 .map(|manually_drop| Arc::clone(&manually_drop))
248 }
249
250 pub const fn is_valid_syntax(value: &str) -> bool {
253 let bytes = value.as_bytes();
254 let Some(&first) = bytes.first() else {
255 return false;
256 };
257 if !Self::is_name_start(first) {
258 return false;
259 }
260 let mut i = 1;
262 while i < bytes.len() {
263 if !Self::is_name_continue(bytes[i]) {
264 return false;
265 }
266 i += 1
267 }
268 true
269 }
270
271 fn check_valid_syntax(value: &str) -> Result<(), InvalidNameError> {
272 if Self::is_valid_syntax(value) {
273 Ok(())
274 } else {
275 Err(InvalidNameError {
276 name: value.to_owned(),
277 location: None,
278 })
279 }
280 }
281
282 const fn is_name_start(byte: u8) -> bool {
284 byte.is_ascii_alphabetic() || byte == b'_'
285 }
286
287 const fn is_name_continue(byte: u8) -> bool {
289 byte.is_ascii_alphanumeric() || byte == b'_'
290 }
291
292 pub fn to_component(&self, origin: ComponentOrigin) -> ComponentName {
293 ComponentName {
294 origin,
295 name: self.clone(),
296 }
297 }
298}
299
300impl Clone for Name {
301 fn clone(&self) -> Self {
302 if let Some(arc) = self.as_arc() {
303 let _ptr = Arc::into_raw(Arc::clone(&arc));
304 }
307 Self { ..*self }
308 }
309}
310
311impl Drop for Name {
312 fn drop(&mut self) {
313 if let Some(arc) = &mut self.as_arc() {
314 unsafe { ManuallyDrop::drop(arc) }
316 }
317 }
318}
319
320impl std::hash::Hash for Name {
321 #[inline]
322 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
323 self.as_str().hash(state) }
325}
326
327impl std::ops::Deref for Name {
328 type Target = str;
329
330 #[inline]
331 fn deref(&self) -> &Self::Target {
332 self.as_str()
333 }
334}
335
336impl AsRef<str> for Name {
337 #[inline]
338 fn as_ref(&self) -> &str {
339 self.as_str()
340 }
341}
342
343impl std::borrow::Borrow<str> for Name {
344 fn borrow(&self) -> &str {
345 self.as_str()
346 }
347}
348
349impl std::fmt::Debug for Name {
350 #[inline]
351 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
352 self.as_str().fmt(f)
353 }
354}
355
356impl std::fmt::Display for Name {
357 #[inline]
358 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
359 self.as_str().fmt(f)
360 }
361}
362
363impl Eq for Name {}
364
365impl PartialEq for Name {
366 #[inline]
367 fn eq(&self, other: &Self) -> bool {
368 self.as_str() == other.as_str() }
370}
371
372impl Ord for Name {
373 #[inline]
374 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
375 self.as_str().cmp(other.as_str())
376 }
377}
378
379impl PartialOrd for Name {
380 #[inline]
381 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
382 Some(self.cmp(other))
383 }
384}
385
386impl PartialEq<str> for Name {
387 #[inline]
388 fn eq(&self, other: &str) -> bool {
389 self.as_str() == other
390 }
391}
392
393impl PartialOrd<str> for Name {
394 #[inline]
395 fn partial_cmp(&self, other: &str) -> Option<std::cmp::Ordering> {
396 self.as_str().partial_cmp(other)
397 }
398}
399
400impl PartialEq<&'_ str> for Name {
401 #[inline]
402 fn eq(&self, other: &&'_ str) -> bool {
403 self.as_str() == *other
404 }
405}
406
407impl PartialOrd<&'_ str> for Name {
408 #[inline]
409 fn partial_cmp(&self, other: &&'_ str) -> Option<std::cmp::Ordering> {
410 self.as_str().partial_cmp(*other)
411 }
412}
413
414impl From<&'_ Self> for Name {
415 #[inline]
416 fn from(value: &'_ Self) -> Self {
417 value.clone()
418 }
419}
420
421impl From<Name> for Arc<str> {
422 fn from(value: Name) -> Self {
423 match value.to_cloned_arc() {
424 Some(arc) => arc,
425 None => value.as_str().into(),
426 }
427 }
428}
429
430impl TryFrom<Arc<str>> for Name {
431 type Error = InvalidNameError;
432
433 fn try_from(value: Arc<str>) -> Result<Self, Self::Error> {
434 Self::check_valid_syntax(&value)?;
435 Ok(Self::from_arc_unchecked(value))
436 }
437}
438
439impl serde::Serialize for Name {
440 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
441 where
442 S: serde::Serializer,
443 {
444 serializer.serialize_str(self.as_str())
445 }
446}
447
448impl<'de> serde::Deserialize<'de> for Name {
449 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
450 where
451 D: serde::Deserializer<'de>,
452 {
453 const EXPECTING: &str = "a string in GraphQL Name syntax";
454 struct Visitor;
455 impl serde::de::Visitor<'_> for Visitor {
456 type Value = Name;
457
458 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
459 formatter.write_str(EXPECTING)
460 }
461
462 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
463 where
464 E: serde::de::Error,
465 {
466 Name::new(v)
467 .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(v), &EXPECTING))
468 }
469 }
470 deserializer.deserialize_str(Visitor)
471 }
472}
473
474impl TryFrom<&str> for Name {
475 type Error = InvalidNameError;
476
477 fn try_from(value: &str) -> Result<Self, Self::Error> {
478 Self::new(value)
479 }
480}
481
482impl TryFrom<String> for Name {
483 type Error = InvalidNameError;
484
485 fn try_from(value: String) -> Result<Self, Self::Error> {
486 Self::new(&value)
487 }
488}
489
490impl TryFrom<&'_ String> for Name {
491 type Error = InvalidNameError;
492
493 fn try_from(value: &'_ String) -> Result<Self, Self::Error> {
494 Self::new(value)
495 }
496}
497
498impl AsRef<Name> for Name {
499 fn as_ref(&self) -> &Name {
500 self
501 }
502}
503
504impl ToCliReport for InvalidNameError {
505 fn location(&self) -> Option<SourceSpan> {
506 self.location
507 }
508 fn report(&self, report: &mut CliReport) {
509 report.with_label_opt(self.location, "cannot be parsed as a GraphQL Name");
510 }
511}
512
513impl fmt::Debug for InvalidNameError {
514 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
515 fmt::Display::fmt(self, f)
516 }
517}