Process a `module-map` target: iterate mappings and create a symlink for each one, resolving the destination filename from: 1. mapping.filename_override (explicit user choice) 2. agent_convention_filename (per-agent convention) 3. source file basename (fallback)
(
&self,
agent_name: &str,
target: &TargetConfig,
options: &SyncOptions,
)
| 1121 | /// 2. agent_convention_filename (per-agent convention) |
| 1122 | /// 3. source file basename (fallback) |
| 1123 | fn process_module_map( |
| 1124 | &self, |
| 1125 | agent_name: &str, |
| 1126 | target: &TargetConfig, |
| 1127 | options: &SyncOptions, |
| 1128 | ) -> Result<SyncResult> { |
| 1129 | let mut result = SyncResult::default(); |
| 1130 | |
| 1131 | if target.mappings.is_empty() { |
| 1132 | if options.verbose { |
| 1133 | println!( |
| 1134 | " {} No mappings defined for module-map target", |
| 1135 | "!".yellow() |
| 1136 | ); |
| 1137 | } |
| 1138 | return Ok(result); |
| 1139 | } |
| 1140 | |
| 1141 | for mapping in &target.mappings { |
| 1142 | let source_path = self.source_dir.join(&mapping.source); |
| 1143 | self.revalidate_path(&source_path)?; // SECURITY: Validate source |
| 1144 | |
| 1145 | // Resolve destination filename |
| 1146 | let filename = crate::config::resolve_module_map_filename(mapping, agent_name); |
| 1147 | |
| 1148 | // SECURITY: Validate that the joined destination (dir + filename) is safe. |
| 1149 | let dest_str = format!("{}/{}", mapping.destination, filename); |
| 1150 | let dest = match self.ensure_safe_destination(&dest_str) { |
| 1151 | Ok(d) => d, |
| 1152 | Err(e) => { |
| 1153 | if options.verbose { |
| 1154 | println!( |
| 1155 | " {} Skipping mapping {}: {}", |
| 1156 | "!".yellow(), |
| 1157 | mapping.source, |
| 1158 | e |
| 1159 | ); |
| 1160 | } |
| 1161 | result.skipped += 1; |
| 1162 | continue; |
| 1163 | } |
| 1164 | }; |
| 1165 | |
| 1166 | let resolved = ResolvedSource { |
| 1167 | path: source_path.clone(), |
| 1168 | exists: source_path.exists(), |
| 1169 | }; |
| 1170 | |
| 1171 | let item_result = self.create_symlink(&resolved, &dest, options)?; |
| 1172 | result.created += item_result.created; |
| 1173 | result.updated += item_result.updated; |
| 1174 | result.skipped += item_result.skipped; |
| 1175 | } |
| 1176 | |
| 1177 | Ok(result) |
| 1178 | } |
| 1179 | |
| 1180 | /// Get the canonical path for a given path, using a cache to avoid |
no test coverage detected