1 // =================================
  2 // Copyright (c) 2024 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 namespace System
  7 {
  8     public string TrimEnd(const string& s)
  9     {
 10         long i = s.Length();
 11         while (i > 0 && IsSpace(s[i - 1]))
 12         {
 13             --i;
 14         }
 15         return s.Substring(0i);
 16     }
 17 
 18     public ustring TrimEnd(const ustring& s)
 19     {
 20         long i = s.Length();
 21         while (i > 0 && IsSpace(cast<char>(s[i - 1])))
 22         {
 23             --i;
 24         }
 25         return s.Substring(0i);
 26     }
 27 
 28     public string Trim(const string& s)
 29     {
 30         long b = 0;
 31         while (b < s.Length() && IsSpace(s[b]))
 32         {
 33             ++b;
 34         }
 35         long e = s.Length() - 1;
 36         while (e >= b && IsSpace(s[e]))
 37         {
 38             --e;
 39         }
 40         return s.Substring(be - b + 1);
 41     }
 42 
 43     [nodiscard]
 44     public Result<wstring> Trim(const wstring& s)
 45     {
 46         auto result = ToUtf32(s);
 47         if (result.Error())
 48         {
 49             return Result<wstring>(ErrorId(result.GetErrorId()));
 50         }
 51         auto trimResult = Trim(result.Value());
 52         if (trimResult.Error())
 53         {
 54             return Result<wstring>(ErrorId(trimResult.GetErrorId()));
 55         }
 56         return ToUtf16(trimResult.Value());
 57     }
 58 
 59     [nodiscard]
 60     public Result<ustring> Trim(const ustring& s)
 61     {
 62         long b = 0;
 63         bool stop = false;
 64         while (b < s.Length() && !stop)
 65         {
 66             auto wsResult = IsWhiteSpace(s[b]);
 67             if (wsResult.Error())
 68             {
 69                 return Result<ustring>(ErrorId(wsResult.GetErrorId()));
 70             }
 71             stop = !wsResult.Value();
 72             if (!stop)
 73             {
 74                 ++b;
 75             }
 76         }
 77         long e = s.Length() - 1;
 78         stop = false;
 79         while (e >= b && !stop)
 80         {
 81             auto wsResult = IsWhiteSpace(s[e]);
 82             if (wsResult.Error())
 83             {
 84                 return Result<ustring>(ErrorId(wsResult.GetErrorId()));
 85             }
 86             stop = !wsResult.Value();
 87             if (!stop)
 88             {
 89                 --e;
 90             }
 91         }
 92         return Result<ustring>(s.Substring(be - b + 1));
 93     }
 94 
 95     public string TrimAll(const string& s)
 96     {
 97         string result;
 98         result.Reserve(s.Length());
 99         int state = 0;
100         for (char c : s)
101         {
102             switch (state)
103             {
104                 case 0:
105                 {
106                     if (!IsSpace(c))
107                     {
108                         result.Append(c);
109                         state = 1;
110                     }
111                     break;
112                 }
113                 case 1:
114                 {
115                     if (IsSpace(c))
116                     {
117                         state = 2;
118                     }
119                     else
120                     {
121                         result.Append(c);
122                     }
123                     break;
124                 }
125                 case 2:
126                 {
127                     if (!IsSpace(c))
128                     {
129                         result.Append(' ');
130                         result.Append(c);
131                         state = 1;
132                     }
133                     break;
134                 }
135             }
136         }
137         return result;
138     }
139 
140     [nodiscard]
141     public Result<wstring> TrimAll(const wstring& s)
142     {
143         auto result = ToUtf32(s);
144         if (result.Error())
145         {
146             return Result<wstring>(ErrorId(result.GetErrorId()));
147         }
148         auto trimResult = TrimAll(result.Value());
149         if (trimResult.Error())
150         {
151             return Result<wstring>(ErrorId(trimResult.GetErrorId()));
152         }
153         return ToUtf16(trimResult.Value());
154     }
155 
156     [nodiscard]
157     public Result<ustring> TrimAll(const ustring& s)
158     {
159         ustring result;
160         result.Reserve(s.Length());
161         int state = 0;
162         for (uchar c : s)
163         {
164             switch (state)
165             {
166                 case 0:
167                 {
168                     auto wsResult = IsWhiteSpace(c);
169                     if (wsResult.Error())
170                     {
171                         return Result<ustring>(ErrorId(wsResult.GetErrorId()));
172                     }
173                     if (!wsResult.Value())
174                     {
175                         result.Append(c);
176                         state = 1;
177                     }
178                     break;
179                 }
180                 case 1:
181                 {
182                     auto wsResult = IsWhiteSpace(c);
183                     if (wsResult.Error())
184                     {
185                         return Result<ustring>(ErrorId(wsResult.GetErrorId()));
186                     }
187                     if (wsResult.Value())
188                     {
189                         state = 2;
190                     }
191                     else
192                     {
193                         result.Append(c);
194                     }
195                     break;
196                 }
197                 case 2:
198                 {
199                     auto wsResult = IsWhiteSpace(c);
200                     if (wsResult.Error())
201                     {
202                         return Result<ustring>(ErrorId(wsResult.GetErrorId()));
203                     }
204                     if (!wsResult.Value())
205                     {
206                         result.Append(u' ');
207                         result.Append(c);
208                         state = 1;
209                     }
210                     break;
211                 }
212             }
213         }
214         return Result<ustring>(result);
215     }
216 
217     [nodiscard]
218     public Result<string> HexEscape(char c)
219     {
220         auto result = ToHexString(cast<byte>(c));
221         if (result.Error())
222         {
223             return result;
224         }
225         return "\\x" + result.Value();
226     }
227 
228     [nodiscard]
229     public Result<string> HexEscape(wchar c)
230     {
231         auto result = ToHexString(cast<ushort>(c));
232         if (result.Error())
233         {
234             return result;
235         }
236         return "\\x" + result.Value();
237     }
238 
239     [nodiscard]
240     public Result<string> HexEscape(uchar c)
241     {
242         auto result = ToHexString(cast<uint>(c));
243         if (result.Error())
244         {
245             return result;
246         }
247         return "\\x" + result.Value();
248     }
249 
250     [nodiscard]
251     public Result<string> CharStr(char c)
252     {
253         switch (c)
254         {
255             case '\\': return Result<string>("\\\\");
256             case '\"': return Result<string>("\\\"");
257             case '\'': return Result<string>("\\\'");
258             case '\a': return Result<string>("\\a");
259             case '\b': return Result<string>("\\b");
260             case '\f': return Result<string>("\\f");
261             case '\n': return Result<string>("\\n");
262             case '\r': return Result<string>("\\r");
263             case '\t': return Result<string>("\\t");
264             case '\v': return Result<string>("\\v");
265             case '\0': return Result<string>("\\0");
266             default:
267             {
268                 if (IsPrintable(c))
269                 {
270                     return Result<string>(string(c));
271                 }
272                 else
273                 {
274                     return HexEscape(c);
275                 }
276             }
277         }
278         return Result<string>(string());
279     }
280 
281     [nodiscard]
282     public Result<string> MakeCharLiteral(char c)
283     {
284         if (c == '\"')
285         {
286             return Result<string>(string("\'\"\'"));
287         }
288         auto charStrResult = CharStr(c);
289         if (charStrResult.Error())
290         {
291             return Result<string>(ErrorId(charStrResult.GetErrorId()));
292         }
293         return Result<string>("\'" + charStrResult.Value() + "\'");
294     }
295 
296     [nodiscard]
297     public Result<wstring> CharStr(wchar c)
298     {
299         Result<string> hexEscapeResult;
300         switch (c)
301         {
302             case w'\\': return Result<wstring>(w"\\\\");
303             case w'\"': return Result<wstring>(w"\\\"");
304             case w'\'': return Result<wstring>(w"\\\'");
305             case w'\a': return Result<wstring>(w"\\a");
306             case w'\b': return Result<wstring>(w"\\b");
307             case w'\f': return Result<wstring>(w"\\f");
308             case w'\n': return Result<wstring>(w"\\n");
309             case w'\r': return Result<wstring>(w"\\r");
310             case w'\t': return Result<wstring>(w"\\t");
311             case w'\v': return Result<wstring>(w"\\v");
312             case w'\0': return Result<wstring>(w"\\0");
313             default:
314             {
315                 if (cast<ushort>(c) < 256u && IsPrintable(cast<char>(c)))
316                 {
317                     return Result<wstring>(wstring(c));
318                 }
319                 else
320                 {
321                     hexEscapeResult = HexEscape(c);
322                     if (hexEscapeResult.Error())
323                     {
324                         return Result<wstring>(ErrorId(hexEscapeResult.GetErrorId()));
325                     }
326                     return ToUtf16(hexEscapeResult.Value());
327                 }
328             }
329         }
330         return Result<wstring>(wstring());
331     }
332 
333     [nodiscard]
334     public Result<ustring> CharStr(uchar c)
335     {
336         Result<string> hexEscapeResult;
337         switch (c)
338         {
339             case u'\\': return Result<ustring>(u"\\\\");
340             case u'\"': return Result<ustring>(u"\\\"");
341             case u'\'': return Result<ustring>(u"\\\'");
342             case u'\a': return Result<ustring>(u"\\a");
343             case u'\b': return Result<ustring>(u"\\b");
344             case u'\f': return Result<ustring>(u"\\f");
345             case u'\n': return Result<ustring>(u"\\n");
346             case u'\r': return Result<ustring>(u"\\r");
347             case u'\t': return Result<ustring>(u"\\t");
348             case u'\v': return Result<ustring>(u"\\v");
349             case u'\0': return Result<ustring>(u"\\0");
350             default:
351             {
352                 if (cast<uint>(c) < 256u && IsPrintable(cast<char>(c)))
353                 {
354                     return Result<ustring>(ustring(c));
355                 }
356                 else
357                 {
358                     hexEscapeResult = HexEscape(c);
359                     if (hexEscapeResult.Error())
360                     {
361                         return Result<ustring>(ErrorId(hexEscapeResult.GetErrorId()));
362                     }
363                     return ToUtf32(hexEscapeResult.Value());
364                 }
365             }
366         }
367         return Result<ustring>(ustring());
368     }
369 
370     [nodiscard]
371     public Result<string> StringStr(const string& s)
372     {
373         string result;
374         for (char c : s)
375         {
376             if (c == '\'')
377             {
378                 result.Append(c);
379             }
380             else
381             {
382                 auto charStrResult = CharStr(c);
383                 if (charStrResult.Error())
384                 {
385                     return Result<string>(ErrorId(charStrResult.GetErrorId()));
386                 }
387                 result.Append(charStrResult.Value());
388             }
389         }
390         return Result<string>(result);
391     }
392 
393     [nodiscard]
394     public Result<ustring> StringStr(const ustring& s)
395     {
396         ustring result;
397         for (uchar c : s)
398         {
399             if (c == u'\'')
400             {
401                 result.Append(c);
402             }
403             else
404             {
405                 auto charStrResult = CharStr(c);
406                 if (charStrResult.Error())
407                 {
408                     return Result<ustring>(ErrorId(charStrResult.GetErrorId()));
409                 }
410                 result.Append(charStrResult.Value());
411             }
412         }
413         return Result<ustring>(result);
414     }
415 
416     [nodiscard]
417     public Result<string> MakeStringLiteral(const string& s)
418     {
419         string result = "\"";
420         auto stringStrResult = StringStr(s);
421         if (stringStrResult.Error())
422         {
423             return Result<string>(ErrorId(stringStrResult.GetErrorId()));
424         }
425         result.Append(stringStrResult.Value());
426         result.Append('\"');
427         return Result<string>(result);
428     }
429 
430     [nodiscard]
431     public Result<ustring> MakeStringLiteral(const ustring& s)
432     {
433         ustring result = u"\"";
434         auto stringStrResult = StringStr(s);
435         if (stringStrResult.Error())
436         {
437             return Result<ustring>(ErrorId(stringStrResult.GetErrorId()));
438         }
439         result.Append(stringStrResult.Value());
440         result.Append(u'\"');
441         return Result<ustring>(result);
442     }
443 
444     public string Unescape(const string& stringStr)
445     {
446         string result;
447         int state = 0;
448         for (char c : stringStr)
449         {
450             switch (state)
451             {
452                 case 0:
453                 {
454                     if (c == '\\')
455                     {
456                         state = 1;
457                     }
458                     else
459                     {
460                         result.Append(c);
461                     }
462                     break;
463                 }
464                 case 1:
465                 {
466                     switch (c)
467                     {
468                         case '\\':
469                         case '\"':
470                         case '\'':
471                         {
472                             result.Append(c);
473                             break;
474                         }
475                         case 'n':
476                         {
477                             result.Append("\n");
478                             break;
479                         }
480                         default:
481                         {
482                             result.Append('\\').Append(c);
483                             break;
484                         }
485                     }
486                     state = 0;
487                     break;
488                 }
489             }
490         }
491         return result;
492     }
493 
494     public ustring Unescape(const ustring& stringStr)
495     {
496         ustring result;
497         int state = 0;
498         for (uchar c : stringStr)
499         {
500             switch (state)
501             {
502                 case 0:
503                 {
504                     if (c == '\\')
505                     {
506                         state = 1;
507                     }
508                     else
509                     {
510                         result.Append(c);
511                     }
512                     break;
513                 }
514                 case 1:
515                 {
516                     switch (c)
517                     {
518                         case '\\':
519                         case '\"':
520                         case '\'':
521                         {
522                             result.Append(c);
523                             break;
524                         }
525                         case 'n':
526                         {
527                             result.Append('\n');
528                             break;
529                         }
530                         default:
531                         {
532                             result.Append('\\').Append(c);
533                             break;
534                         }
535                     }
536                     state = 0;
537                     break;
538                 }
539             }
540         }
541         return result;
542     }
543 
544     public enum FormatWidth
545     {
546         exactmin
547     }
548 
549     public enum FormatJustify
550     {
551         leftright
552     }
553 
554     public string Format(const string& sint width)
555     {
556         return Format(swidthFormatWidth.exactFormatJustify.left);
557     }
558 
559     public string Format(const string& sint widthFormatJustify justify)
560     {
561         return Format(swidthFormatWidth.exactjustify);
562     }
563 
564     public string Format(const string& sint widthFormatWidth fw)
565     {
566         return Format(swidthfwFormatJustify.left);
567     }
568 
569     public string Format(const string& sint widthFormatWidth fwFormatJustify justify)
570     {
571         return Format(swidthfwjustify' ');
572     }
573 
574     public string Format(const string& sint widthFormatWidth fwFormatJustify justifychar fillChar)
575     {
576         string result;
577         int m = cast<int>(s.Length());
578         if (fw == FormatWidth.min)
579         {
580             width = Max(widthm);
581         }
582         else if (fw == FormatWidth.exact)
583         {
584             m = Min(mwidth);
585         }
586         int n = Max(0width - m);
587         if (justify == FormatJustify.right)
588         {
589             for (int i = 0; i < n; ++i;)
590             {
591                 result.Append(fillChar);
592             }
593         }
594         for (int i = 0; i < m; ++i;)
595         {
596             result.Append(s[i]);
597         }
598         if (justify == FormatJustify.left)
599         {
600             for (int i = 0; i < n; ++i;)
601             {
602                 result.Append(fillChar);
603             }
604         }
605         return result;
606     }
607 
608     public ustring Format(const ustring& sint width)
609     {
610         return Format(swidthFormatWidth.exactFormatJustify.left);
611     }
612 
613     public ustring Format(const ustring& sint widthFormatJustify justify)
614     {
615         return Format(swidthFormatWidth.exactjustify);
616     }
617 
618     public ustring Format(const ustring& sint widthFormatWidth fwFormatJustify justify)
619     {
620         return Format(swidthfwjustify' ');
621     }
622 
623     public ustring Format(const ustring& sint widthFormatWidth fwFormatJustify justifychar fillChar)
624     {
625         ustring result;
626         int m = cast<int>(s.Length());
627         if (fw == FormatWidth.min)
628         {
629             width = Max(widthm);
630         }
631         else if (fw == FormatWidth.exact)
632         {
633             m = Min(mwidth);
634         }
635         int n = Max(0width - m);
636         if (justify == FormatJustify.right)
637         {
638             for (int i = 0; i < n; ++i;)
639             {
640                 result.Append(fillChar);
641             }
642         }
643         for (int i = 0; i < m; ++i;)
644         {
645             result.Append(s[i]);
646         }
647         if (justify == FormatJustify.left)
648         {
649             for (int i = 0; i < n; ++i;)
650             {
651                 result.Append(fillChar);
652             }
653         }
654         return result;
655     }