/*!
# Adbyss: PSL
*/

// A number of suffix-related pieces are generated by `build.rs`, including:
// * Constant slices of `(&[u8], SuffixKind)` used for binary searching;
// * The `MAX_PARTS` constant defining the upper limit of possible suffix parts;
// * The `WildKind` enum and corresponding exception matching;
// * The `SuffixKind::from_part` method that matches a byte slice to a `SuffixKind`;
include!(concat!(env!("OUT_DIR"), "/adbyss-psl.rs"));



#[derive(Clone, Copy)]
/// # Suffix Kinds.
///
/// All valid suffixes will be one of the following:
/// * `Tld`: it is what it is.
/// * `Wild`: it is itself and whatever part appears before it.
/// * `WildEx`: it is itself and whatever part appears before it, unless that part matches an exception.
pub(super) enum SuffixKind {
	/// # Normal TLD.
	Tld,

	/// # Wildcard TLD.
	Wild,

	/// # Wildcard Exception.
	WildEx(WildKind),
}

impl SuffixKind {
	/// # Into Suffix Parts.
	///
	/// Return an array containing increasingly larger (potential) suffix
	/// subdomains, e.g. "com", "domain.com", "www.domain.com", etc.
	///
	/// No public suffixes have more than `MAX_PARTS`, so there's no point
	/// slicing mega-sub-domains any further than that.
	pub(super) fn suffix_parts(src: &[u8]) -> [Option<&[u8]>; MAX_PARTS] {
		let mut out = [None; MAX_PARTS];
		for (o, part) in out.iter_mut().zip(SuffixPartIter::new(src)) {
			o.replace(part);
		}
		out
	}
}



/// # Domain Part Iterator.
///
/// Split a domain into increasingly larger domain parts, working backwards
/// from the end, e.g. "com", "domain.com", "www.domain.com", etc.
struct SuffixPartIter<'a> {
	/// # Full domain.
	domain: &'a [u8],

	/// # Current Position.
	pos: usize,
}

impl<'a> SuffixPartIter<'a> {
	/// # New Instance.
	const fn new(domain: &'a [u8]) -> Self {
		Self {
			domain,
			pos: domain.len().saturating_sub(1)
		}
	}
}

impl<'a> Iterator for SuffixPartIter<'a> {
	type Item = &'a [u8];

	fn next(&mut self) -> Option<Self::Item> {
		// This can't fail.
		if self.domain.len() <= self.pos { return None; }

		while 0 != self.pos {
			self.pos -= 1;

			// If we've reached the beginning, return the whole thing.
			if self.pos == 0 { return Some(self.domain); }

			// If we've reached a dot, return everything after it.
			if self.domain[self.pos] == b'.' {
				return Some(&self.domain[self.pos + 1..]);
			}
		}

		// There's nothing more to return.
		None
	}
}

impl std::iter::FusedIterator for SuffixPartIter<'_> {}



#[cfg(test)]
mod test {
	use super::*;

	#[test]
	fn t_suffix_part_iter() {
		let mut iter = SuffixPartIter::new(b"one.two.three.four.com");
		assert_eq!(iter.next(), Some(b"com".as_slice()));
		assert_eq!(iter.next(), Some(b"four.com".as_slice()));
		assert_eq!(iter.next(), Some(b"three.four.com".as_slice()));
		assert_eq!(iter.next(), Some(b"two.three.four.com".as_slice()));
		assert_eq!(iter.next(), Some(b"one.two.three.four.com".as_slice()));
		assert!(iter.next().is_none());
	}
}
