use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AiMdType {
Managed,
User,
Project,
Local,
}
impl std::fmt::Display for AiMdType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AiMdType::Managed => write!(f, "managed"),
AiMdType::User => write!(f, "user"),
AiMdType::Project => write!(f, "project"),
AiMdType::Local => write!(f, "local"),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AiMdFrontmatter {
#[serde(default)]
pub paths: Option<Vec<String>>,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub r#type: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AiMdFile {
pub path: String,
pub md_type: AiMdType,
pub content: String,
#[serde(default)]
pub raw_content: Option<String>,
#[serde(default)]
pub globs: Option<Vec<String>>,
#[serde(default)]
pub parent: Option<String>,
#[serde(default)]
pub content_differs_from_disk: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AiMdContent {
pub path: String,
pub content: String,
pub type_description: String,
}
impl AiMdContent {
pub fn new(path: String, content: String, md_type: AiMdType) -> Self {
let type_description = match md_type {
AiMdType::Managed => "(global policy instructions)".to_string(),
AiMdType::User => "(user's private global instructions for all projects)".to_string(),
AiMdType::Project => "(project instructions, checked into the codebase)".to_string(),
AiMdType::Local => "(user's private project instructions, not checked in)".to_string(),
};
Self {
path,
content,
type_description,
}
}
}
#[derive(Debug)]
pub struct ParsedAiMd {
pub frontmatter: AiMdFrontmatter,
pub content: String,
}
pub const TEXT_FILE_EXTENSIONS: &[&str] = &[
".md",
".txt",
".text",
".json",
".yaml",
".yml",
".toml",
".xml",
".csv",
".html",
".htm",
".css",
".scss",
".sass",
".less",
".js",
".ts",
".tsx",
".jsx",
".mjs",
".cjs",
".mts",
".cts",
".py",
".pyi",
".pyw",
".rb",
".erb",
".rake",
".go",
".rs",
".java",
".kt",
".kts",
".scala",
".c",
".cpp",
".cc",
".cxx",
".h",
".hpp",
".hxx",
".cs",
".swift",
".sh",
".bash",
".zsh",
".fish",
".ps1",
".bat",
".cmd",
".env",
".ini",
".cfg",
".conf",
".config",
".properties",
".sql",
".graphql",
".gql",
".proto",
".vue",
".svelte",
".astro",
".ejs",
".hbs",
".pug",
".jade",
".php",
".pl",
".pm",
".lua",
".r",
".R",
".dart",
".ex",
".exs",
".erl",
".hrl",
".clj",
".cljs",
".cljc",
".edn",
".hs",
".lhs",
".elm",
".ml",
".mli",
".f",
".f90",
".f95",
".for",
".cmake",
".make",
".makefile",
".gradle",
".sbt",
".rst",
".adoc",
".asciidoc",
".org",
".tex",
".latex",
".lock",
".log",
".diff",
".patch",
];
pub fn is_allowed_extension(ext: &str) -> bool {
TEXT_FILE_EXTENSIONS.contains(&ext.to_lowercase().as_str())
}