1+ using System ;
2+ using System . IO ;
3+
4+ namespace ManagedCode . Storage . Core . Helpers ;
5+
6+ /// <summary>
7+ /// Helper methods for cross-platform path operations
8+ /// </summary>
9+ public static class PathHelper
10+ {
11+ /// <summary>
12+ /// Normalizes path separators for the target system
13+ /// </summary>
14+ /// <param name="path">Path to normalize</param>
15+ /// <param name="targetSeparator">Target path separator character</param>
16+ /// <returns>Normalized path</returns>
17+ public static string NormalizePath ( string ? path , char targetSeparator = '/' )
18+ {
19+ if ( string . IsNullOrEmpty ( path ) )
20+ return string . Empty ;
21+
22+ // Replace all possible path separators with target separator
23+ return path . Replace ( '\\ ' , targetSeparator ) . Replace ( '/' , targetSeparator ) ;
24+ }
25+
26+ /// <summary>
27+ /// Normalizes path for Unix-like systems (FTP, Linux, etc.)
28+ /// Always uses forward slash (/) as separator
29+ /// </summary>
30+ /// <param name="path">Path to normalize</param>
31+ /// <returns>Unix-style path</returns>
32+ public static string ToUnixPath ( string ? path )
33+ {
34+ return NormalizePath ( path , '/' ) ;
35+ }
36+
37+ /// <summary>
38+ /// Normalizes path for Windows systems
39+ /// Always uses backslash (\) as separator
40+ /// </summary>
41+ /// <param name="path">Path to normalize</param>
42+ /// <returns>Windows-style path</returns>
43+ public static string ToWindowsPath ( string ? path )
44+ {
45+ return NormalizePath ( path , '\\ ' ) ;
46+ }
47+
48+ /// <summary>
49+ /// Gets directory path from file path and normalizes separators
50+ /// </summary>
51+ /// <param name="filePath">Full file path</param>
52+ /// <param name="targetSeparator">Target path separator</param>
53+ /// <returns>Normalized directory path or empty string</returns>
54+ public static string GetDirectoryPath ( string ? filePath , char targetSeparator = '/' )
55+ {
56+ if ( string . IsNullOrEmpty ( filePath ) )
57+ return string . Empty ;
58+
59+ var directoryPath = Path . GetDirectoryName ( filePath ) ;
60+ return NormalizePath ( directoryPath , targetSeparator ) ;
61+ }
62+
63+ /// <summary>
64+ /// Gets Unix-style directory path from file path
65+ /// </summary>
66+ /// <param name="filePath">Full file path</param>
67+ /// <returns>Unix-style directory path</returns>
68+ public static string GetUnixDirectoryPath ( string ? filePath )
69+ {
70+ return GetDirectoryPath ( filePath , '/' ) ;
71+ }
72+
73+ /// <summary>
74+ /// Combines path segments using the specified separator
75+ /// </summary>
76+ /// <param name="separator">Path separator to use</param>
77+ /// <param name="paths">Path segments to combine</param>
78+ /// <returns>Combined path</returns>
79+ public static string CombinePaths ( char separator , params string [ ] paths )
80+ {
81+ if ( paths == null || paths . Length == 0 )
82+ return string . Empty ;
83+
84+ var result = paths [ 0 ] ?? string . Empty ;
85+
86+ for ( int i = 1 ; i < paths . Length ; i ++ )
87+ {
88+ var path = paths [ i ] ;
89+ if ( string . IsNullOrEmpty ( path ) )
90+ continue ;
91+
92+ // Remove leading separators from current path
93+ path = path . TrimStart ( '/' , '\\ ' ) ;
94+
95+ // Ensure result doesn't end with separator (unless it's root)
96+ if ( result . Length > 0 && result [ ^ 1 ] != separator )
97+ result += separator ;
98+
99+ result += path ;
100+ }
101+
102+ return NormalizePath ( result , separator ) ;
103+ }
104+
105+ /// <summary>
106+ /// Combines path segments using Unix-style separators (/)
107+ /// </summary>
108+ /// <param name="paths">Path segments to combine</param>
109+ /// <returns>Combined Unix-style path</returns>
110+ public static string CombineUnixPaths ( params string [ ] paths )
111+ {
112+ return CombinePaths ( '/' , paths ) ;
113+ }
114+
115+ /// <summary>
116+ /// Combines path segments using Windows-style separators (\)
117+ /// </summary>
118+ /// <param name="paths">Path segments to combine</param>
119+ /// <returns>Combined Windows-style path</returns>
120+ public static string CombineWindowsPaths ( params string [ ] paths )
121+ {
122+ return CombinePaths ( '\\ ' , paths ) ;
123+ }
124+
125+ /// <summary>
126+ /// Ensures path is relative (doesn't start with separator)
127+ /// </summary>
128+ /// <param name="path">Path to make relative</param>
129+ /// <returns>Relative path</returns>
130+ public static string EnsureRelativePath ( string ? path )
131+ {
132+ if ( string . IsNullOrEmpty ( path ) )
133+ return string . Empty ;
134+
135+ return path . TrimStart ( '/' , '\\ ' ) ;
136+ }
137+
138+ /// <summary>
139+ /// Ensures path is absolute (starts with separator)
140+ /// </summary>
141+ /// <param name="path">Path to make absolute</param>
142+ /// <param name="separator">Path separator to use</param>
143+ /// <returns>Absolute path</returns>
144+ public static string EnsureAbsolutePath ( string ? path , char separator = '/' )
145+ {
146+ if ( string . IsNullOrEmpty ( path ) )
147+ return separator . ToString ( ) ;
148+
149+ var normalizedPath = NormalizePath ( path , separator ) ;
150+
151+ if ( normalizedPath [ 0 ] != separator )
152+ normalizedPath = separator + normalizedPath ;
153+
154+ return normalizedPath ;
155+ }
156+
157+ /// <summary>
158+ /// Checks if path is absolute (starts with separator or drive letter on Windows)
159+ /// </summary>
160+ /// <param name="path">Path to check</param>
161+ /// <returns>True if path is absolute</returns>
162+ public static bool IsAbsolutePath ( string ? path )
163+ {
164+ if ( string . IsNullOrEmpty ( path ) )
165+ return false ;
166+
167+ // Unix-style absolute path
168+ if ( path [ 0 ] == '/' || path [ 0 ] == '\\ ' )
169+ return true ;
170+
171+ // Windows-style absolute path (C:\, D:\, etc.)
172+ if ( path . Length >= 2 && char . IsLetter ( path [ 0 ] ) && path [ 1 ] == ':' )
173+ return true ;
174+
175+ return false ;
176+ }
177+
178+ /// <summary>
179+ /// Removes trailing path separators from path (except for root paths)
180+ /// </summary>
181+ /// <param name="path">Path to trim</param>
182+ /// <returns>Path without trailing separators</returns>
183+ public static string TrimTrailingSeparators ( string ? path )
184+ {
185+ if ( string . IsNullOrEmpty ( path ) || path . Length <= 1 )
186+ return path ?? string . Empty ;
187+
188+ return path . TrimEnd ( '/' , '\\ ' ) ;
189+ }
190+
191+ /// <summary>
192+ /// Gets the file name from path without directory
193+ /// </summary>
194+ /// <param name="path">Full path</param>
195+ /// <returns>File name only</returns>
196+ public static string GetFileName ( string ? path )
197+ {
198+ if ( string . IsNullOrEmpty ( path ) )
199+ return string . Empty ;
200+
201+ var normalizedPath = NormalizePath ( path ) ;
202+ var lastSeparatorIndex = normalizedPath . LastIndexOfAny ( new [ ] { '/' , '\\ ' } ) ;
203+
204+ return lastSeparatorIndex >= 0
205+ ? normalizedPath [ ( lastSeparatorIndex + 1 ) ..]
206+ : normalizedPath ;
207+ }
208+ }
0 commit comments