@@ -49,6 +49,17 @@ public static IFileInfo File(this IDirectoryInfo info, string name)
4949 return info . FileSystem . FileInfo . New ( info . FileSystem . Path . Combine ( info . FullName , name ) ) ;
5050 }
5151
52+ /// <summary>
53+ /// Get the full path for the specified file <paramref name="name"/> in the <paramref name="info"/> folder
54+ /// </summary>
55+ /// <param name="info"></param>
56+ /// <param name="name">File name (ex. "test.txt")</param>
57+ /// <returns>A <see cref="string"/> with the full path of the file</returns>
58+ public static string GetFilePath ( this IDirectoryInfo info , string name )
59+ {
60+ return info . FileSystem . Path . Combine ( info . FullName , name ) ;
61+ }
62+
5263 /// <summary>
5364 /// Get an <see cref="IFileInfo"/> for the specified sub-directories file <paramref name="names"/>
5465 /// </summary>
@@ -79,7 +90,131 @@ public static IFileInfo File(this IDirectoryInfo info, IEnumerable<string> names
7990 public static void ThrowIfNotFound ( this IDirectoryInfo info )
8091 {
8192 if ( ! info . Exists )
93+ {
8294 throw new DirectoryNotFoundException ( StringResources . Format ( "COULD_NOT_FIND_PART_OF_PATH_EXCEPTION" , info . FullName ) ) ;
95+ }
96+ }
97+
98+ /// <summary>
99+ /// Checks if <paramref name="ancestor"/> is an ancestor of <paramref name="child"/>.
100+ /// If <paramref name="ancestor"/> is a parent this method will return <see cref="true"/>
101+ /// If <paramref name="ancestor"/> and <paramref name="child"/> are the same directory, this will return <see cref="false"/>
102+ /// </summary>
103+ /// <param name="ancestor">Ancestor directory</param>
104+ /// <param name="child">Child directory (sub-directory)</param>
105+ /// <returns>True if <paramref name="ancestor"/> is an ancestor of <paramref name="child"/> otherwise false</returns>
106+ public static bool IsAncestorOf ( this IDirectoryInfo ancestor , IDirectoryInfo child )
107+ {
108+ return child . FullName . Length > ancestor . FullName . Length &&
109+ child . FullName . StartsWith ( ancestor . FullName ) ;
110+ }
111+
112+ /// <summary>
113+ /// Checks if <paramref name="ancestor"/> is an ancestor of <paramref name="child"/> and returns
114+ /// a list of segments of the paths of <paramref name="child"/> that are not in common with <paramref name="ancestor"/>
115+ /// </summary>
116+ /// <param name="ancestor">Ancestor directory</param>
117+ /// <param name="child">Child directory (sub-directory)</param>
118+ /// <returns>A <see cref="string[]"/> with the segments of the paths of <paramref name="child"/> not in common with <paramref name="ancestor"/></returns>
119+ /// <exception cref="ArgumentException">Exception thrown if <paramref name="ancestor"/> is not an ancestor of <paramref name="child"/></exception>
120+ public static string [ ] DiffPaths ( this IDirectoryInfo ancestor , IDirectoryInfo child )
121+ {
122+ if ( ! ancestor . IsAncestorOf ( child ) )
123+ {
124+ throw new ArgumentException ( StringResources . Format ( "NOT_AN_ANCESTOR" , ancestor . FullName , child . FullName ) , nameof ( child ) ) ;
125+ }
126+
127+ return child . FullName . Substring ( ancestor . FullName . Length + 1 )
128+ . Split ( ancestor . FileSystem . Path . PathSeparator ) ;
129+ }
130+
131+ /// <summary>
132+ /// Applies a <see cref="DiffPaths(IDirectoryInfo, IDirectoryInfo)"/> between <paramref name="ancestor1"/> and <paramref name="child"/>.
133+ /// The resulting diff of path segments is applied to <paramref name="ancestor2"/> and returned.
134+ /// If the flag <paramref name="create"/> is set to true the resulting subdirectory of <paramref name="ancestor2"/> will also be created.
135+ /// <paramref name="ancestor1"/> must be the same directory or an ancestor of <paramref name="child"/> otherwise this method will throw an <see cref="ArgumentException"/>
136+ /// </summary>
137+ /// <param name="ancestor1">Ancestor directory</param>
138+ /// <param name="child">Child directory (sub-directory)</param>
139+ /// <param name="ancestor2">Directory to apply the diff to</param>
140+ /// <param name="create">If set to true, the resulting directory will also be created</param>
141+ /// <returns>An <see cref="IDirectoryInfo"/> which is either a child of <paramref name="ancestor2"/> or <paramref name="ancestor2"/> ifself</returns>
142+ public static IDirectoryInfo TranslatePaths (
143+ this IDirectoryInfo ancestor1 ,
144+ IDirectoryInfo child ,
145+ IDirectoryInfo ancestor2 ,
146+ bool create = false )
147+ {
148+ var ret = ancestor1 . Equals ( child )
149+ ? ancestor2
150+ : ancestor2 . SubDirectory ( ancestor1 . DiffPaths ( child ) ) ;
151+
152+ if ( create )
153+ {
154+ ret . Create ( ) ;
155+ }
156+
157+ return ret ;
158+ }
159+
160+ /// <summary>
161+ /// Executes a <paramref name="fileAction"/> for each file in the <paramref name="info"/> directory
162+ /// A <paramref name="directoryAction"/> action is also executed before entering any directory, including <paramref name="info"/>
163+ /// The <see cref="IDirectoryInfo"/> returned by <paramref name="directoryAction"/> is passed as parameter into <paramref name="fileAction"/>
164+ /// </summary>
165+ /// <param name="info">Directory where to search for files</param>
166+ /// <param name="fileAction">Action to apply for each file found in <paramref name="info"/></param>
167+ /// <param name="directoryAction">Action to apply upon entering any directory including <paramref name="info"/></param>
168+ /// <param name="recursive">If true the search will be recursive and will include subfolders of <paramref name="info"/>. Defaults to false</param>
169+ /// <param name="filesSearchPattern">Search pattern to apply when searching files, defaults to '*'</param>
170+ /// <param name="directoriesSearchPattern">Search pattern to apply when searching directories, defaults to '*'</param>
171+ public static void ForEachFile (
172+ this IDirectoryInfo info , Action < IFileInfo , IDirectoryInfo > fileAction ,
173+ Func < IDirectoryInfo , IDirectoryInfo > directoryAction ,
174+ bool recursive = false ,
175+ string filesSearchPattern = "*" ,
176+ string directoriesSearchPattern = "*" )
177+ {
178+ info . ThrowIfNotFound ( ) ;
179+
180+ var d = directoryAction ? . Invoke ( info ) ?? info ;
181+ foreach ( var file in info . EnumerateFiles ( filesSearchPattern ) )
182+ {
183+ fileAction . Invoke ( file , d ) ;
184+ }
185+
186+ if ( ! recursive )
187+ {
188+ return ;
189+ }
190+
191+ foreach ( var dir in info . EnumerateDirectories ( directoriesSearchPattern ) )
192+ {
193+ dir . ForEachFile ( fileAction , directoryAction , recursive , filesSearchPattern , directoriesSearchPattern ) ;
194+ }
195+ }
196+
197+ /// <summary>
198+ /// Copies files from <paramref name="source"/> to <paramref name="destination"/>
199+ /// </summary>
200+ /// <param name="source">Source directory</param>
201+ /// <param name="destination">Destination directory</param>
202+ /// <param name="recursive">If true the copy will be recursive and will include subfolders of <paramref name="info"/>. Defaults to false</param>
203+ /// <param name="filesSearchPattern">Search pattern to apply when searching files, defaults to '*'</param>
204+ /// <param name="directoriesSearchPattern">Search pattern to apply when searching directories, defaults to '*'</param>
205+ public static void CopyTo (
206+ this IDirectoryInfo source ,
207+ IDirectoryInfo destination ,
208+ bool recursive = false ,
209+ string filesSearchPattern = "*" ,
210+ string directoriesSearchPattern = "*" )
211+ {
212+ source . ForEachFile (
213+ ( file , destDir ) => file . CopyTo ( destDir . GetFilePath ( file . Name ) ) ,
214+ subDirectory => source . TranslatePaths ( subDirectory , destination , true ) ,
215+ recursive ,
216+ filesSearchPattern ,
217+ directoriesSearchPattern ) ;
83218 }
84219
85220 private static string [ ] GetPaths ( IDirectoryInfo info , IEnumerable < string > names )
0 commit comments