1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
use std::{
path::{Path, PathBuf},
sync::{atomic, Arc},
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SourceType {
Schema,
Executable,
Document,
BuiltIn,
}
impl SourceType {
/// Returns `true` if the source type is [`BuiltIn`].
///
/// [`BuiltIn`]: SourceType::BuiltIn
#[must_use]
pub fn is_built_in(&self) -> bool {
matches!(self, Self::BuiltIn)
}
/// Returns `true` if the source type is [`Document`].
///
/// [`Document`]: SourceType::Document
#[must_use]
pub fn is_document(&self) -> bool {
matches!(self, Self::Document)
}
/// Returns `true` if the source type is [`Executable`].
///
/// [`Executable`]: SourceType::Executable
#[must_use]
pub fn is_executable(&self) -> bool {
matches!(self, Self::Executable)
}
/// Returns `true` if the source type is [`Schema`].
///
/// [`Schema`]: SourceType::Schema
#[must_use]
pub fn is_schema(&self) -> bool {
matches!(self, Self::Schema)
}
}
/// Represents a GraphQL source file.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct Source {
ty: SourceType,
filename: PathBuf,
text: Arc<str>,
}
impl Source {
/// Create a GraphQL schema source file.
pub fn schema(filename: PathBuf, text: impl Into<Arc<str>>) -> Self {
Self {
ty: SourceType::Schema,
filename,
text: text.into(),
}
}
/// Create a GraphQL executable source file.
pub fn executable(filename: PathBuf, text: impl Into<Arc<str>>) -> Self {
Self {
ty: SourceType::Executable,
filename,
text: text.into(),
}
}
/// Create a GraphQL document source file.
///
/// A Document can contain type definitions *and* executable definitions. You can also use it
/// when you don't know the actual source type.
pub fn document(filename: PathBuf, text: impl Into<Arc<str>>) -> Self {
Self {
ty: SourceType::Document,
filename,
text: text.into(),
}
}
/// Create a GraphQL type system file with built in types.
pub(crate) fn built_in(filename: PathBuf, text: impl Into<Arc<str>>) -> Self {
Self {
ty: SourceType::BuiltIn,
filename,
text: text.into(),
}
}
pub fn filename(&self) -> &Path {
&self.filename
}
pub fn source_type(&self) -> SourceType {
self.ty
}
pub fn text(&self) -> Arc<str> {
Arc::clone(&self.text)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct FileId {
id: u64,
}
impl From<u64> for FileId {
fn from(val: u64) -> Self {
Self { id: val }
}
}
/// The next file ID to use. This is global so file IDs do not conflict between different compiler
/// instances.
static NEXT: atomic::AtomicU64 = atomic::AtomicU64::new(1);
impl FileId {
// Returning a different value every time does not sound like good `impl Default`
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
id: NEXT.fetch_add(1, atomic::Ordering::Relaxed),
}
}
// Exposed for tests, but relying on the test order is probably not a good idea…
pub(crate) fn as_u64(self) -> u64 {
self.id
}
/// Reset file ID back to 1, used to get consistent results in tests.
#[allow(unused)]
pub(crate) fn reset() {
NEXT.store(1, atomic::Ordering::SeqCst);
}
}