use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Lang {
#[default]
Rust,
C,
Cpp,
Java,
Go,
Php,
Python,
Ruby,
TypeScript,
JavaScript,
}
impl Lang {
pub fn from_slug(s: &str) -> Option<Lang> {
match s {
"rust" => Some(Lang::Rust),
"c" => Some(Lang::C),
"cpp" => Some(Lang::Cpp),
"java" => Some(Lang::Java),
"go" => Some(Lang::Go),
"php" => Some(Lang::Php),
"python" => Some(Lang::Python),
"ruby" => Some(Lang::Ruby),
"typescript" | "ts" => Some(Lang::TypeScript),
"javascript" | "js" => Some(Lang::JavaScript),
_ => None,
}
}
pub fn from_extension(ext: &str) -> Option<Lang> {
match ext {
"rs" => Some(Lang::Rust),
"c" => Some(Lang::C),
"cpp" => Some(Lang::Cpp),
"java" => Some(Lang::Java),
"go" => Some(Lang::Go),
"php" => Some(Lang::Php),
"py" => Some(Lang::Python),
"ts" => Some(Lang::TypeScript),
"js" => Some(Lang::JavaScript),
"rb" => Some(Lang::Ruby),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Lang::Rust => "rust",
Lang::C => "c",
Lang::Cpp => "cpp",
Lang::Java => "java",
Lang::Go => "go",
Lang::Php => "php",
Lang::Python => "python",
Lang::Ruby => "ruby",
Lang::TypeScript => "typescript",
Lang::JavaScript => "javascript",
}
}
}
impl fmt::Display for Lang {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum FuncKind {
#[default]
Function,
Method,
Constructor,
Closure,
Getter,
Setter,
TopLevel,
}
impl FuncKind {
pub fn as_str(&self) -> &'static str {
match self {
FuncKind::Function => "fn",
FuncKind::Method => "method",
FuncKind::Constructor => "ctor",
FuncKind::Closure => "closure",
FuncKind::Getter => "getter",
FuncKind::Setter => "setter",
FuncKind::TopLevel => "toplevel",
}
}
pub fn from_slug(s: &str) -> FuncKind {
match s {
"fn" => FuncKind::Function,
"method" => FuncKind::Method,
"ctor" => FuncKind::Constructor,
"closure" => FuncKind::Closure,
"getter" => FuncKind::Getter,
"setter" => FuncKind::Setter,
"toplevel" => FuncKind::TopLevel,
_ => FuncKind::Function,
}
}
}
#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct FuncKey {
pub lang: Lang,
pub namespace: String,
#[serde(default)]
pub container: String,
pub name: String,
pub arity: Option<usize>,
#[serde(default)]
pub disambig: Option<u32>,
#[serde(default)]
pub kind: FuncKind,
}
impl FuncKey {
pub fn new_function(
lang: Lang,
namespace: impl Into<String>,
name: impl Into<String>,
arity: Option<usize>,
) -> Self {
FuncKey {
lang,
namespace: namespace.into(),
container: String::new(),
name: name.into(),
arity,
disambig: None,
kind: FuncKind::Function,
}
}
pub fn qualified_name(&self) -> String {
if self.container.is_empty() {
self.name.clone()
} else {
format!("{}::{}", self.container, self.name)
}
}
}
impl fmt::Display for FuncKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}::{}::", self.lang, self.namespace)?;
if !self.container.is_empty() {
write!(f, "{}::", self.container)?;
}
write!(f, "{}", self.name)?;
if let Some(a) = self.arity {
write!(f, "/{a}")?;
}
if let Some(d) = self.disambig {
write!(f, "#{d}")?;
}
if self.kind != FuncKind::Function {
write!(f, "[{}]", self.kind.as_str())?;
}
Ok(())
}
}
pub fn normalize_namespace(abs_path: &str, root: Option<&str>) -> String {
if let Some(r) = root {
let r = r.trim_end_matches('/');
if let Some(rest) = abs_path.strip_prefix(r) {
return rest.trim_start_matches('/').to_string();
}
}
abs_path.to_string()
}
#[cfg(test)]
mod tests;