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())
}