lib-humus-configuration 0.2.0

Helper crate for reading configuration files into data structures using serde.
Documentation
// SPDX-FileCopyrightText: 2025 Slatian
//
// SPDX-License-Identifier: LGPL-3.0-or-later

use serde::{Deserialize, Serialize};

use std::fmt;
use std::fmt::Display;
use std::path::Path;

/// Enumerates supported configuration formats.
///
/// **⚠️ Warning:** Available variants depend on the enabled feature flags, don't do exhaustive matching on them in libraries.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ConfigFormat {
	/// JSON
	///
	/// Detected via the `.json` file extension.
	#[cfg(feature = "json")]
	Json,

	/// [JSON5](https://json5.org/)
	///
	/// Detected via the `.json5` file extension.
	#[cfg(feature = "json5")]
	Json5,

	/// [TOML](https://toml.io/)
	///
	/// Detected via the `.toml` file extension.
	#[cfg(feature = "toml")]
	Toml,
}

impl ConfigFormat {
	/// From a given path this guesses the appropriate format based on the file extension
	pub fn guess_from_path(path: impl AsRef<Path>) -> Option<Self> {
		match path.as_ref().extension()?.to_str()? {
			#[cfg(feature = "json")]
			"json" => Some(Self::Json),
			#[cfg(feature = "json5")]
			"json5" => Some(Self::Json5),
			#[cfg(feature = "toml")]
			"toml" => Some(Self::Toml),
			_ => None,
		}
	}
}

impl Display for ConfigFormat {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match self {
			#[cfg(feature = "json")]
			Self::Json => f.write_str("json"),
			#[cfg(feature = "json5")]
			Self::Json5 => f.write_str("json5"),
			#[cfg(feature = "toml")]
			Self::Toml => f.write_str("toml"),
		}
	}
}

#[cfg(all(test, feature = "json"))]
#[test]
fn test_path_format_guessing_json() {
	assert_eq!(
		ConfigFormat::guess_from_path("test-data/test.json"),
		Some(ConfigFormat::Json)
	);
	assert_eq!(
		ConfigFormat::guess_from_path("test-data/.test.json"),
		Some(ConfigFormat::Json)
	);
	assert_eq!(
		ConfigFormat::guess_from_path("test-data/.test.foo.json"),
		Some(ConfigFormat::Json)
	);
}

#[cfg(all(test, feature = "json5"))]
#[test]
fn test_path_format_guessing_json5() {
	assert_eq!(
		ConfigFormat::guess_from_path("test-data/test.json5"),
		Some(ConfigFormat::Json5)
	);
}

#[cfg(all(test, feature = "toml"))]
#[test]
fn test_path_format_guessing_toml() {
	assert_eq!(
		ConfigFormat::guess_from_path("test-data/test.toml"),
		Some(ConfigFormat::Toml)
	);
}

#[cfg(test)]
#[test]
fn test_path_format_guessing_none() {
	assert_eq!(ConfigFormat::guess_from_path("test-data/test.txt"), None);
	assert_eq!(ConfigFormat::guess_from_path("test-data/test"), None);
	assert_eq!(ConfigFormat::guess_from_path("test-data/"), None);
}