fad06fd
use std::path::{Component, Path};
use crate::error::BabyError;
pub fn validate_local_no_escape(path: &Path) -> Result<(), BabyError> {
if path.as_os_str().is_empty() {
return Ok(());
}
if let Some(s) = path.to_str() {
if s.starts_with('-') {
return Err(BabyError::PathEscapesRepository);
}
} else {
return Err(BabyError::PathEscapesRepository);
}
if path.is_absolute() {
return Err(BabyError::PathEscapesRepository);
}
for comp in path.components() {
if matches!(comp, Component::ParentDir) {
return Err(BabyError::PathEscapesRepository);
}
}
Ok(())
}
pub fn validate_revision_non_empty(rev: &str) -> Result<(), BabyError> {
if rev.is_empty() {
return Err(BabyError::RevisionEmpty);
}
Ok(())
}
pub fn validate_ref_name(name: &str) -> Result<(), BabyError> {
if name.is_empty() || name == "@" {
return Err(BabyError::InvalidRefName(name.to_string()));
}
if name.starts_with('-') {
return Err(BabyError::InvalidRefName(name.to_string()));
}
if name.starts_with('/') || name.ends_with('/') {
return Err(BabyError::InvalidRefName(name.to_string()));
}
if name.starts_with('.') || name.ends_with('.') {
return Err(BabyError::InvalidRefName(name.to_string()));
}
if name.contains("..") || name.contains("//") || name.contains("@{") {
return Err(BabyError::InvalidRefName(name.to_string()));
}
if name.contains(".lock/") || name.ends_with(".lock") {
return Err(BabyError::InvalidRefName(name.to_string()));
}
if name.contains("/.") {
return Err(BabyError::InvalidRefName(name.to_string()));
}
for ch in name.chars() {
let b = ch as u32;
if b < 0o40 || b == 0o177 {
return Err(BabyError::InvalidRefName(name.to_string()));
}
match ch {
' ' | '~' | '^' | ':' | '?' | '*' | '[' | ']' => {
return Err(BabyError::InvalidRefName(name.to_string()));
}
_ => {}
}
}
Ok(())
}
pub fn validate_ref_pattern(pattern: &str) -> Result<(), BabyError> {
if pattern.is_empty() {
return Err(BabyError::InvalidRefName(pattern.to_string()));
}
if pattern.starts_with('-') {
return Err(BabyError::InvalidRefName(pattern.to_string()));
}
if pattern.contains("..") || pattern.contains("//") || pattern.contains("@{") {
return Err(BabyError::InvalidRefName(pattern.to_string()));
}
for ch in pattern.chars() {
let b = ch as u32;
if b < 0o40 || b == 0o177 {
return Err(BabyError::InvalidRefName(pattern.to_string()));
}
match ch {
' ' | '~' | '^' | ':' => {
return Err(BabyError::InvalidRefName(pattern.to_string()));
}
_ => {}
}
}
Ok(())
}
pub fn to_string_lossy_owned(path: &Path) -> String {
path.to_string_lossy().into_owned()
}
pub fn parse_git_mode(octal: &str) -> Result<u32, BabyError> {
u32::from_str_radix(octal, 8)
.map_err(|_| BabyError::Custom(format!("invalid git file mode `{}`", octal)))
}
pub fn parse_object_id(hex: &str) -> Result<gix::ObjectId, BabyError> {
gix::ObjectId::from_hex(hex.as_bytes())
.map_err(|source| BabyError::Custom(format!("invalid object id `{}`: {}", hex, source)))
}
pub fn parse_object_map(content: &str) -> Result<Vec<(gix::ObjectId, gix::ObjectId)>, BabyError> {
let mut pairs = Vec::new();
for raw_line in content.lines() {
let line = raw_line.trim();
if line.is_empty()
|| line.starts_with('#')
|| line.starts_with("old new")
{
continue;
}
let (old_hex, new_hex) = line
.split_once(' ')
.ok_or_else(|| BabyError::Custom(format!("object map line missing space: {}", line)))?;
let old = parse_object_id(old_hex)?;
let new = parse_object_id(new_hex)?;
pairs.push((old, new));
}
Ok(pairs)
}