use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SourceLanguage {
C,
Cxx,
}
impl SourceLanguage {
pub const fn as_key(self) -> &'static str {
match self {
Self::C => "c",
Self::Cxx => "cxx",
}
}
pub const fn human_label(self) -> &'static str {
match self {
Self::C => "C",
Self::Cxx => "C++",
}
}
}
impl std::fmt::Display for SourceLanguage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_key())
}
}
pub fn classify_source(path: &Path) -> Option<SourceLanguage> {
let ext = path.extension()?;
let ext = ext.to_str()?;
match ext {
"c" => Some(SourceLanguage::C),
"cc" | "cpp" | "cxx" | "c++" | "C" => Some(SourceLanguage::Cxx),
_ => None,
}
}
pub fn link_driver_language(languages: &[SourceLanguage]) -> SourceLanguage {
if languages.contains(&SourceLanguage::Cxx) {
SourceLanguage::Cxx
} else {
SourceLanguage::C
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn classifies_c_extension_as_c() {
assert_eq!(
classify_source(&PathBuf::from("foo.c")),
Some(SourceLanguage::C)
);
assert_eq!(
classify_source(&PathBuf::from("src/lib.c")),
Some(SourceLanguage::C)
);
}
#[test]
fn classifies_cpp_extensions_as_cxx() {
for ext in ["cc", "cpp", "cxx", "c++", "C"] {
let path = PathBuf::from(format!("src/file.{ext}"));
assert_eq!(
classify_source(&path),
Some(SourceLanguage::Cxx),
"extension `.{ext}` must classify as C++"
);
}
}
#[test]
fn classification_is_case_sensitive_for_lower_case_only() {
assert_eq!(
classify_source(&PathBuf::from("file.C")),
Some(SourceLanguage::Cxx)
);
assert!(classify_source(&PathBuf::from("file.CPP")).is_none());
}
#[test]
fn classification_returns_none_for_unknown_or_missing_extension() {
assert!(classify_source(&PathBuf::from("file")).is_none());
assert!(classify_source(&PathBuf::from("file.h")).is_none());
assert!(classify_source(&PathBuf::from("file.hpp")).is_none());
assert!(classify_source(&PathBuf::from("file.txt")).is_none());
}
#[test]
fn link_driver_is_cxx_when_any_source_is_cpp() {
assert_eq!(
link_driver_language(&[SourceLanguage::Cxx]),
SourceLanguage::Cxx
);
assert_eq!(
link_driver_language(&[SourceLanguage::C, SourceLanguage::Cxx]),
SourceLanguage::Cxx
);
assert_eq!(
link_driver_language(&[SourceLanguage::Cxx, SourceLanguage::C]),
SourceLanguage::Cxx
);
}
#[test]
fn link_driver_is_c_when_every_source_is_c() {
assert_eq!(
link_driver_language(&[SourceLanguage::C]),
SourceLanguage::C
);
assert_eq!(
link_driver_language(&[SourceLanguage::C, SourceLanguage::C]),
SourceLanguage::C
);
}
#[test]
fn link_driver_falls_back_to_c_for_empty_input() {
assert_eq!(link_driver_language(&[]), SourceLanguage::C);
}
#[test]
fn keys_are_stable_across_renames() {
assert_eq!(SourceLanguage::C.as_key(), "c");
assert_eq!(SourceLanguage::Cxx.as_key(), "cxx");
assert_eq!(SourceLanguage::C.to_string(), "c");
assert_eq!(SourceLanguage::Cxx.to_string(), "cxx");
}
}