gitdataai/libs/models/projects/project_skill.rs
2026-04-14 19:02:01 +08:00

116 lines
3.6 KiB
Rust

//! Skill registered to a project.
//!
//! Skills can be sourced manually (by project admin) or auto-discovered from
//! repositories within the project (via `.claude/skills/` directory scanning).
use crate::{DateTimeUtc, ProjectId, RepoId, UserId};
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
/// Skill source: `"manual"` or `"repo"`.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum SkillSource {
Manual,
Repo,
}
impl std::fmt::Display for SkillSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SkillSource::Manual => write!(f, "manual"),
SkillSource::Repo => write!(f, "repo"),
}
}
}
impl std::str::FromStr for SkillSource {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"manual" => Ok(SkillSource::Manual),
"repo" => Ok(SkillSource::Repo),
_ => Err("unknown skill source"),
}
}
}
/// Parsed frontmatter from a skill's SKILL.md file.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct SkillMetadata {
/// Human-readable name (falls back to slug/folder name).
#[serde(default)]
pub name: Option<String>,
/// Short description of what the skill does.
#[serde(default)]
pub description: Option<String>,
/// SPDX license identifier.
#[serde(default)]
pub license: Option<String>,
/// Compatibility notes (e.g. "Requires openspec CLI").
#[serde(default)]
pub compatibility: Option<String>,
/// Free-form metadata from the frontmatter.
#[serde(default)]
pub metadata: serde_json::Value,
}
impl From<serde_json::Value> for SkillMetadata {
fn from(v: serde_json::Value) -> Self {
serde_json::from_value(v).unwrap_or_default()
}
}
/// Skill record persisted in the `project_skill` table.
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "project_skill")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i64,
/// Project this skill belongs to.
pub project_uuid: ProjectId,
/// URL-safe identifier, unique within a project.
pub slug: String,
/// Display name (extracted from frontmatter or folder name).
pub name: String,
/// Optional short description.
pub description: Option<String>,
/// `"manual"` or `"repo"`.
pub source: String,
/// If source=repo, the repo this skill was discovered from.
#[sea_orm(nullable)]
pub repo_id: Option<RepoId>,
/// If source=repo, the commit SHA where the skill was found.
#[sea_orm(nullable)]
pub commit_sha: Option<String>,
/// If source=repo, the blob SHA of the SKILL.md file.
#[sea_orm(nullable)]
pub blob_hash: Option<String>,
/// Raw markdown content (SKILL.md body after frontmatter).
pub content: String,
/// Full frontmatter as JSON.
#[sea_orm(column_type = "JsonBinary")]
pub metadata: serde_json::Value,
/// Whether this skill is currently active.
pub enabled: bool,
/// Who added this skill (null for repo-sourced skills).
#[sea_orm(nullable)]
pub created_by: Option<UserId>,
pub created_at: DateTimeUtc,
pub updated_at: DateTimeUtc,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
impl Model {
pub fn source_enum(&self) -> Result<SkillSource, &'static str> {
self.source.parse()
}
pub fn metadata_parsed(&self) -> SkillMetadata {
SkillMetadata::from(self.metadata.clone())
}
}