(raw: &str)
| 154 | } |
| 155 | |
| 156 | pub(crate) fn validate_dlc_name(raw: &str) -> Result<String, String> { |
| 157 | let dlc = raw.trim(); |
| 158 | if dlc.is_empty() { |
| 159 | return Err("dlc name cannot be empty".to_string()); |
| 160 | } |
| 161 | if dlc.eq_ignore_ascii_case("self") { |
| 162 | return Err("dlc name `self` is reserved".to_string()); |
| 163 | } |
| 164 | if dlc.contains('/') || dlc.contains('\\') { |
| 165 | return Err("dlc name must not include path separators".to_string()); |
| 166 | } |
| 167 | if Path::new(dlc) |
| 168 | .components() |
| 169 | .any(|c| matches!(c, std::path::Component::ParentDir)) |
| 170 | { |
| 171 | return Err("dlc name must not include `..` segments".to_string()); |
| 172 | } |
| 173 | Ok(dlc.to_string()) |
| 174 | } |
| 175 | |
| 176 | fn resolve_content_root(project_dir: &Path, args: &[String]) -> Result<PathBuf, String> { |
| 177 | if let Some(raw_dlc) = parse_flag_value(args, "--dlc") { |
no test coverage detected