@@ -148,6 +148,12 @@ module ArcEntityPathRules =
148148 | CanonicalDataMapFileTarget of zone : AddZone * identifier : string * normalizedRelativePath : string
149149 | GenericTarget of normalizedRelativePath : string
150150
151+ type private StructuralArcPath =
152+ | AddZoneRoot
153+ | EntityFolder
154+ | ProtectedEntityChildFolder
155+ | OtherPath
156+
151157 let private protectedDeleteTargetNames = [ " .gitattributes" ; " .gitkeep" ; " readme.md" ]
152158 let private protectedRenameRootFolderNames = [ " notes" ]
153159 let private disallowedGenericPathSegments = [ " .git" ]
@@ -172,6 +178,19 @@ module ArcEntityPathRules =
172178 | AddZone.Workflows -> ARCtrl.ArcPathHelper.WorkflowFileName
173179 | AddZone.Runs -> ARCtrl.ArcPathHelper.RunFileName
174180
181+ let private nativeEntityChildFolderNames =
182+ function
183+ | AddZone.Studies -> [
184+ ARCtrl.ArcPathHelper.StudiesProtocolsFolderName
185+ ARCtrl.ArcPathHelper.StudiesResourcesFolderName
186+ ]
187+ | AddZone.Assays -> [
188+ ARCtrl.ArcPathHelper.AssayDatasetFolderName
189+ ARCtrl.ArcPathHelper.AssayProtocolsFolderName
190+ ]
191+ | AddZone.Workflows
192+ | AddZone.Runs -> []
193+
175194 let private tryParseZone ( segment : string ) =
176195 if PathHelpers.pathsEqual segment ARCtrl.ArcPathHelper.StudiesFolderName then
177196 Some AddZone.Studies
@@ -184,6 +203,31 @@ module ArcEntityPathRules =
184203 else
185204 None
186205
206+ let private isAddZoneSegment segment = ( tryParseZone segment) .IsSome
207+
208+ let private classifyStructuralArcPath =
209+ function
210+ | [| zoneSegment |] when isAddZoneSegment zoneSegment -> StructuralArcPath.AddZoneRoot
211+ | [| zoneSegment; _ |] when isAddZoneSegment zoneSegment -> StructuralArcPath.EntityFolder
212+ | [| zoneSegment; _; childFolderName |] ->
213+ match tryParseZone zoneSegment with
214+ | Some zone when PathHelpers.pathMatchesAny ( nativeEntityChildFolderNames zone) childFolderName ->
215+ StructuralArcPath.ProtectedEntityChildFolder
216+ | _ -> StructuralArcPath.OtherPath
217+ | _ -> StructuralArcPath.OtherPath
218+
219+ let private blocksGenericFileSystemTarget =
220+ function
221+ | StructuralArcPath.AddZoneRoot
222+ | StructuralArcPath.EntityFolder
223+ | StructuralArcPath.ProtectedEntityChildFolder -> true
224+ | StructuralArcPath.OtherPath -> false
225+
226+ let private isProtectedTarget normalizedRelativePath =
227+ function
228+ | StructuralArcPath.ProtectedEntityChildFolder -> true
229+ | _ -> PathHelpers.isProtectedDeleteTarget protectedDeleteTargetNames normalizedRelativePath
230+
187231 let private tryParseCanonicalArcFileTargetFromSegments ( segments : string []) =
188232 if segments.Length = 0 then
189233 None
@@ -214,10 +258,7 @@ module ArcEntityPathRules =
214258
215259 let private containsDisallowedGenericPathSegment ( segments : string []) =
216260 segments
217- |> Array.exists ( fun segment ->
218- disallowedGenericPathSegments
219- |> List.exists ( fun blocked -> PathHelpers.pathsEqual segment blocked)
220- )
261+ |> Array.exists ( PathHelpers.pathMatchesAny disallowedGenericPathSegments)
221262
222263 /// Parses canonical ARC file targets from the tail of a path and supports absolute paths.
223264 let tryParseCanonicalArcFileTarget ( path : string ) =
@@ -228,14 +269,14 @@ module ArcEntityPathRules =
228269
229270 let classifyDeleteTarget ( relativePath : string ) =
230271 let normalizedRelativePath = normalizeRelativePath relativePath
272+ let segments = normalizedRelativePath |> splitPathSegments
273+ let structuralPath = classifyStructuralArcPath segments
231274
232275 if String.IsNullOrWhiteSpace normalizedRelativePath then
233276 DeletePathClassification.DisallowedTarget normalizedRelativePath
234- elif PathHelpers.isProtectedDeleteTarget protectedDeleteTargetNames normalizedRelativePath then
277+ elif isProtectedTarget normalizedRelativePath structuralPath then
235278 DeletePathClassification.ProtectedTarget normalizedRelativePath
236279 else
237- let segments = normalizedRelativePath |> splitPathSegments
238-
239280 match segments with
240281 | [| singleSegment |] ->
241282 match tryParseZone singleSegment with
@@ -283,21 +324,20 @@ module ArcEntityPathRules =
283324
284325 let isGenericFileSystemTargetAllowed ( relativePath : string ) =
285326 let normalizedRelativePath = normalizeRelativePath relativePath
327+ let segments = normalizedRelativePath |> splitPathSegments
328+ let structuralPath = classifyStructuralArcPath segments
286329
287330 if
288331 String.IsNullOrWhiteSpace normalizedRelativePath
289332 || PathHelpers.containsPathTraversalSegments normalizedRelativePath
290- || PathHelpers.isProtectedDeleteTarget protectedDeleteTargetNames normalizedRelativePath
333+ || isProtectedTarget normalizedRelativePath structuralPath
291334 then
292335 false
293336 else
294- let segments = normalizedRelativePath |> splitPathSegments
295-
296337 segments.Length >= 1
297338 && ( segments |> containsDisallowedGenericPathSegment |> not )
298339 && ( PathHelpers.getFileName normalizedRelativePath |> isCanonicalArcFileName |> not )
299- && ( segments.Length <> 1 || ( tryParseZone segments.[ 0 ]) .IsNone)
300- && ( segments.Length <> 2 || ( tryParseZone segments.[ 0 ]) .IsNone)
340+ && ( blocksGenericFileSystemTarget structuralPath |> not )
301341
302342 let tryNormalizeGenericFileSystemTarget ( errorMessage : string ) ( relativePath : string ) : Result < string , string > =
303343 let normalizedRelativePath = normalizeRelativePath relativePath
@@ -352,10 +392,11 @@ module ArcEntityPathRules =
352392 RenamePathClassification.RootTarget
353393 else
354394 let segments = normalizedRelativePath |> splitPathSegments
395+ let structuralPath = classifyStructuralArcPath segments
355396
356397 if PathHelpers.containsPathTraversalSegments normalizedRelativePath then
357398 RenamePathClassification.DisallowedTarget normalizedRelativePath
358- elif PathHelpers.isProtectedDeleteTarget protectedDeleteTargetNames normalizedRelativePath then
399+ elif isProtectedTarget normalizedRelativePath structuralPath then
359400 RenamePathClassification.ProtectedTarget normalizedRelativePath
360401 else
361402 match segments with
@@ -403,13 +444,9 @@ module ArcEntityPathRules =
403444 false
404445 else
405446 let segments = normalizedRelativePath |> splitPathSegments
447+ let structuralPath = classifyStructuralArcPath segments
406448
407- let isArcEntityFolder = segments.Length = 2 && ( tryParseZone segments.[ 0 ]) .IsSome
408-
409- let isSafeGenericDirectoryCandidate =
410- isGenericFileSystemTargetAllowed normalizedRelativePath
411-
412- ( isArcEntityFolder || isSafeGenericDirectoryCandidate)
449+ structuralPath <> StructuralArcPath.AddZoneRoot
413450 && ( segments |> containsDisallowedGenericPathSegment |> not )
414451 && ( PathHelpers.getFileName normalizedRelativePath |> isCanonicalArcFileName |> not )
415452
0 commit comments