use proc_macro2::Span;
use std::iter::Peekable;
use std::{fmt::Display, sync::Arc};
use syn::{parse_quote, Ident, PathSegment, TypePath};
use crate::known_types::KNOWN_TYPES;
pub(crate) fn make_ident<S: AsRef<str>>(id: S) -> Ident {
Ident::new(id.as_ref(), Span::call_site())
}
#[derive(Debug, PartialEq, PartialOrd, Eq, Hash, Clone)]
#[allow(clippy::rc_buffer)]
pub struct Namespace(Arc<Vec<String>>);
impl Namespace {
pub(crate) fn new() -> Self {
Self(Arc::new(Vec::new()))
}
#[must_use]
pub(crate) fn push(&self, segment: String) -> Self {
let mut bigger = (*self.0).clone();
bigger.push(segment);
Namespace(Arc::new(bigger))
}
pub(crate) fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub(crate) fn iter(&self) -> impl Iterator<Item = &String> {
self.0.iter()
}
#[cfg(test)]
pub(crate) fn from_user_input(input: &str) -> Self {
Self(Arc::new(input.split("::").map(|x| x.to_string()).collect()))
}
pub(crate) fn depth(&self) -> usize {
self.0.len()
}
pub(crate) fn to_display_suffix(&self) -> String {
if self.is_empty() {
String::new()
} else {
format!(" (in namespace {})", self)
}
}
}
impl Display for Namespace {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0.join("::"))
}
}
impl<'a> IntoIterator for &'a Namespace {
type Item = &'a String;
type IntoIter = std::slice::Iter<'a, String>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[derive(Debug, PartialEq, PartialOrd, Eq, Hash, Clone)]
pub struct TypeName(Namespace, String);
impl TypeName {
pub(crate) fn from_type_path(typ: &TypePath) -> Self {
let mut seg_iter = typ.path.segments.iter().peekable();
let first_seg = seg_iter.next().unwrap().ident.clone();
if first_seg == "root" {
Self::from_segments(seg_iter) } else {
Self::from_segments(typ.path.segments.iter().peekable())
}
}
fn from_segments<'a, T: Iterator<Item = &'a PathSegment>>(mut seg_iter: Peekable<T>) -> Self {
let mut ns = Namespace::new();
while let Some(seg) = seg_iter.next() {
if seg_iter.peek().is_some() {
ns = ns.push(seg.ident.to_string());
} else {
return Self(ns, seg.ident.to_string());
}
}
unreachable!()
}
pub(crate) fn new(ns: &Namespace, id: &str) -> Self {
Self(ns.clone(), id.to_string())
}
pub(crate) fn new_from_user_input(id: &str) -> Self {
let mut seg_iter = id.split("::").peekable();
let mut ns = Namespace::new();
while let Some(seg) = seg_iter.next() {
if seg_iter.peek().is_some() {
ns = ns.push(seg.to_string());
} else {
return Self(ns, seg.to_string());
}
}
unreachable!()
}
pub(crate) fn get_final_ident(&self) -> &str {
&self.1
}
pub(crate) fn has_namespace(&self) -> bool {
!self.0.is_empty()
}
pub(crate) fn get_namespace(&self) -> &Namespace {
&self.0
}
pub(crate) fn to_cpp_name(&self) -> String {
let special_cpp_name = KNOWN_TYPES.special_cpp_name(&self);
match special_cpp_name {
Some(name) => name,
None => {
let mut s = String::new();
for seg in &self.0 {
s.push_str(&seg);
s.push_str("::");
}
s.push_str(&self.1);
s
}
}
}
pub(crate) fn to_type_path(&self) -> TypePath {
if let Some(known_type_path) = KNOWN_TYPES.known_type_type_path(self) {
known_type_path
} else {
let root = "root".to_string();
let segs = std::iter::once(&root)
.chain(self.ns_segment_iter())
.chain(std::iter::once(&self.1))
.map(make_ident);
parse_quote! {
#(#segs)::*
}
}
}
pub(crate) fn ns_segment_iter(&self) -> impl Iterator<Item = &String> {
self.0.iter()
}
pub(crate) fn is_cvoid(&self) -> bool {
self.to_cpp_name() == "std::os::raw::c_void"
}
}
impl Display for TypeName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for seg in &self.0 {
f.write_str(&seg)?;
f.write_str("::")?;
}
f.write_str(&self.1)
}
}
#[cfg(test)]
mod tests {
use super::TypeName;
#[test]
fn test_ints() {
assert_eq!(TypeName::new_from_user_input("i8").to_cpp_name(), "int8_t");
assert_eq!(
TypeName::new_from_user_input("u64").to_cpp_name(),
"uint64_t"
);
}
}