| use super::RewriteProfile; |
|
|
| pub(super) fn rewrite_terms(content: &str, profile: RewriteProfile) -> String { |
| let mut rewritten = |
| replace_case_insensitive_with_boundaries(content, profile.doc_file_name, "AGENTS.md"); |
| for from in profile.term_variants { |
| rewritten = replace_case_insensitive_with_boundaries(&rewritten, from, "Codex"); |
| } |
| for from in profile.case_sensitive_term_variants { |
| rewritten = replace_with_boundaries(&rewritten, from, "Codex"); |
| } |
| rewritten |
| } |
|
|
| fn replace_with_boundaries(input: &str, needle: &str, replacement: &str) -> String { |
| if needle.is_empty() { |
| return input.to_string(); |
| } |
|
|
| replace_with_boundaries_impl(input, needle, replacement, input) |
| } |
|
|
| fn replace_case_insensitive_with_boundaries( |
| input: &str, |
| needle: &str, |
| replacement: &str, |
| ) -> String { |
| let needle_lower = needle.to_ascii_lowercase(); |
| if needle_lower.is_empty() { |
| return input.to_string(); |
| } |
| let haystack_lower = input.to_ascii_lowercase(); |
| replace_with_boundaries_impl(input, &needle_lower, replacement, &haystack_lower) |
| } |
|
|
| fn replace_with_boundaries_impl( |
| input: &str, |
| needle: &str, |
| replacement: &str, |
| searchable_input: &str, |
| ) -> String { |
| let bytes = input.as_bytes(); |
| let mut output = String::with_capacity(input.len()); |
| let mut last_emitted = 0usize; |
| let mut search_start = 0usize; |
|
|
| while let Some(relative_pos) = searchable_input[search_start..].find(needle) { |
| let start = search_start + relative_pos; |
| let end = start + needle.len(); |
| let boundary_before = start == 0 || !is_word_byte(bytes[start - 1]); |
| let boundary_after = end == bytes.len() || !is_word_byte(bytes[end]); |
|
|
| if boundary_before && boundary_after { |
| output.push_str(&input[last_emitted..start]); |
| output.push_str(replacement); |
| last_emitted = end; |
| } |
| search_start = start + 1; |
| } |
|
|
| if last_emitted == 0 { |
| return input.to_string(); |
| } |
| output.push_str(&input[last_emitted..]); |
| output |
| } |
|
|
| pub(super) fn yaml_string(value: &str) -> String { |
| format!("\"{}\"", value.replace('\\', "\\\\").replace('"', "\\\"")) |
| } |
|
|
| pub(super) fn slugify_name(value: &str) -> String { |
| let mut slug = String::new(); |
| let mut last_was_dash = false; |
| for ch in value.chars() { |
| if ch.is_ascii_alphanumeric() { |
| slug.push(ch.to_ascii_lowercase()); |
| last_was_dash = false; |
| } else if !last_was_dash { |
| slug.push('-'); |
| last_was_dash = true; |
| } |
| } |
|
|
| let slug = slug.trim_matches('-').to_string(); |
| if slug.is_empty() { |
| "migrated".to_string() |
| } else { |
| slug |
| } |
| } |
|
|
| fn is_word_byte(byte: u8) -> bool { |
| byte.is_ascii_alphanumeric() || byte == b'_' |
| } |
|
|