#![doc(hidden)]

/// A wrapper for `bitflags!` which adds a `FromStr` implementation.
#[macro_export]
macro_rules! c_bitfield
{
	(
		$(#[$outer:meta])*
		pub struct $t:ident: $ti:ty {
			$(
				$(#[$inner:ident $($args:tt)*])*
				$f:ident = $v:expr
			),+
			$(,)?
		}
	) => {
		bitflags! {
			$(#[$outer])*
			pub struct $t: $ti {
				$(
					$(#[$inner $($args)*])*
					const $f = 1 << $v;
				)+
			}
		}

		#[allow(unused_qualifications)]
		impl std::str::FromStr for $t
		{
			type Err = $crate::err::ParseFlagError;

			fn from_str(s: &str) -> Result<Self, Self::Err>
			{
				let mut flags = Self::empty();

				for s in s.split('|') {
					match s {
						$(
							stringify!($f) => flags.insert(Self::$f),
						)+
						"(none)" => (),
						_        => return Err(Self::Err::new(stringify!($t)))
					}
				}

				Ok(flags)
			}
		}
	}
}

#[cfg(test)]
mod test
{
	use crate::err::ParseFlagError;
	use std::str::FromStr;

	c_bitfield! {
		pub struct TestFlag: u16 {
			ZERO = 0,
			ONE  = 1,
			TWO  = 2,
		}
	}

	#[test]
	fn c_bitfield()
	{
		assert_eq!(TestFlag::from_bits(0), Some(TestFlag::empty()));
		assert_eq!(TestFlag::from_bits(1), Some(TestFlag::ZERO));
		assert_eq!(TestFlag::from_bits(2), Some(TestFlag::ONE));
		assert_eq!(TestFlag::from_bits(4), Some(TestFlag::TWO));
		assert_eq!(TestFlag::from_bits(8), None);
		assert_eq!(TestFlag::from_str("(none)"), Ok(TestFlag::empty()));
		assert_eq!(TestFlag::from_str("ZERO"), Ok(TestFlag::ZERO));
		assert_eq!(TestFlag::from_str("ONE"), Ok(TestFlag::ONE));
		assert_eq!(TestFlag::from_str("TWO"), Ok(TestFlag::TWO));
		assert_eq!(TestFlag::from_str("ZERO|ONE|TWO"), Ok(TestFlag::all()));
		assert_eq!(TestFlag::from_str("TWO|ZERO|ONE"), Ok(TestFlag::all()));
		assert_eq!(TestFlag::from_str("ONE|ONE|ONE"), Ok(TestFlag::ONE));
		assert_eq!(TestFlag::from_str("(none)|(none)"), Ok(TestFlag::empty()));
		assert_eq!(TestFlag::from_str("(none)|ONE"), Ok(TestFlag::ONE));
		assert_eq!(TestFlag::from_str("THREE"),
		           Err(ParseFlagError::new("TestFlag")));
	}
}

// EOF
