feat: add main features
Agents for developpement added + frontend add + backend added. Git viewer created + agent and template creator + layout and project creator
This commit is contained in:
59
crates/domain/src/validation.rs
Normal file
59
crates/domain/src/validation.rs
Normal file
@ -0,0 +1,59 @@
|
||||
//! Small pure validation helpers shared across value objects.
|
||||
|
||||
use crate::error::DomainError;
|
||||
|
||||
/// Returns the trimmed string if non-empty, otherwise [`DomainError::EmptyField`].
|
||||
pub(crate) fn non_empty(value: &str, field: &'static str) -> Result<(), DomainError> {
|
||||
if value.trim().is_empty() {
|
||||
Err(DomainError::EmptyField { field })
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates that `var` is a syntactically valid environment-variable identifier:
|
||||
/// non-empty, starts with a letter or `_`, and contains only ASCII alphanumeric
|
||||
/// characters or `_`.
|
||||
pub(crate) fn valid_env_var(var: &str) -> Result<(), DomainError> {
|
||||
let invalid = || DomainError::InvalidEnvVar {
|
||||
value: var.to_string(),
|
||||
};
|
||||
let mut chars = var.chars();
|
||||
match chars.next() {
|
||||
Some(c) if c.is_ascii_alphabetic() || c == '_' => {}
|
||||
_ => return Err(invalid()),
|
||||
}
|
||||
if chars.all(|c| c.is_ascii_alphanumeric() || c == '_') {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(invalid())
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates that a path is relative and does not escape its root via `..`.
|
||||
///
|
||||
/// Used for [`crate::profile::ContextInjection::ConventionFile`] targets and
|
||||
/// manifest `.md` paths, which must stay inside the project / `.ideai/` tree.
|
||||
pub(crate) fn relative_safe(path: &str) -> Result<(), DomainError> {
|
||||
let err = || DomainError::PathNotRelativeSafe {
|
||||
path: path.to_string(),
|
||||
};
|
||||
if path.is_empty() {
|
||||
return Err(err());
|
||||
}
|
||||
// Reject absolute POSIX paths and Windows drive / UNC paths.
|
||||
let bytes = path.as_bytes();
|
||||
if bytes[0] == b'/' || bytes[0] == b'\\' {
|
||||
return Err(err());
|
||||
}
|
||||
if path.len() >= 2 && bytes[1] == b':' && bytes[0].is_ascii_alphabetic() {
|
||||
return Err(err());
|
||||
}
|
||||
// Reject any `..` traversal component (handle both separators).
|
||||
for component in path.split(['/', '\\']) {
|
||||
if component == ".." {
|
||||
return Err(err());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user