databoard 0.4.0

Provides a hierarchical key-value-store
Documentation
// Copyright © 2025 Stephan Kunz
//! The [`Databoard`](crate::Databoard) error handling.

use thiserror::Error;

use crate::ConstString;

/// Things that may go wrong using the [`Databoard`](crate::Databoard).
#[derive(Error, Debug)]
pub enum Error {
	/// Entry already exists.
	#[error("cannot create entry with name '{name}' as it already exist")]
	AlreadyExists {
		/// Name of the entry to create.
		name: ConstString,
	},
	/// Name is already remapped.
	#[error("name '{name}' is already remapped as '{remapped}'")]
	AlreadyRemapped {
		/// Name to be remapped.
		name: ConstString,
		/// The already existing remapping.
		remapped: ConstString,
	},
	/// Remapping is a value assignment.
	#[error("remapping of '{name}' contains an assignment of {value}")]
	Assignment {
		/// Name to be remapped.
		name: ConstString,
		/// Value to be asssigned.
		value: ConstString,
	},
	/// Conversion From str failed.
	#[error("could not convert '{value}' to type of port {port}")]
	ConversionFromStr {
		/// Content of str.
		value: ConstString,
		/// Affected port.
		port: ConstString,
	},
	/// Value cannot be converted into a [`RemappingTarget`](crate::RemappingTarget).
	#[error("cannot create a remapping target from '{name}'")]
	CreateRemapping {
		/// 'str' to convert into target.
		name: ConstString,
	},
	/// Entry has different type.
	#[error("the entry for the name '{name}' is stored with a different type")]
	DataType {
		/// Name of the wanted entry.
		name: ConstString,
	},
	/// Entry with is locked.
	#[error("the entry '{name}' is locked")]
	IsLocked {
		/// Name of the wanted entry.
		name: ConstString,
	},
	/// Defined a remapping without giving a parent.
	#[error("remapping of '{name}' to '{remapped}' without a parent board")]
	NoParent {
		/// Original name.
		name: ConstString,
		/// Defined remapping.
		remapped: ConstString,
	},
	/// Defined a remapping without valid target.
	#[error("no remapping target for '{name}' defined")]
	NoTarget {
		/// Original name.
		name: ConstString,
	},
	/// Entry not stored.
	#[error("an entry for the name '{name}' is not existing")]
	NotFound {
		/// Name of the wanted entry.
		name: ConstString,
	},
	/// Entry has no value.
	#[error("port '{name}' has no valid value set")]
	NoValue {
		/// Name of the wanted entry.
		name: ConstString,
	},
	/// Entry has unknown type.
	#[error("port '{port}' has an unkonwn type")]
	UnknownType {
		/// Name of the wanted entry.
		port: ConstString,
	},

	/// Something impossible happened.
	#[error("an unexpected error occured in '{0}' at line '{1}'")]
	Unreachable(ConstString, u32),
}

impl From<Error> for dataport::Error {
	fn from(value: Error) -> Self {
		match value {
			Error::AlreadyExists { .. } | Error::AlreadyRemapped { .. } => Self::AlreadyExists,
			Error::Assignment { name, value } => Self::ReturnTypeMismatch { port: name, value },
			Error::CreateRemapping { .. } => Self::ParseValue,
			Error::ConversionFromStr { .. } => Self::FromStr,
			Error::IsLocked { .. } => Self::IsLocked,
			Error::NoParent { name, .. } | Error::NoTarget { name } | Error::NotFound { name } => Self::NotFound { name },
			Error::NoValue { .. } => Self::NoValueSet,
			Error::DataType { .. } | Error::UnknownType { .. } => Self::DataType,
			Error::Unreachable(name, _line) => Self::NotFound { name }, // @TODO: what needs to be done here exactly?
		}
	}
}

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

	// check, that the auto traits are available
	const fn is_normal<T: Sized + Send + Sync>() {}

	#[test]
	const fn normal_types() {
		is_normal::<Error>();
	}

	#[test]
	fn from_already_exists() {
		let err = Error::AlreadyExists { name: "x".into() };
		assert!(matches!(dataport::Error::from(err), dataport::Error::AlreadyExists));
	}

	#[test]
	fn from_already_remapped() {
		let err = Error::AlreadyRemapped {
			name: "x".into(),
			remapped: "y".into(),
		};
		assert!(matches!(dataport::Error::from(err), dataport::Error::AlreadyExists));
	}

	#[test]
	fn from_assignment() {
		let err = Error::Assignment {
			name: "p".into(),
			value: "v".into(),
		};
		assert!(matches!(
			dataport::Error::from(err),
			dataport::Error::ReturnTypeMismatch { .. }
		));
	}

	#[test]
	fn from_create_remapping() {
		let err = Error::CreateRemapping { name: "x".into() };
		assert!(matches!(dataport::Error::from(err), dataport::Error::ParseValue));
	}

	#[test]
	fn from_conversion_from_str() {
		let err = Error::ConversionFromStr {
			value: "v".into(),
			port: "p".into(),
		};
		assert!(matches!(dataport::Error::from(err), dataport::Error::FromStr));
	}

	#[test]
	fn from_is_locked() {
		let err = Error::IsLocked { name: "x".into() };
		assert!(matches!(dataport::Error::from(err), dataport::Error::IsLocked));
	}

	#[test]
	fn from_no_parent() {
		let err = Error::NoParent {
			name: "x".into(),
			remapped: "y".into(),
		};
		assert!(matches!(dataport::Error::from(err), dataport::Error::NotFound { .. }));
	}

	#[test]
	fn from_no_target() {
		let err = Error::NoTarget { name: "x".into() };
		assert!(matches!(dataport::Error::from(err), dataport::Error::NotFound { .. }));
	}

	#[test]
	fn from_not_found() {
		let err = Error::NotFound { name: "x".into() };
		assert!(matches!(dataport::Error::from(err), dataport::Error::NotFound { .. }));
	}

	#[test]
	fn from_no_value() {
		let err = Error::NoValue { name: "x".into() };
		assert!(matches!(dataport::Error::from(err), dataport::Error::NoValueSet));
	}

	#[test]
	fn from_data_type() {
		let err = Error::DataType { name: "x".into() };
		assert!(matches!(dataport::Error::from(err), dataport::Error::DataType));
	}

	#[test]
	fn from_unreachable() {
		let err = Error::Unreachable("x".into(), 42);
		assert!(matches!(dataport::Error::from(err), dataport::Error::NotFound { .. }));
	}
}