using System;
using System.Collections.Generic;
namespace Cthangover.Tools.Services
{
///
/// Reading and changing one key of a .properties text the way
/// LocaleLoader reads it: key = value per line, # comments,
/// no escapes and no multi-line values (a line break in a value becomes a
/// space). Everything else in the text — comments, order, other keys, the
/// line endings — stays byte for byte, so an editor save shows up in a diff
/// as the one changed line.
///
public static class PropertiesText
{
/// The value of , or null when the text does not declare it (the last line wins, as in the loader).
public static string Get(string text, string key)
{
string found = null;
foreach (var line in Lines(text))
{
if (KeyOf(line) == key)
found = line.Substring(line.IndexOf('=') + 1).Trim();
}
return found;
}
/// All keys declared in the text.
public static HashSet Keys(string text)
{
var keys = new HashSet(StringComparer.Ordinal);
foreach (var line in Lines(text))
{
var key = KeyOf(line);
if (key != null)
keys.Add(key);
}
return keys;
}
///
/// The text with set to : every
/// line of the key is rewritten in place, or a key = value line is
/// appended (after a line break the text may lack). Line endings follow the text.
///
public static string Set(string text, string key, string value)
{
text ??= string.Empty;
value = (value ?? string.Empty).Replace("\r", " ").Replace("\n", " ").Trim();
var newline = text.Contains("\r\n") ? "\r\n" : "\n";
var lines = text.Split('\n');
var replaced = false;
for (var i = 0; i < lines.Length; i++)
{
var hasCr = lines[i].EndsWith("\r");
if (KeyOf(hasCr ? lines[i][..^1] : lines[i]) != key)
continue;
lines[i] = $"{key} = {value}" + (hasCr ? "\r" : string.Empty);
replaced = true;
}
if (replaced)
return string.Join("\n", lines);
var head = text.Length == 0 || text.EndsWith("\n") ? text : text + newline;
return head + $"{key} = {value}" + newline;
}
private static IEnumerable Lines(string text) => (text ?? string.Empty).Replace("\r\n", "\n").Split('\n');
private static string KeyOf(string rawLine)
{
var line = rawLine.Trim();
if (line.Length == 0 || line[0] == '#')
return null;
var eq = line.IndexOf('=');
return eq <= 0 ? null : line.Substring(0, eq).Trim();
}
}
}