pub(crate) mod parse;
use std::{
fmt::{self, Debug, Display},
mem,
sync::Arc,
};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct Symbol(Arc<String>);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SymbolName(Arc<String>);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Context(Arc<String>);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RelativeContext(Arc<String>);
const _: () = assert!(mem::size_of::<Symbol>() == mem::size_of::<usize>());
const _: () = assert!(mem::align_of::<Symbol>() == mem::align_of::<usize>());
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SymbolRef<'s>(&'s str);
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SymbolNameRef<'s>(&'s str);
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ContextRef<'s>(pub(super) &'s str);
impl From<&Symbol> for Symbol {
fn from(sym: &Symbol) -> Self {
sym.clone()
}
}
impl Symbol {
pub fn try_new(input: &str) -> Option<Self> {
let sym_ref = SymbolRef::try_new(input)?;
Some(sym_ref.to_symbol())
}
#[track_caller]
pub fn new(input: &str) -> Self {
match Symbol::try_new(input) {
Some(symbol) => symbol,
None => panic!("string is not parseable as a symbol: {}", input),
}
}
pub fn as_symbol_ref(&self) -> SymbolRef {
let Symbol(arc_string) = self;
SymbolRef(arc_string.as_str())
}
pub fn context(&self) -> ContextRef {
self.as_symbol_ref().context()
}
pub fn symbol_name(&self) -> SymbolNameRef {
self.as_symbol_ref().symbol_name()
}
}
impl SymbolName {
pub fn try_new(input: &str) -> Option<SymbolName> {
SymbolNameRef::try_new(input)
.as_ref()
.map(SymbolNameRef::to_symbol_name)
}
pub fn as_symbol_name_ref(&self) -> SymbolNameRef {
SymbolNameRef(self.as_str())
}
}
impl Context {
pub fn try_new(input: &str) -> Option<Self> {
let context_ref = ContextRef::try_new(input)?;
Some(context_ref.to_context())
}
#[track_caller]
pub fn new(input: &str) -> Self {
match Context::try_new(input) {
Some(context) => context,
None => panic!("string is not parseable as a context: {}", input),
}
}
pub fn global() -> Self {
Context(Arc::new(String::from("Global`")))
}
pub fn system() -> Self {
Context(Arc::new(String::from("System`")))
}
pub fn join(&self, name: SymbolNameRef) -> Context {
let Context(context) = self;
Context::try_new(&format!("{}{}`", context, name.as_str()))
.expect("Context::join(): invalid Context")
}
pub fn components(&self) -> Vec<SymbolNameRef> {
let Context(string) = self;
let comps: Vec<SymbolNameRef> = string
.split('`')
.filter(|comp| !comp.is_empty())
.map(|comp| {
SymbolNameRef::try_new(comp)
.expect("Context::components(): invalid context component")
})
.collect();
comps
}
pub fn as_context_ref(&self) -> ContextRef {
ContextRef(self.as_str())
}
pub fn from_symbol_name(name: &SymbolName) -> Self {
Context::try_new(&format!("{}`", name)).unwrap()
}
}
impl RelativeContext {
pub fn try_new(input: &str) -> Option<Self> {
crate::symbol::parse::RelativeContext_try_new(input)
}
pub fn components(&self) -> Vec<SymbolNameRef> {
let RelativeContext(string) = self;
let comps: Vec<SymbolNameRef> = string
.split('`')
.filter(|comp| !comp.is_empty())
.map(|comp| {
SymbolNameRef::try_new(comp)
.expect("RelativeContext::components(): invalid context component")
})
.collect();
comps
}
}
macro_rules! common_impls {
(impl $ty:ident) => {
impl Display for $ty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let $ty(string) = self;
write!(f, "{}", string)
}
}
impl $ty {
pub fn as_str(&self) -> &str {
let $ty(string) = self;
string.as_str()
}
pub(crate) unsafe fn unchecked_new<S: Into<String>>(input: S) -> $ty {
let inner: Arc<String> = Arc::new(input.into());
$ty(inner)
}
}
};
}
common_impls!(impl Symbol);
common_impls!(impl SymbolName);
common_impls!(impl Context);
common_impls!(impl RelativeContext);
impl<'s> SymbolRef<'s> {
pub fn try_new(string: &'s str) -> Option<Self> {
crate::symbol::parse::SymbolRef_try_new(string)
}
pub fn as_str(&self) -> &'s str {
let SymbolRef(string) = self;
string
}
pub fn to_symbol(&self) -> Symbol {
let SymbolRef(string) = self;
unsafe { Symbol::unchecked_new(string.to_owned()) }
}
#[doc(hidden)]
pub unsafe fn unchecked_new(string: &'s str) -> Self {
SymbolRef(string)
}
pub fn context(&self) -> ContextRef<'s> {
let string = self.as_str();
let last_grave = string
.rfind('`')
.expect("Failed to find grave '`' character in symbol");
let (context, _) = string.split_at(last_grave + 1);
unsafe { ContextRef::unchecked_new(context) }
}
pub fn symbol_name(&self) -> SymbolNameRef<'s> {
let string = self.as_str();
let last_grave = string
.rfind('`')
.expect("Failed to find grave '`' character in symbol");
let (_, name) = string.split_at(last_grave + 1);
unsafe { SymbolNameRef::unchecked_new(name) }
}
}
impl<'s> SymbolNameRef<'s> {
pub fn try_new(string: &'s str) -> Option<Self> {
crate::symbol::parse::SymbolNameRef_try_new(string)
}
pub fn as_str(&self) -> &'s str {
let SymbolNameRef(string) = self;
string
}
pub fn to_symbol_name(&self) -> SymbolName {
let SymbolNameRef(string) = self;
unsafe { SymbolName::unchecked_new(string.to_owned()) }
}
#[doc(hidden)]
pub unsafe fn unchecked_new(string: &'s str) -> Self {
SymbolNameRef(string)
}
}
impl<'s> ContextRef<'s> {
pub fn try_new(string: &'s str) -> Option<Self> {
crate::symbol::parse::ContextRef_try_new(string)
}
pub fn as_str(&self) -> &'s str {
let ContextRef(string) = self;
string
}
pub fn to_context(&self) -> Context {
let ContextRef(string) = self;
unsafe { Context::unchecked_new(string.to_owned()) }
}
#[doc(hidden)]
pub unsafe fn unchecked_new(string: &'s str) -> Self {
ContextRef(string)
}
}
impl Display for SymbolNameRef<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}