pub(crate) fn unique_suffix() -> String {
static COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let count = COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
format!("{}-{}-{}", std::process::id(), nanos, count)
}
pub(crate) fn is_valid_container_id(id: &str) -> bool {
if id.len() < 2 || id.len() > 63 {
return false;
}
let bytes = id.as_bytes();
if !bytes[0].is_ascii_alphanumeric() {
return false;
}
bytes[1..]
.iter()
.all(|b| b.is_ascii_alphanumeric() || matches!(b, b'_' | b'.' | b'-'))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn container_id_accepts_valid_ids() {
assert!(
is_valid_container_id("c-1-2-3"),
"デフォルト形式の ID は適合するべき"
);
assert!(
is_valid_container_id("my_container.test-01"),
"許可文字のみの名前は適合するべき"
);
assert!(
is_valid_container_id("A1"),
"英数字 2 文字以上は適合するべき"
);
assert!(
is_valid_container_id("1a"),
"数字先頭の 2 文字 ID も適合するべき"
);
}
#[test]
fn container_id_rejects_invalid_ids() {
assert!(!is_valid_container_id(""), "空文字は拒否するべき");
assert!(!is_valid_container_id("A"), "1 文字の ID は拒否するべき");
assert!(!is_valid_container_id("1"), "1 文字の ID は拒否するべき");
assert!(
!is_valid_container_id("-abc"),
"先頭が英数字でない ID は拒否するべき"
);
assert!(
!is_valid_container_id("_abc"),
"先頭が英数字でない ID は拒否するべき"
);
assert!(
!is_valid_container_id(".abc"),
"先頭が英数字でない ID は拒否するべき"
);
assert!(
!is_valid_container_id("has space"),
"空白を含む ID は拒否するべき"
);
assert!(
!is_valid_container_id("glob*"),
"glob 文字を含む ID は拒否するべき"
);
assert!(
!is_valid_container_id("line\nbreak"),
"改行を含む ID は拒否するべき"
);
assert!(
!is_valid_container_id("q?"),
"クエスチョンマークを含む ID は拒否するべき"
);
assert!(
!is_valid_container_id("a/b"),
"スラッシュを含む ID は拒否するべき"
);
assert!(
!is_valid_container_id("あいう"),
"非 ASCII の ID は拒否するべき"
);
}
#[test]
fn container_id_boundary_length() {
let exactly_63 = "a".repeat(63);
assert!(
is_valid_container_id(&exactly_63),
"ちょうど 63 文字は許可されるべき"
);
let over_64 = "a".repeat(64);
assert!(!is_valid_container_id(&over_64), "64 文字は拒否されるべき");
}
#[test]
fn auto_generated_container_id_passes_validation() {
let id = format!("c-{}", unique_suffix());
assert!(
is_valid_container_id(&id),
"自動生成 ID は適合するべき: {id}"
);
assert!(id.len() <= 63, "自動生成 ID は 63 文字以下であるべき: {id}");
}
}