use crate::{languages::Error as LanguageError, Error};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Language {
pub code: Code,
pub extension: String,
pub name: String,
}
impl Default for Language {
fn default() -> Self {
Language {
code: Code::rs,
extension: "rs".to_string(),
name: "Rust".to_string(),
}
}
}
impl std::fmt::Display for Language {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
impl From<Code> for Language {
fn from(code: Code) -> Self {
let name = code.get_name();
let extension = code.get_extension();
Language {
code,
extension: extension.to_string(),
name: name.to_string(),
}
}
}
impl TryFrom<&str> for Language {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match Code::try_from(value) {
Ok(code) => {
let name = code.get_name();
let extension = code.get_extension();
Ok(Language {
code,
extension: extension.to_string(),
name: name.to_string(),
})
}
Err(_) => Err(LanguageError::InvalidLanguageName(value.to_string()).into()),
}
}
}
impl TryFrom<&str> for Code {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
if let Some(code) = get_language_code(value) {
Ok(code)
} else if let Some(code) = get_language_code_from_name(value) {
Ok(code)
} else {
Err(LanguageError::InvalidLanguageCode(value.to_string()).into())
}
}
}
impl TryFrom<String> for Code {
type Error = Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
Code::try_from(value.as_str())
}
}
macro_rules! generate_programming_enum {
($(($code:ident, $name:literal, $ext:literal)),* $(,)?) => {
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum Code {
$(
#[allow(non_camel_case_types)]
$code,
)*
}
impl Code {
pub fn get_name(&self) -> &'static str {
match &self {
$(
Code::$code => $name,
)*
}
}
pub fn get_extension(&self) -> &'static str {
match &self {
$(
Code::$code => $ext,
)*
}
}
}
impl Default for Code {
fn default() -> Self {
Code::rs
}
}
impl std::fmt::Display for Code {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(
Code::$code => write!(f, "{}", stringify!($code)),
)*
}
}
}
impl std::cmp::PartialOrd for Code {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl std::cmp::Ord for Code {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.to_string().cmp(&other.to_string())
}
}
impl IntoIterator for Code {
type Item = Code;
type IntoIter = std::iter::Copied<std::slice::Iter<'static, Code>>;
fn into_iter(self) -> Self::IntoIter {
static LANGUAGES: [Code; 20] = [
$(
Code::$code,
)*
];
LANGUAGES.iter().copied()
}
}
pub fn get_language_code_from_name(name: &str) -> Option<Code> {
match name {
$(
$name => Some(Code::$code),
)*
_ => None,
}
}
pub fn get_language_code(code: &str) -> Option<Code> {
let code = code.to_lowercase();
if code.len() != 2 {
return None;
}
match code.as_str() {
$(
stringify!($code) => Some(Code::$code),
)*
_ => None,
}
}
};
}
generate_programming_enum! {
(py, "Python", "py"),
(js, "JavaScript", "js"),
(ja, "Java", "java"),
(cs, ".Net", "cs"),
(cp, "C++", "cpp"),
(go, "Go", "go"),
(rb, "Ruby", "rb"),
(ph, "PHP", "php"),
(ts, "TypeScript", "ts"),
(sw, "Swift", "swift"),
(kt, "Kotlin", "kt"),
(rs, "Rust", "rs"),
(sc, "Scala", "scala"),
(lu, "Lua", "lua"),
(pe, "Perl", "pl"),
(hs, "Haskell", "hs"),
(er, "Erlang", "erl"),
(cl, "Clojure", "clj"),
(el, "Elixir", "ex"),
(fs, "F#", "fs"),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_language_code_from_name() {
assert_eq!(get_language_code_from_name("Python"), Some(Code::py));
assert_eq!(get_language_code_from_name("JavaScript"), Some(Code::js));
assert_eq!(get_language_code_from_name("Java"), Some(Code::ja));
assert_eq!(get_language_code_from_name(".Net"), Some(Code::cs));
assert_eq!(get_language_code_from_name("C++"), Some(Code::cp));
assert_eq!(get_language_code_from_name("Go"), Some(Code::go));
assert_eq!(get_language_code_from_name("Ruby"), Some(Code::rb));
assert_eq!(get_language_code_from_name("PHP"), Some(Code::ph));
assert_eq!(get_language_code_from_name("TypeScript"), Some(Code::ts));
assert_eq!(get_language_code_from_name("Swift"), Some(Code::sw));
assert_eq!(get_language_code_from_name("Kotlin"), Some(Code::kt));
assert_eq!(get_language_code_from_name("Rust"), Some(Code::rs));
assert_eq!(get_language_code_from_name("Scala"), Some(Code::sc));
assert_eq!(get_language_code_from_name("Lua"), Some(Code::lu));
assert_eq!(get_language_code_from_name("Perl"), Some(Code::pe));
assert_eq!(get_language_code_from_name("Haskell"), Some(Code::hs));
assert_eq!(get_language_code_from_name("Erlang"), Some(Code::er));
assert_eq!(get_language_code_from_name("Clojure"), Some(Code::cl));
assert_eq!(get_language_code_from_name("Elixir"), Some(Code::el));
assert_eq!(get_language_code_from_name("F#"), Some(Code::fs));
assert!(get_language_code_from_name("Brainfuck").is_none());
}
#[test]
fn test_get_language_code() {
assert_eq!(get_language_code("py"), Some(Code::py));
assert_eq!(get_language_code("js"), Some(Code::js));
assert_eq!(get_language_code("ja"), Some(Code::ja));
assert_eq!(get_language_code("cs"), Some(Code::cs));
assert_eq!(get_language_code("cp"), Some(Code::cp));
assert_eq!(get_language_code("go"), Some(Code::go));
assert_eq!(get_language_code("rb"), Some(Code::rb));
assert_eq!(get_language_code("ph"), Some(Code::ph));
assert_eq!(get_language_code("ts"), Some(Code::ts));
assert_eq!(get_language_code("sw"), Some(Code::sw));
assert_eq!(get_language_code("kt"), Some(Code::kt));
assert_eq!(get_language_code("rs"), Some(Code::rs));
assert_eq!(get_language_code("sc"), Some(Code::sc));
assert_eq!(get_language_code("lu"), Some(Code::lu));
assert_eq!(get_language_code("pe"), Some(Code::pe));
assert_eq!(get_language_code("hs"), Some(Code::hs));
assert_eq!(get_language_code("er"), Some(Code::er));
assert_eq!(get_language_code("cl"), Some(Code::cl));
assert_eq!(get_language_code("el"), Some(Code::el));
assert_eq!(get_language_code("fs"), Some(Code::fs));
assert!(get_language_code("bf").is_none());
}
#[test]
fn test_get_name() {
assert_eq!(Code::py.get_name(), "Python");
assert_eq!(Code::js.get_name(), "JavaScript");
assert_eq!(Code::ja.get_name(), "Java");
assert_eq!(Code::cs.get_name(), ".Net");
assert_eq!(Code::cp.get_name(), "C++");
assert_eq!(Code::go.get_name(), "Go");
assert_eq!(Code::rb.get_name(), "Ruby");
assert_eq!(Code::ph.get_name(), "PHP");
assert_eq!(Code::ts.get_name(), "TypeScript");
assert_eq!(Code::sw.get_name(), "Swift");
assert_eq!(Code::kt.get_name(), "Kotlin");
assert_eq!(Code::rs.get_name(), "Rust");
assert_eq!(Code::sc.get_name(), "Scala");
assert_eq!(Code::lu.get_name(), "Lua");
assert_eq!(Code::pe.get_name(), "Perl");
assert_eq!(Code::hs.get_name(), "Haskell");
assert_eq!(Code::er.get_name(), "Erlang");
assert_eq!(Code::cl.get_name(), "Clojure");
assert_eq!(Code::el.get_name(), "Elixir");
assert_eq!(Code::fs.get_name(), "F#");
assert!(get_language_code("bf").is_none());
}
#[test]
fn test_code_from_name() {
assert_eq!(Code::try_from("Python").unwrap(), Code::py);
assert_eq!(Code::try_from("JavaScript").unwrap(), Code::js);
assert_eq!(Code::try_from("Java").unwrap(), Code::ja);
assert_eq!(Code::try_from(".Net").unwrap(), Code::cs);
assert_eq!(Code::try_from("C++").unwrap(), Code::cp);
assert_eq!(Code::try_from("Go").unwrap(), Code::go);
assert_eq!(Code::try_from("Ruby").unwrap(), Code::rb);
assert_eq!(Code::try_from("PHP").unwrap(), Code::ph);
assert_eq!(Code::try_from("TypeScript").unwrap(), Code::ts);
assert_eq!(Code::try_from("Swift").unwrap(), Code::sw);
assert_eq!(Code::try_from("Kotlin").unwrap(), Code::kt);
assert_eq!(Code::try_from("Rust").unwrap(), Code::rs);
assert_eq!(Code::try_from("Scala").unwrap(), Code::sc);
assert_eq!(Code::try_from("Lua").unwrap(), Code::lu);
assert_eq!(Code::try_from("Perl").unwrap(), Code::pe);
assert_eq!(Code::try_from("Haskell").unwrap(), Code::hs);
assert_eq!(Code::try_from("Erlang").unwrap(), Code::er);
assert_eq!(Code::try_from("Clojure").unwrap(), Code::cl);
assert_eq!(Code::try_from("Elixir").unwrap(), Code::el);
assert_eq!(Code::try_from("F#").unwrap(), Code::fs);
assert!(Language::try_from("Brainfuck").is_err());
}
#[test]
fn test_language_from_code() {
assert_eq!(
Language::from(Code::py),
Language {
code: Code::py,
extension: "py".to_string(),
name: "Python".to_string(),
}
);
assert_eq!(
Language::from(Code::js),
Language {
code: Code::js,
extension: "js".to_string(),
name: "JavaScript".to_string(),
}
);
assert_eq!(
Language::from(Code::ja),
Language {
code: Code::ja,
extension: "java".to_string(),
name: "Java".to_string(),
}
);
}
#[test]
fn test_language_from_name() {
assert_eq!(
Language::try_from("Python").unwrap(),
Language {
code: Code::py,
extension: "py".to_string(),
name: "Python".to_string(),
}
);
assert_eq!(
Language::try_from("JavaScript").unwrap(),
Language {
code: Code::js,
extension: "js".to_string(),
name: "JavaScript".to_string(),
}
);
assert_eq!(
Language::try_from("Java").unwrap(),
Language {
code: Code::ja,
extension: "java".to_string(),
name: "Java".to_string(),
}
);
}
}