standarbuild-detect 0.3.0

Detect project kind (Rust, Node, Bun, Deno, Python, Lua, C/C++) AND workspace kind (Cargo, Npm/Pnpm/Yarn/Bun, Deno, Go, Lerna, Nx, Turborepo, Mira) in polyglot monorepos
Documentation
//! Opaque string-based identifier for a project kind. Built-in kinds are
//! exposed as `const` items; custom kinds (`KindId::custom("wgsl")`) let
//! downstream crates extend the detection space without touching the lib.

use std::borrow::Cow;
use std::fmt;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct KindId(Cow<'static, str>);

impl KindId {
	pub const RUST: Self = Self(Cow::Borrowed("rust"));
	pub const NODE: Self = Self(Cow::Borrowed("node"));
	pub const BUN: Self = Self(Cow::Borrowed("bun"));
	pub const DENO: Self = Self(Cow::Borrowed("deno"));
	pub const PYTHON: Self = Self(Cow::Borrowed("python"));
	pub const LUA: Self = Self(Cow::Borrowed("lua"));
	pub const C: Self = Self(Cow::Borrowed("c"));
	pub const CPP: Self = Self(Cow::Borrowed("cpp"));
	pub const UNKNOWN: Self = Self(Cow::Borrowed("unknown"));

	pub const fn from_static(s: &'static str) -> Self {
		Self(Cow::Borrowed(s))
	}

	pub fn custom(name: impl Into<String>) -> Self {
		Self(Cow::Owned(name.into()))
	}

	pub fn as_str(&self) -> &str {
		&self.0
	}

	/// Returns the canonical built-in matching `s`, accepting the same aliases
	/// as the original enum (`c++` → `cpp`). Returns `None` for any other
	/// string — callers wanting to accept unknown kinds should use
	/// [`KindId::custom`] explicitly.
	pub fn parse_builtin(s: &str) -> Option<Self> {
		match s {
			"rust" => Some(Self::RUST),
			"node" => Some(Self::NODE),
			"bun" => Some(Self::BUN),
			"deno" => Some(Self::DENO),
			"python" => Some(Self::PYTHON),
			"lua" => Some(Self::LUA),
			"c" => Some(Self::C),
			"cpp" | "c++" => Some(Self::CPP),
			"unknown" => Some(Self::UNKNOWN),
			_ => None,
		}
	}
}

impl fmt::Display for KindId {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		f.write_str(self.as_str())
	}
}

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

	#[test]
	fn const_builtins_have_expected_strings() {
		assert_eq!(KindId::RUST.as_str(), "rust");
		assert_eq!(KindId::CPP.as_str(), "cpp");
		assert_eq!(KindId::UNKNOWN.as_str(), "unknown");
	}

	#[test]
	fn custom_kind_roundtrip() {
		let k = KindId::custom("wgsl");
		assert_eq!(k.as_str(), "wgsl");
		assert_ne!(k, KindId::RUST);
	}

	#[test]
	fn parse_builtin_accepts_cpp_alias() {
		assert_eq!(KindId::parse_builtin("c++"), Some(KindId::CPP));
		assert_eq!(KindId::parse_builtin("cpp"), Some(KindId::CPP));
	}

	#[test]
	fn parse_builtin_returns_none_for_custom() {
		assert_eq!(KindId::parse_builtin("wgsl"), None);
	}

	#[test]
	fn equality_does_not_depend_on_cow_variant() {
		assert_eq!(KindId::RUST, KindId::custom("rust"));
	}
}