fastskill_core/validation/
field_validation.rs1use crate::core::skill_manager::SkillDefinition;
4use crate::validation::result::{ErrorSeverity, ValidationResult};
5
6pub(crate) fn is_valid_skill_name(s: &str) -> bool {
7 s.chars()
8 .all(|c| c.is_alphanumeric() || c == '-' || c == '_')
9}
10
11pub(crate) fn is_valid_semver(s: &str) -> bool {
12 semver::Version::parse(s).is_ok()
13}
14
15pub(crate) fn validate_required_fields(
16 skill: &SkillDefinition,
17 mut result: ValidationResult,
18 required_fields: &[String],
19 max_description_length: usize,
20) -> ValidationResult {
21 for field in required_fields {
22 match field.as_str() {
23 "name" => {
24 if skill.name.trim().is_empty() {
25 result = result.with_error(
26 "name",
27 "Skill name cannot be empty",
28 ErrorSeverity::Critical,
29 );
30 }
31 }
32 "description" => {
33 if skill.description.trim().is_empty() {
34 result = result.with_error(
35 "description",
36 "Skill description cannot be empty",
37 ErrorSeverity::Critical,
38 );
39 } else if skill.description.len() > max_description_length {
40 result = result.with_warning(
41 "description",
42 &format!(
43 "Description is very long ({} chars, max: {})",
44 skill.description.len(),
45 max_description_length
46 ),
47 );
48 }
49 }
50 "version" => {
51 if skill.version.trim().is_empty() {
52 result = result.with_error(
53 "version",
54 "Skill version cannot be empty",
55 ErrorSeverity::Critical,
56 );
57 }
58 }
59 _ => {}
60 }
61 }
62 result
63}
64
65pub(crate) fn validate_field_formats(
66 skill: &SkillDefinition,
67 mut result: ValidationResult,
68) -> ValidationResult {
69 if !is_valid_skill_name(&skill.name) {
70 result = result.with_warning(
71 "name",
72 "Skill name should only contain alphanumeric characters, hyphens, and underscores",
73 );
74 }
75 if !is_valid_semver(&skill.version) {
76 result = result.with_warning(
77 "version",
78 "Version should follow semantic versioning (e.g., 1.0.0)",
79 );
80 }
81 result
82}