use crate::share::errors::GitError; /// Maximum length of a lock path (git-lfs enforces a similar cap client-side). pub const MAX_LOCK_PATH_LEN: usize = 1024; /// Normalize a client-supplied oid into the canonical form used as the /// storage key / DB value: lowercase `sha256:<64 hex>`. /// /// Both the spec form (`sha256:`) and the bare 64-hex form accepted by /// some servers/clients are accepted. pub fn canonical_oid(raw: &str) -> Result { let raw = raw.trim(); let hex = match raw.strip_prefix("sha256:") { Some(hex) => hex, None if raw.len() == 64 => raw, None => return Err(oid_error(raw)), }; if hex.len() != 64 || !hex.bytes().all(|b| b.is_ascii_hexdigit()) { return Err(oid_error(raw)); } let mut out = String::with_capacity(70); out.push_str("sha256:"); for c in hex.bytes() { out.push(c.to_ascii_lowercase() as char); } Ok(out) } pub fn is_canonical_oid(oid: &str) -> bool { oid.len() == 71 && oid.starts_with("sha256:") && oid[7..].bytes().all(|b| b.is_ascii_hexdigit()) } /// Reject zero/negative sizes and anything above the configured cap. pub fn validate_size(size: i64, cap_bytes: u64) -> Result<(), GitError> { if size < 0 { return Err(GitError::InvalidArgument(format!("invalid size {size}"))); } let bytes = size as u64; if bytes > cap_bytes { return Err(GitError::InvalidArgument(format!( "object size {bytes} exceeds limit {cap_bytes}" ))); } Ok(()) } /// Locks paths must be non-empty, NUL-free and within a sane length. pub fn validate_lock_path(path: &str) -> Result<&str, GitError> { let path = path.trim(); if path.is_empty() { return Err(GitError::InvalidArgument( "lock path must not be empty".to_string(), )); } if path.len() > MAX_LOCK_PATH_LEN { return Err(GitError::InvalidArgument(format!( "lock path exceeds {MAX_LOCK_PATH_LEN} chars" ))); } if path.contains('\0') { return Err(GitError::InvalidArgument( "lock path contains NUL".to_string(), )); } Ok(path) } fn oid_error(raw: &str) -> GitError { GitError::InvalidArgument(format!("invalid oid `{raw}` (expected sha256:<64 hex>)")) }