syn-path 2.1.0

A simple macro to declare a syn::Path at compile time
Documentation
#![warn(rust_2018_idioms, unreachable_pub)]
#![deny(elided_lifetimes_in_paths)]
#![forbid(unsafe_code)]

//! This crate contains macros to construct [`syn`]-types that contain paths inside a
//! procedural macro.
//!
//!  - The [`path!`] macro constructs a [`syn::Path`].
//!  - The [`type_path!`] macro constructs a [`syn::TypePath`].
//!  - The [`ty!`] macro constructs a [`syn::Type`].

#[doc(hidden)]
pub mod private {
	pub use proc_macro2::{Ident, Span};
	pub use std::{
		boxed::Box,
		option::Option::{None, Some},
		stringify
	};
	pub use syn::{punctuated::Punctuated, Path, PathSegment, QSelf, Type, TypePath};

	#[inline]
	pub fn default<T: Default>() -> T {
		T::default()
	}

	#[inline]
	pub const fn len<T, const LEN: usize>(_: &[T; LEN]) -> usize {
		LEN
	}
}

/// This macro takes type paths of the form `my_crate::my_mod::FooBar` and
/// `<my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType` and
/// turns them into a [`syn::Type`].
#[macro_export]
macro_rules! ty {
	(:: $($segment:ident)::*) => {
		$crate::private::Type::Path($crate::type_path!(:: $($segment)::*))
	};
	($($segment:ident)::*) => {
		$crate::private::Type::Path($crate::type_path!($($segment)::*))
	};

	(< $($ty_segment:ident)::* > :: $($segment:ident)::*) => {
		$crate::private::Type::Path($crate::type_path!(<$($ty_segment)::*>::$($segment)::*))
	};
	(< :: $($ty_segment:ident)::* > :: $($segment:ident)::*) => {
		$crate::private::Type::Path($crate::type_path!(<:: $($ty_segment)::*>::$($segment)::*))
	};

	(< $($ty_segment:ident)::* as $($as_segment:ident)::* > :: $($segment:ident)::*) => {
		$crate::private::Type::Path(
			$crate::type_path!(<$($ty_segment)::* as $($as_segment)::*>::$($segment)::*)
		)
	};
	(< :: $($ty_segment:ident)::* as $($as_segment:ident)::* > :: $($segment:ident)::*) => {
		$crate::private::Type::Path(
			$crate::type_path!(<:: $($ty_segment)::* as $($as_segment)::*>::$($segment)::*)
		)
	};
	(< $($ty_segment:ident)::* as :: $($as_segment:ident)::* > :: $($segment:ident)::*) => {
		$crate::private::Type::Path(
			$crate::type_path!(<$($ty_segment)::* as :: $($as_segment)::*>::$($segment)::*)
		)
	};
	(< :: $($ty_segment:ident)::* as :: $($as_segment:ident)::* > :: $($segment:ident)::*) => {
		$crate::private::Type::Path(
			$crate::type_path!(<:: $($ty_segment)::* as :: $($as_segment)::*>::$($segment)::*)
		)
	};
}

/// This macro takes type paths of the form `my_crate::my_mod::FooBar` and
/// `<my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType` and
/// turns them into a [`syn::TypePath`].
#[macro_export]
macro_rules! type_path {
	(:: $($segment:ident)::*) => {
		$crate::type_path_impl!($crate::private::None, $crate::path!(:: $($segment)::*))
	};
	($($segment:ident)::*) => {
		$crate::type_path_impl!($crate::private::None, $crate::path!($($segment)::*))
	};

	(< $($ty_segment:ident)::* > :: $($segment:ident)::*) => {
		$crate::type_path_impl!(
			$crate::private::Some($crate::private::QSelf {
				lt_token: $crate::private::default(),
				ty: Box::new($crate::ty!($($ty_segment)::*)),
				position: 0,
				as_token: $crate::private::None,
				gt_token: $crate::private::default()
			}),
			$crate::path!(:: $($segment)::*)
		)
	};
	(< :: $($ty_segment:ident)::* > :: $($segment:ident)::*) => {
		$crate::type_path_impl!(
			$crate::private::Some($crate::private::QSelf {
				lt_token: $crate::private::default(),
				ty: Box::new($crate::ty!(:: $($ty_segment)::*)),
				position: 0,
				as_token: $crate::private::None,
				gt_token: $crate::private::default()
			}),
			$crate::path!(:: $($segment)::*)
		)
	};

	(< $($ty_segment:ident)::* as $($as_segment:ident)::* > :: $($segment:ident)::*) => {
		$crate::type_path_impl!(
			$crate::private::Some($crate::private::QSelf {
				lt_token: $crate::private::default(),
				ty: Box::new($crate::ty!($($ty_segment)::*)),
				position: $crate::private::len(&[$($crate::private::stringify!($as_segment)),*]),
				as_token: $crate::private::Some($crate::private::default()),
				gt_token: $crate::private::default()
			}),
			$crate::path!($($as_segment)::* :: $($segment)::*)
		)
	};
	(< :: $($ty_segment:ident)::* as $($as_segment:ident)::* > :: $($segment:ident)::*) => {
		$crate::type_path_impl!(
			$crate::private::Some($crate::private::QSelf {
				lt_token: $crate::private::default(),
				ty: Box::new($crate::ty!(:: $($ty_segment)::*)),
				position: $crate::private::len(&[$($crate::private::stringify!($as_segment)),*]),
				as_token: $crate::private::Some($crate::private::default()),
				gt_token: $crate::private::default()
			}),
			$crate::path!($($as_segment)::* :: $($segment)::*)
		)
	};
	(< $($ty_segment:ident)::* as :: $($as_segment:ident)::* > :: $($segment:ident)::*) => {
		$crate::type_path_impl!(
			$crate::private::Some($crate::private::QSelf {
				lt_token: $crate::private::default(),
				ty: Box::new($crate::ty!($($ty_segment)::*)),
				position: $crate::private::len(&[$($crate::private::stringify!($as_segment)),*]),
				as_token: $crate::private::Some($crate::private::default()),
				gt_token: $crate::private::default()
			}),
			$crate::path!(:: $($as_segment)::* :: $($segment)::*)
		)
	};
	(< :: $($ty_segment:ident)::* as :: $($as_segment:ident)::* > :: $($segment:ident)::*) => {
		$crate::type_path_impl!(
			$crate::private::Some($crate::private::QSelf {
				lt_token: $crate::private::default(),
				ty: Box::new($crate::ty!(:: $($ty_segment)::*)),
				position: $crate::private::len(&[$($crate::private::stringify!($as_segment)),*]),
				as_token: $crate::private::Some($crate::private::default()),
				gt_token: $crate::private::default()
			}),
			$crate::path!(:: $($as_segment)::* :: $($segment)::*)
		)
	};
}

#[macro_export]
#[doc(hidden)]
macro_rules! type_path_impl {
	($qself:expr, $path:expr) => {
		$crate::private::TypePath {
			qself: $qself,
			path: $path
		}
	};
}

/// This macro takes paths of the form `my_crate::my_mod::FooBar` and
/// `::my_crate::my_mod::FooBar` and turns them into a [`syn::Path`].
#[macro_export]
macro_rules! path {
	(:: $($segment:ident)::*) => {
		$crate::path_impl!($crate::private::Some($crate::private::default()), $($segment),*)
	};
	($($segment:ident)::*) => {
		$crate::path_impl!($crate::private::None, $($segment),*)
	};
}

#[macro_export]
#[doc(hidden)]
macro_rules! path_impl {
	($leading_colon:expr, $($segment:ident),*) => {
		{
			#[allow(unused_mut)]
			let mut segments: $crate::private::Punctuated<$crate::private::PathSegment, _> = $crate::private::default();
			$(
				segments.push($crate::private::PathSegment {
					ident: $crate::private::Ident::new(
						$crate::private::stringify!($segment),
						$crate::private::Span::call_site()
					),
					arguments: $crate::private::default()
				});
			)*
			$crate::private::Path {
				leading_colon: $leading_colon,
				segments
			}
		}
	};
}

#[cfg(test)]
mod tests {
	use std::fmt::Debug;
	use syn::parse::Parse;

	#[track_caller]
	fn assert_eq<T>(t: T, s: &str)
	where
		T: Debug + Eq + Parse
	{
		let expected: T = syn::parse_str(s).unwrap();
		assert_eq!(expected, t);
	}

	// ### ty! macro tests

	#[test]
	fn type_with_leading_colon() {
		assert_eq(
			ty!(::my_crate::my_mod::FooBar),
			"::my_crate::my_mod::FooBar"
		);
	}

	#[test]
	fn type_without_leading_colon() {
		assert_eq(ty!(my_crate::my_mod::FooBar), "my_crate::my_mod::FooBar");
	}

	#[test]
	fn type_with_qself_with_leading_colon() {
		assert_eq(
			ty!(<::my_crate::my_mod::FooBar>::MyType),
			"<::my_crate::my_mod::FooBar>::MyType"
		);
	}

	#[test]
	fn type_with_qself_without_leading_colon() {
		assert_eq(
			ty!(<my_crate::my_mod::FooBar>::MyType),
			"<my_crate::my_mod::FooBar>::MyType"
		);
	}

	#[test]
	fn type_with_qself_with_leading_colon_with_as_with_leading_colon() {
		assert_eq(
			ty!(<::my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType),
			"<::my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType"
		);
	}

	#[test]
	fn type_with_qself_with_leading_colon_with_as_without_leading_colon() {
		assert_eq(
			ty!(<::my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType),
			"<::my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType"
		);
	}

	#[test]
	fn type_with_qself_without_leading_colon_with_as_with_leading_colon() {
		assert_eq(
			ty!(<my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType),
			"<my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType"
		);
	}

	#[test]
	fn type_with_qself_without_leading_colon_with_as_without_leading_colon() {
		assert_eq(
			ty!(<my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType),
			"<my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType"
		);
	}

	// ### type_path! macro tests

	#[test]
	fn type_path_with_leading_colon() {
		assert_eq(
			type_path!(::my_crate::my_mod::FooBar),
			"::my_crate::my_mod::FooBar"
		);
	}

	#[test]
	fn type_path_without_leading_colon() {
		assert_eq(
			type_path!(my_crate::my_mod::FooBar),
			"my_crate::my_mod::FooBar"
		);
	}

	#[test]
	fn type_path_with_qself_with_leading_colon() {
		assert_eq(
			type_path!(<::my_crate::my_mod::FooBar>::MyType),
			"<::my_crate::my_mod::FooBar>::MyType"
		);
	}

	#[test]
	fn type_path_with_qself_without_leading_colon() {
		assert_eq(
			type_path!(<my_crate::my_mod::FooBar>::MyType),
			"<my_crate::my_mod::FooBar>::MyType"
		);
	}

	#[test]
	fn type_path_with_qself_with_leading_colon_with_as_with_leading_colon() {
		assert_eq(
			type_path!(
				<::my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType
			),
			"<::my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType"
		);
	}

	#[test]
	fn type_path_with_qself_with_leading_colon_with_as_without_leading_colon() {
		assert_eq(
			type_path!(<::my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType),
			"<::my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType"
		);
	}

	#[test]
	fn type_path_with_qself_without_leading_colon_with_as_with_leading_colon() {
		assert_eq(
			type_path!(<my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType),
			"<my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType"
		);
	}

	#[test]
	fn type_path_with_qself_without_leading_colon_with_as_without_leading_colon() {
		assert_eq(
			type_path!(<my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType),
			"<my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType"
		);
	}

	// ### path! macro tests

	#[test]
	fn path_with_leading_colon() {
		assert_eq(
			path!(::my_crate::my_mod::FooBar),
			"::my_crate::my_mod::FooBar"
		);
	}

	#[test]
	fn path_without_leading_colon() {
		assert_eq(path!(my_crate::my_mod::FooBar), "my_crate::my_mod::FooBar");
	}
}