1 module seed;
2 
3 // -- IMPORTS
4 
5 import core.stdc.stdlib : exit;
6 import std.ascii : isDigit, isLower, isUpper;
7 import std.conv : to;
8 import std.file : dirEntries, exists, mkdirRecurse, read, readText, remove, rmdir, thisExePath, write, SpanMode;
9 import std.path : dirName;
10 import std.stdio : writeln;
11 import std.string : capitalize, endsWith, indexOf, join, lastIndexOf, replace, split, startsWith, strip, stripRight, toLower, toUpper;
12 import std.uni : isAlpha;
13 
14 // -- TYPES
15 
16 enum VERBOSITY
17 {
18     Error,
19     Warning,
20     Activity,
21     Action,
22     Operation,
23     Information,
24     Debug
25 }
26 
27 // -- VARIABLES
28 
29 VERBOSITY
30     Verbosity = VERBOSITY.Debug;
31 
32 // -- FUNCTIONS
33 
34 void SetVerbosity(
35     VERBOSITY verbosity
36     )
37 {
38     Verbosity = verbosity;
39 }
40 
41 // ~~
42 
43 bool HasVerbosity(
44     VERBOSITY verbosity
45     )
46 {
47     return Verbosity >= verbosity;
48 }
49 
50 // ~~
51 
52 void PrintWarning(
53     string message
54     )
55 {
56     writeln( "*** WARNING : ", message );
57 }
58 
59 // ~~
60 
61 void PrintError(
62     string message
63     )
64 {
65     writeln( "*** ERROR : ", message );
66 }
67 
68 // ~~
69 
70 void Abort(
71     string message
72     )
73 {
74     PrintError( message );
75 
76     exit( -1 );
77 }
78 
79 // ~~
80 
81 void Abort(
82     string message,
83     Exception exception
84     )
85 {
86     PrintError( message );
87     PrintError( exception.msg );
88 
89     exit( -1 );
90 }
91 
92 // ~~
93 
94 bool IsLinux(
95     )
96 {
97     version ( linux )
98     {
99         return true;
100     }
101     else
102     {
103         return false;
104     }
105 }
106 
107 // ~~
108 
109 bool IsMacOs(
110     )
111 {
112     version ( OSX )
113     {
114         return true;
115     }
116     else
117     {
118         return false;
119     }
120 }
121 
122 // ~~
123 
124 bool IsWindows(
125     )
126 {
127     version ( Windows )
128     {
129         return true;
130     }
131     else
132     {
133         return false;
134     }
135 }
136 
137 // ~~
138 
139 bool IsAlphabeticalCharacter(
140     char character
141     )
142 {
143     return
144         ( character >= 'a' && character <= 'z' )
145         || ( character >= 'A' && character <= 'Z' );
146 }
147 
148 // ~~
149 
150 bool IsIdentifierCharacter(
151     char character
152     )
153 {
154     return
155         ( character >= 'a' && character <= 'z' )
156         || ( character >= 'A' && character <= 'Z' )
157         || ( character >= '0' && character <= '9' )
158         || character == '_';
159 }
160 
161 // ~~
162 
163 bool IsDecimalCharacter(
164     char character
165     )
166 {
167     return ( character >= '0' && character <= '9' );
168 }
169 
170 // ~~
171 
172 bool IsDecimalCharacter(
173     dchar character
174     )
175 {
176     return ( character >= '0' && character <= '9' );
177 }
178 
179 // ~~
180 
181 bool IsNumberCharacter(
182     char character
183     )
184 {
185     return
186         ( character >= '0' && character <= '9' )
187         || character == '.'
188         || character == '-'
189         || character == 'e'
190         || character == 'E';
191 }
192 
193 // ~~
194 
195 bool IsSpaceCharacter(
196     dchar character
197     )
198 {
199     return
200         character == dchar( ' ' )
201         || character == dchar( '\t' )
202         || character == dchar( 0xA0 );
203 }
204 
205 // ~~
206 
207 bool IsDigitCharacter(
208     dchar character
209     )
210 {
211     return
212         character >= '0'
213         && character <= '9';
214 }
215 
216 // ~~
217 
218 bool IsVowelCharacter(
219     char character
220     )
221 {
222     return "aeiouy".indexOf( character ) >= 0;
223 }
224 
225 // ~~
226 
227 bool IsConsonantCharacter(
228     char character
229     )
230 {
231     return "bcdfghjklmnpqrstvwx".indexOf( character ) >= 0;
232 }
233 
234 // ~~
235 
236 bool StartsByVowel(
237     string text
238     )
239 {
240     return
241         text != ""
242         && IsVowelCharacter( text[ 0 ] );
243 }
244 
245 // ~~
246 
247 bool StartsByConsonant(
248     string text
249     )
250 {
251     return
252         text != ""
253         && IsConsonantCharacter( text[ 0 ] );
254 }
255 
256 // ~~
257 
258 bool EndsByVowel(
259     string text
260     )
261 {
262     return
263         text != ""
264         && IsVowelCharacter( text[ $ - 1 ] );
265 }
266 
267 // ~~
268 
269 bool EndsByConsonant(
270     string text
271     )
272 {
273     return
274         text != ""
275         && IsConsonantCharacter( text[ $ - 1 ] );
276 }
277 
278 // ~~
279 
280 string GetBooleanText(
281     bool boolean
282     )
283 {
284     if ( boolean )
285     {
286         return "true";
287     }
288     else
289     {
290         return "false";
291     }
292 }
293 
294 // ~~
295 
296 bool IsNatural(
297     string text
298     )
299 {
300     if ( text.length > 0 )
301     {
302         foreach ( character; text )
303         {
304             if ( character < '0'
305                  || character > '9' )
306             {
307                 return false;
308             }
309         }
310 
311         return true;
312     }
313     else
314     {
315         return false;
316     }
317 }
318 
319 // ~~
320 
321 bool IsInteger(
322     string text
323     )
324 {
325     if ( text.length > 0
326          && text[ 0 ] == '-' )
327     {
328         text = text[ 1 .. $ ];
329     }
330 
331     return IsNatural( text );
332 }
333 
334 // ~~
335 
336 ulong GetNatural(
337     string text
338     )
339 {
340     try
341     {
342         return text.to!ulong();
343     }
344     catch ( Exception exception )
345     {
346         Abort( "Invalid natural : " ~ text, exception );
347     }
348 
349     return 0;
350 }
351 
352 // ~~
353 
354 long GetInteger(
355     string text
356     )
357 {
358     try
359     {
360         return text.to!long();
361     }
362     catch ( Exception exception )
363     {
364         Abort( "Invalid integer : " ~ text, exception );
365     }
366 
367     return 0;
368 }
369 
370 // ~~
371 
372 double GetReal(
373     long integer
374     )
375 {
376     return integer.to!double();
377 }
378 
379 // ~~
380 
381 double GetReal(
382     string text
383     )
384 {
385     try
386     {
387         return text.to!double();
388     }
389     catch ( Exception exception )
390     {
391         Abort( "Invalid real : " ~ text, exception );
392     }
393 
394     return 0.0;
395 }
396 
397 // ~~
398 
399 bool ContainsText(
400     string text,
401     string searched_text
402     )
403 {
404     return text.indexOf( searched_text ) >= 0;
405 }
406 
407 // ~~
408 
409 bool HasPrefix(
410     string text,
411     string prefix
412     )
413 {
414     return text.startsWith( prefix );
415 }
416 
417 // ~~
418 
419 bool HasSuffix(
420     string text,
421     string suffix
422     )
423 {
424     return text.endsWith( suffix );
425 }
426 
427 // ~~
428 
429 string GetPrefix(
430     string text,
431     string separator
432     )
433 {
434     return text.split( separator )[ 0 ];
435 }
436 
437 // ~~
438 
439 string GetSuffix(
440     string text,
441     string separator
442     )
443 {
444     return text.split( separator )[ $ - 1 ];
445 }
446 
447 // ~~
448 
449 string AddPrefix(
450     string text,
451     string prefix
452     )
453 {
454     return prefix ~ text;
455 }
456 
457 // ~~
458 
459 string AddSuffix(
460     string text,
461     string suffix
462     )
463 {
464     return text ~ suffix;
465 }
466 
467 // ~~
468 
469 string RemovePrefix(
470     string text,
471     string prefix
472     )
473 {
474     if ( text.startsWith( prefix ) )
475     {
476         return text[ prefix.length .. $ ];
477     }
478     else
479     {
480         return text;
481     }
482 }
483 
484 // ~~
485 
486 string RemoveSuffix(
487     string text,
488     string suffix
489     )
490 {
491     if ( text.endsWith( suffix ) )
492     {
493         return text[ 0 .. $ - suffix.length ];
494     }
495     else
496     {
497         return text;
498     }
499 }
500 
501 // ~~
502 
503 string ReplacePrefix(
504     string text,
505     string old_prefix,
506     string new_prefix
507     )
508 {
509     if ( text.startsWith( old_prefix ) )
510     {
511         return new_prefix ~ text[ old_prefix.length .. $ ];
512     }
513     else
514     {
515         return text;
516     }
517 }
518 
519 // ~~
520 
521 string ReplaceSuffix(
522     string text,
523     string old_suffix,
524     string new_suffix
525     )
526 {
527     if ( text.endsWith( old_suffix ) )
528     {
529         return text[ 0 .. $ - old_suffix.length ] ~ new_suffix;
530     }
531     else
532     {
533         return text;
534     }
535 }
536 
537 // ~~
538 
539 string ReplaceText(
540     string text,
541     string old_text,
542     string new_text
543     )
544 {
545     return text.replace( old_text, new_text );
546 }
547 
548 // ~~
549 
550 string GetStrippedText(
551     string text
552     )
553 {
554     dstring
555         unicode_text;
556 
557     unicode_text = text.to!dstring();
558 
559     while ( unicode_text.length > 0
560             && IsSpaceCharacter( unicode_text[ 0 ] ) )
561     {
562         unicode_text = unicode_text[ 1 .. $ ];
563     }
564 
565     while ( unicode_text.length > 0
566             && IsSpaceCharacter( unicode_text[ $ - 1 ] ) )
567     {
568         unicode_text = unicode_text[ 0 .. $ - 1 ];
569     }
570 
571     return unicode_text.to!string();
572 }
573 
574 // ~~
575 
576 string GetLeftStrippedText(
577     string text
578     )
579 {
580     dstring
581         unicode_text;
582 
583     unicode_text = text.to!dstring();
584 
585     while ( unicode_text.length > 0
586             && IsSpaceCharacter( unicode_text[ 0 ] ) )
587     {
588         unicode_text = unicode_text[ 1 .. $ ];
589     }
590 
591     return unicode_text.to!string();
592 }
593 
594 // ~~
595 
596 string GetRightStrippedText(
597     string text
598     )
599 {
600     dstring
601         unicode_text;
602 
603     unicode_text = text.to!dstring();
604 
605     while ( unicode_text.length > 0
606             && IsSpaceCharacter( unicode_text[ $ - 1 ] ) )
607     {
608         unicode_text = unicode_text[ 0 .. $ - 1 ];
609     }
610 
611     return unicode_text.to!string();
612 }
613 
614 // ~~
615 
616 dstring GetUnaccentedCharacter(
617     dchar character,
618     string language_code = "",
619     bool next_character_is_lower_case = false
620     )
621 {
622     switch ( character )
623     {
624         case 'á', 'à', 'â' :
625         {
626             return "a";
627         }
628         case 'ä' :
629         {
630             if ( language_code == "de" )
631             {
632                 return "ae";
633             }
634             else
635             {
636                 return "a";
637             }
638         }
639         case 'é', 'è', 'ê', 'ë' :
640         {
641             return "e";
642         }
643         case 'í', 'ì', 'î', 'ï' :
644         {
645             return "i";
646         }
647         case 'ó', 'ò', 'ô' :
648         {
649             return "o";
650         }
651         case 'ö' :
652         {
653             if ( language_code == "de" )
654             {
655                 return "oe";
656             }
657             else
658             {
659                 return "o";
660             }
661         }
662         case 'œ' :
663         {
664             return "oe";
665         }
666         case 'ú', 'ù', 'û' :
667         {
668             return "u";
669         }
670         case 'ü' :
671         {
672             if ( language_code == "de" )
673             {
674                 return "ue";
675             }
676             else
677             {
678                 return "u";
679             }
680         }
681         case 'ç' :
682         {
683             return "c";
684         }
685         case 'ñ' :
686         {
687             return "n";
688         }
689         case 'ß' :
690         {
691             return "ss";
692         }
693         case 'Á', 'À', 'Â' :
694         {
695             return "A";
696         }
697         case 'Ä' :
698         {
699             if ( language_code == "de" )
700             {
701                 if ( next_character_is_lower_case )
702                 {
703                     return "Ae";
704                 }
705                 else
706                 {
707                     return "AE";
708                 }
709             }
710             else
711             {
712                 return "A";
713             }
714         }
715         case 'É', 'È', 'Ê', 'Ë' :
716         {
717             return "E";
718         }
719         case 'Í', 'Ì', 'Î' :
720         {
721             return "I";
722         }
723         case 'Ï' :
724         {
725             return "I";
726         }
727         case 'Ó', 'Ò', 'Ô' :
728         {
729             return "O";
730         }
731         case 'Ö' :
732         {
733             if ( language_code == "de" )
734             {
735                 if ( next_character_is_lower_case )
736                 {
737                     return "Oe";
738                 }
739                 else
740                 {
741                     return "OE";
742                 }
743             }
744             else
745             {
746                 return "O";
747             }
748         }
749         case 'Œ' :
750         {
751             return "Oe";
752         }
753         case 'Ú', 'Ù', 'Û' :
754         {
755             return "U";
756         }
757         case 'Ü' :
758         {
759             if ( language_code == "de" )
760             {
761                 if ( next_character_is_lower_case )
762                 {
763                     return "Ue";
764                 }
765                 else
766                 {
767                     return "UE";
768                 }
769             }
770             else
771             {
772                 return "U";
773             }
774         }
775         case 'Ç' :
776         {
777             return "C";
778         }
779         case 'Ñ' :
780         {
781             return "N";
782         }
783         case 'ẞ' :
784         {
785             if ( next_character_is_lower_case )
786             {
787                 return "Ss";
788             }
789             else
790             {
791                 return "SS";
792             }
793         }
794         default :
795         {
796             return character.to!dstring();
797         }
798     }
799 }
800 
801 // ~~
802 
803 string GetUnaccentedText(
804     string text,
805     string language_code = ""
806     )
807 {
808     dstring
809         unaccented_text,
810         unicode_text;
811 
812     unicode_text = text.to!dstring();
813 
814     foreach ( character; unicode_text )
815     {
816         unaccented_text ~= GetUnaccentedCharacter( character, language_code );
817     }
818 
819     return unaccented_text.to!string();
820 }
821 
822 // ~~
823 
824 string GetMinorCaseText(
825     string text
826     )
827 {
828     dstring
829         unicode_text;
830 
831     if ( text == "" )
832     {
833         return "";
834     }
835     else
836     {
837         unicode_text = text.to!dstring();
838 
839         return ( unicode_text[ 0 .. 1 ].toLower() ~ unicode_text[ 1 .. $ ] ).to!string();
840     }
841 }
842 
843 // ~~
844 
845 string GetMajorCaseText(
846     string text
847     )
848 {
849     dstring
850         unicode_text;
851 
852     if ( text == "" )
853     {
854         return "";
855     }
856     else
857     {
858         unicode_text = text.to!dstring();
859 
860         return ( unicode_text[ 0 .. 1 ].capitalize() ~ unicode_text[ 1 .. $ ] ).to!string();
861     }
862 }
863 
864 // ~~
865 
866 string GetLowerCaseText(
867     string text
868     )
869 {
870     return text.toLower();
871 }
872 
873 // ~~
874 
875 string GetUpperCaseText(
876     string text
877     )
878 {
879     return text.toUpper();
880 }
881 
882 // ~~
883 
884 string GetSpacedText(
885     string text
886     )
887 {
888     foreach ( character; [ '\t', '_', '-', ',', ';', ':', '.', '!', '?' ] )
889     {
890         text = text.replace( character, ' ' );
891     }
892 
893     while ( text.indexOf( "  " ) >= 0 )
894     {
895         text = text.replace( "  ", " " );
896     }
897 
898     return text;
899 }
900 
901 // ~~
902 
903 string GetPascalCaseText(
904     string text
905     )
906 {
907     string[]
908         word_array;
909 
910     word_array = text.GetSpacedText().strip().split( ' ' );
911 
912     foreach ( ref word; word_array )
913     {
914         word = word.GetMajorCaseText();
915     }
916 
917     return word_array.join( "" );
918 }
919 
920 // ~~
921 
922 string GetCamelCaseText(
923     string text
924     )
925 {
926     return text.GetPascalCaseText().GetMinorCaseText();
927 }
928 
929 // ~~
930 
931 string GetSnakeCaseText(
932     string text
933     )
934 {
935     dchar
936         character,
937         next_character,
938         prior_character;
939     long
940         character_index;
941     dstring
942         snake_case_text,
943         unicode_text;
944 
945     unicode_text = text.GetSpacedText().strip().to!dstring();
946 
947     snake_case_text = "";
948     prior_character = 0;
949 
950     for ( character_index = 0;
951           character_index < unicode_text.length;
952           ++character_index )
953     {
954         character = unicode_text[ character_index ];
955 
956         if ( character_index + 1 < unicode_text.length )
957         {
958             next_character = unicode_text[ character_index + 1 ];
959         }
960         else
961         {
962             next_character = 0;
963         }
964 
965         if ( ( prior_character.isLower()
966                && ( character.isUpper()
967                     || character.isDigit() ) )
968              || ( prior_character.isDigit()
969                   && ( character.isLower()
970                        || character.isUpper() ) )
971              || ( prior_character.isUpper()
972                   && character.isUpper()
973                   && next_character.isLower() ) )
974         {
975             snake_case_text ~= '_';
976         }
977 
978         if ( character == ' '
979              && !snake_case_text.endsWith( '_' ) )
980         {
981             character = '_';
982         }
983 
984         snake_case_text ~= character;
985         prior_character = character;
986     }
987 
988     return snake_case_text.to!string().toLower();
989 }
990 
991 // ~~
992 
993 string GetKebabCaseText(
994     string text
995     )
996 {
997     return GetSnakeCaseText( text ).replace( '_', '-' );
998 }
999 
1000 // ~~
1001 
1002 string GetSlugCaseText(
1003     string text,
1004     string language_code = ""
1005     )
1006 {
1007     dstring
1008         slug_case_text,
1009         unicode_text;
1010 
1011     unicode_text = text.GetUnaccentedText().GetSpacedText().strip().to!dstring();
1012 
1013     foreach ( character; unicode_text )
1014     {
1015         if ( character.isAlpha() )
1016         {
1017             slug_case_text ~= character.toLower();
1018         }
1019         else if ( character >= '0'
1020                   && character <= '9' )
1021         {
1022             if ( slug_case_text != ""
1023                  && !slug_case_text.endsWith( '-' )
1024                  && !IsDecimalCharacter( slug_case_text[ $ - 1 ] ) )
1025             {
1026                 slug_case_text ~= '-';
1027             }
1028 
1029             slug_case_text ~= character;
1030         }
1031         else
1032         {
1033             if ( !slug_case_text.endsWith( '-' ) )
1034             {
1035                 slug_case_text ~= '-';
1036             }
1037         }
1038     }
1039 
1040     while ( slug_case_text.endsWith( '-' ) )
1041     {
1042         slug_case_text = slug_case_text[ 0 .. $ - 1 ];
1043     }
1044 
1045     return slug_case_text.to!string();
1046 }
1047 
1048 // ~~
1049 
1050 string GetSearchCaseText(
1051     string text,
1052     string language_code = ""
1053     )
1054 {
1055     return GetSlugCaseText( text, language_code ).replace( '-', ' ' );
1056 }
1057 
1058 // ~~
1059 
1060 string GetFileCaseText(
1061     string text
1062     )
1063 {
1064     return GetSlugCaseText( text ).replace( '-', '_' );
1065 }
1066 
1067 // ~~
1068 
1069 string GetBasilText(
1070     string text
1071     )
1072 {
1073     return
1074         text
1075             .replace( "\\", "\\\\" )
1076             .replace( "~", "\\~" )
1077             .replace( "^", "\\^" )
1078             .replace( "{", "\\{" )
1079             .replace( "}", "\\}" )
1080             .replace( "\n", "\\n" )
1081             .replace( "\r", "\\r" )
1082             .replace( "\t", "\\t" )
1083             .ReplacePrefix( "#", "\\#" )
1084             .ReplacePrefix( "%", "\\%" )
1085             .ReplacePrefix( " ", "^" )
1086             .ReplaceSuffix( " ", "^" );
1087 }
1088 
1089 // ~~
1090 
1091 string GetCsvText(
1092     string text
1093     )
1094 {
1095     if ( text.indexOf( '"' ) >= 0
1096          || text.indexOf( ',' ) >= 0
1097          || text.indexOf( '\r' ) >= 0
1098          || text.indexOf( '\n' ) >= 0 )
1099     {
1100         return "\"" ~ text.replace( "\"", "\"\"" ) ~ "\"";
1101     }
1102     else
1103     {
1104         return text;
1105     }
1106 }
1107 
1108 // ~~
1109 
1110 string GetJsonText(
1111     string text
1112     )
1113 {
1114     return
1115         "\""
1116         ~ text
1117             .replace( "\\", "\\\\" )
1118             .replace( "\n", "\\n" )
1119             .replace( "\r", "\\r" )
1120             .replace( "\t", "\\t" )
1121             .replace( "\"", "\\\"" )
1122         ~ "\"";
1123 }
1124 
1125 // ~~
1126 
1127 string GetSqlText(
1128     string text
1129     )
1130 {
1131     return
1132         "\""
1133         ~ text
1134             .replace( "\\", "\\\\" )
1135             .replace( "\n", "\\n" )
1136             .replace( "\r", "\\r" )
1137             .replace( "\t", "\\t" )
1138             .replace( "\"", "\\\"" )
1139             .replace( "'", "\\'" )
1140         ~ "\"";
1141 }
1142 
1143 // ~~
1144 
1145 string GetExecutablePath(
1146     string file_name
1147     )
1148 {
1149     return dirName( thisExePath() ) ~ "/" ~ file_name;
1150 }
1151 
1152 
1153 
1154 // ~~
1155 
1156 string GetLogicalPath(
1157     string path
1158     )
1159 {
1160     return path.replace( '\\', '/' );
1161 }
1162 
1163 // ~~
1164 
1165 string GetFolderPath(
1166     string file_path
1167     )
1168 {
1169     long
1170         slash_character_index;
1171 
1172     slash_character_index = file_path.GetLogicalPath().lastIndexOf( '/' );
1173 
1174     if ( slash_character_index >= 0 )
1175     {
1176         return file_path[ 0 .. slash_character_index + 1 ];
1177     }
1178     else
1179     {
1180         return "";
1181     }
1182 }
1183 
1184 // ~~
1185 
1186 string GetFileName(
1187     string file_path
1188     )
1189 {
1190     long
1191         slash_character_index;
1192 
1193     slash_character_index = file_path.GetLogicalPath().lastIndexOf( '/' );
1194 
1195     if ( slash_character_index >= 0 )
1196     {
1197         return file_path[ slash_character_index + 1 .. $ ];
1198     }
1199     else
1200     {
1201         return file_path;
1202     }
1203 }
1204 
1205 // ~~
1206 
1207 string GetFileLabel(
1208     string file_path
1209     )
1210 {
1211     long
1212         dot_character_index;
1213     string
1214         file_name;
1215 
1216     file_name = file_path.GetFileName();
1217     dot_character_index = file_name.lastIndexOf( '.' );
1218 
1219     if ( dot_character_index >= 0 )
1220     {
1221         return file_name[ 0 .. dot_character_index ];
1222     }
1223     else
1224     {
1225         return file_name;
1226     }
1227 }
1228 
1229 // ~~
1230 
1231 string GetFileExtension(
1232     string file_path
1233     )
1234 {
1235     long
1236         dot_character_index;
1237     string
1238         file_name;
1239 
1240     file_name = file_path.GetFileName();
1241     dot_character_index = file_name.lastIndexOf( '.' );
1242 
1243     if ( dot_character_index >= 0 )
1244     {
1245         return file_name[ dot_character_index .. $ ];
1246     }
1247     else
1248     {
1249         return "";
1250     }
1251 }
1252 
1253 // ~~
1254 
1255 bool IsEmptyFolder(
1256     string folder_path
1257     )
1258 {
1259     bool
1260         it_is_empty_folder;
1261 
1262     try
1263     {
1264         it_is_empty_folder = true;
1265 
1266         foreach ( folder_entry; dirEntries( folder_path, SpanMode.shallow ) )
1267         {
1268             it_is_empty_folder = false;
1269 
1270             break;
1271         }
1272     }
1273     catch ( Exception exception )
1274     {
1275         Abort( "Can't read folder : " ~ folder_path, exception );
1276     }
1277 
1278     return it_is_empty_folder;
1279 }
1280 
1281 // ~~
1282 
1283 void CreateFolder(
1284     string folder_path
1285     )
1286 {
1287     if ( folder_path != ""
1288          && folder_path != "/"
1289          && !folder_path.exists() )
1290     {
1291         if ( HasVerbosity( VERBOSITY.Operation ) )
1292         {
1293             writeln( "Creating folder : ", folder_path );
1294         }
1295 
1296         try
1297         {
1298             folder_path.mkdirRecurse();
1299         }
1300         catch ( Exception exception )
1301         {
1302             Abort( "Can't create folder : " ~ folder_path, exception );
1303         }
1304     }
1305 }
1306 
1307 // ~~
1308 
1309 void RemoveFolder(
1310     string folder_path
1311     )
1312 {
1313     writeln( "Removing folder : ", folder_path );
1314 
1315     try
1316     {
1317         folder_path.rmdir();
1318     }
1319     catch ( Exception exception )
1320     {
1321         Abort( "Can't create folder : " ~ folder_path, exception );
1322     }
1323 }
1324 
1325 // ~~
1326 
1327 void RemoveFile(
1328     string file_path
1329     )
1330 {
1331     writeln( "Removing file : ", file_path );
1332 
1333     try
1334     {
1335         file_path.remove();
1336     }
1337     catch ( Exception exception )
1338     {
1339         Abort( "Can't remove file : " ~ file_path, exception );
1340     }
1341 }
1342 
1343 // ~~
1344 
1345 void WriteByteArray(
1346     string file_path,
1347     ubyte[] file_byte_array,
1348     bool folder_is_created = true
1349     )
1350 {
1351     if ( folder_is_created )
1352     {
1353         CreateFolder( file_path.dirName() );
1354     }
1355 
1356     if ( HasVerbosity( VERBOSITY.Operation ) )
1357     {
1358         writeln( "Writing file : ", file_path );
1359     }
1360 
1361     try
1362     {
1363         file_path.write( file_byte_array );
1364     }
1365     catch ( Exception exception )
1366     {
1367         Abort( "Can't write file : " ~ file_path, exception );
1368     }
1369 }
1370 
1371 // ~~
1372 
1373 ubyte[] ReadByteArray(
1374     string file_path
1375     )
1376 {
1377     ubyte[]
1378         file_byte_array;
1379 
1380     if ( HasVerbosity( VERBOSITY.Operation ) )
1381     {
1382         writeln( "Reading file : ", file_path );
1383     }
1384 
1385     try
1386     {
1387         file_byte_array = cast( ubyte[] )file_path.read();
1388     }
1389     catch ( Exception exception )
1390     {
1391         Abort( "Can't read file : " ~ file_path, exception );
1392     }
1393 
1394     return file_byte_array;
1395 }
1396 
1397 // ~~
1398 
1399 void WriteText(
1400     string file_path,
1401     string file_text,
1402     bool folder_is_created = true
1403     )
1404 {
1405     if ( folder_is_created )
1406     {
1407         CreateFolder( file_path.dirName() );
1408     }
1409 
1410     if ( HasVerbosity( VERBOSITY.Operation ) )
1411     {
1412         writeln( "Writing file : ", file_path );
1413     }
1414 
1415     file_text = file_text.stripRight();
1416 
1417     if ( file_text != ""
1418          && !file_text.endsWith( '\n' ) )
1419     {
1420         file_text ~= '\n';
1421     }
1422 
1423     try
1424     {
1425         if ( !file_path.exists()
1426              || file_path.readText() != file_text )
1427         {
1428             file_path.write( file_text );
1429         }
1430     }
1431     catch ( Exception exception )
1432     {
1433         Abort( "Can't write file : " ~ file_path, exception );
1434     }
1435 }
1436 
1437 // ~~
1438 
1439 string ReadText(
1440     string file_path
1441     )
1442 {
1443     string
1444         file_text;
1445 
1446     if ( HasVerbosity( VERBOSITY.Operation ) )
1447     {
1448         writeln( "Reading file : ", file_path );
1449     }
1450 
1451     try
1452     {
1453         file_text = file_path.readText();
1454     }
1455     catch ( Exception exception )
1456     {
1457         Abort( "Can't read file : " ~ file_path, exception );
1458     }
1459 
1460     return file_text;
1461 }
1462