looklook 0.5.0

Descriptive signal synthesiser.
// Copyright 2026 Gabriel Bjørnager Jensen.
//
// This file is part of LOOKLOOK.
//
// LOOKLOOK is free software: you can redistribute
// it and/or modify it under the terms of the GNU
// Affero General Public License as published by
// the Free Software Foundation, either version 3
// of the License, or (at your option) any later
// version.
//
// LOOKLOOK is distributed in the hope that it will
// be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Af-
// fero General Public License along with LOOKLOOK.
// If not, see <https://www.gnu.org/licenses/>.

//! The [`mode::Builder`] type.
//!
//! [`mode::Builder`]: Builder

use crate::mode::Mode;

/// A mode builder.
#[must_use]
#[derive(Debug)]
#[derive_const(Clone, Default)]
pub struct Builder {
	/// The sampling depth.
	pub sample_depth: Option<u32>,

	/// The amount of channels.
	pub channel_count: Option<u32>,

	/// The frame height.
	pub width: Option<u32>,

	/// The frame height.
	pub height: Option<u32>,

	/// The total count of frames.
	pub duration: Option<u64>,
}

impl Builder {
	/// Constructs a new mode builder.
	#[inline]
	pub const fn new() -> Self {
		Default::default()
	}

	/// Specifies the sampling depth.
	///
	/// # Panics
	///
	/// This method will panic if the depth is already
	/// specified.
	#[inline]
	pub const fn sample_depth(mut self, sample_depth: u32) -> Self {
		assert!(
			self.sample_depth.is_none(),
			"sampling depth is already specified",
		);

		self.sample_depth = Some(sample_depth);
		self
	}

	/// Specifies the channel count.
	///
	/// # Panics
	///
	/// This method will panic if the channel count is
	/// already specified.
	#[inline]
	pub const fn channel_count(mut self, channel_count: u32) -> Self {
		assert!(
			self.channel_count.is_none(),
			"channel count is already specified",
		);

		self.channel_count = Some(channel_count);
		self
	}

	/// Specifies the frame width.
	///
	/// For non-video renders, this value should be `1`.
	///
	/// # Panics
	///
	/// This method will panic if the frame width is
	/// already specified.
	#[inline]
	pub const fn width(mut self, width: u32) -> Self {
		assert!(
			self.width.is_none(),
			"frame width is already specified",
		);

		self.width = Some(width);
		self
	}

	/// Specifies the frame height.
	///
	/// For non-video renders, this value should be `1`.
	///
	/// # Panics
	///
	/// This method will panic if the frame height is
	/// already specified.
	#[inline]
	pub const fn height(mut self, height: u32) -> Self {
		assert!(
			self.height.is_none(),
			"frame height is already specified",
		);

		self.height = Some(height);
		self
	}

	/// Specifies the render duration.
	///
	/// The provided value is measured in count of
	/// frames. For non-video renders, this should
	/// simply be the total sample count.
	///
	/// # Panics
	///
	/// This method will panic if the duration is
	/// already specified.
	#[inline]
	pub const fn duration(mut self, duration: u64) -> Self {
		assert!(
			self.duration.is_none(),
			"frame height is already specified",
		);

		self.duration = Some(duration);
		self
	}

	/// Builds the mode.
	///
	/// # Panics
	///
	/// This method will panic in the following cases:
	///
	/// * `sample_depth` is unspecified, zero, or
	///   greater than `4`;
	/// * `channel_count` is unspecified, zero, or
	///   greater than `64`;
	/// * `width` is unspecified, zero, or greater than
	///   `32768`;
	/// * `height` is unspecified, zero, or greater than
	///   `32768`;
	/// * `duration` is unspecified;
	/// * The total sample count is greater than
	///   [`isize::MAX`].
	#[inline]
	#[must_use]
	pub const fn build(self) -> Mode {
		Mode::from_builder(self)
	}
}