Ferrite.Data/Repositories/LangPackRepository.cs
Rama main · réplica de lectura del repositorio original.
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2022-2026 Aykut Alparslan KOC
using System.Text;
using Ferrite.TL;
using Ferrite.TL.baseLayer;
namespace Ferrite.Data.Repositories;
public class LangPackRepository : ILangPackRepository
{
private readonly IKVStore _store;
private readonly IKVStore _storeStrings;
public LangPackRepository(IKVStore store, IKVStore storeStrings)
{
_store = store;
_store.SetSchema(new TableDefinition("ferrite", "lang_packs_tl1",
new KeyDefinition("pk",
new DataColumn { Name = "lang_pack", Type = DataType.String },
new DataColumn { Name = "lang_code", Type = DataType.String })));
_storeStrings = storeStrings;
_storeStrings.SetSchema(new TableDefinition("ferrite", "lang_pack_strings_tl1",
new KeyDefinition("pk",
new DataColumn { Name = "lang_pack", Type = DataType.String },
new DataColumn { Name = "lang_code", Type = DataType.String })));
}
public bool SaveLanguage(string langPack, TLLangPackLanguage language)
{
string langCode = Encoding.UTF8.GetString(language.AsLangPackLanguage().LangCode);
return _store.Put(language.AsSpan().ToArray(), langPack, langCode);
}
public bool SaveLangPackDifference(string langPack, TLLangPackDifference difference)
{
string langCode = Encoding.UTF8.GetString(
difference.AsLangPackDifference().LangCode);
return _storeStrings.Put(difference.AsSpan().ToArray(), langPack, langCode);
}
public bool HasLanguages(string langPack) => _store.Iterate(langPack).Any();
public ValueTask<List<TLLangPackLanguage>> GetLanguagesAsync(string? langPack)
{
var result = new List<TLLangPackLanguage>();
IEnumerable<byte[]> found = string.IsNullOrEmpty(langPack)
? _store.Iterate()
: _store.Iterate(langPack);
foreach (byte[] bytes in found)
{
result.Add(ReadLanguage(bytes));
}
return new(result);
}
public ValueTask<TLLangPackLanguage?> GetLanguageAsync(string langPack,
string langCode)
{
byte[]? bytes = _store.Get(langPack, langCode);
return new(bytes == null ? null : ReadLanguage(bytes));
}
public ValueTask<TLLangPackDifference?> GetLangPackAsync(string langPack,
string langCode) =>
new(GetDifferenceInternal(langPack, langCode, 0));
public ValueTask<TLLangPackDifference?> GetDifferenceAsync(string langPack,
string langCode, int fromVersion) =>
new(GetDifferenceInternal(langPack, langCode, fromVersion));
public ValueTask<List<TLLangPackString>> GetStringsAsync(string langPack,
string langCode, ICollection<string> keys)
{
var result = new List<TLLangPackString>();
using TLLangPackDifference? difference =
GetDifferenceInternal(langPack, langCode, 0);
if (difference == null) return new(result);
var strings = difference.Value.AsLangPackDifference().Strings;
for (int i = 0; i < strings.Count; i++)
{
Span<byte> element = strings.ReadTLObject();
if (!keys.Contains(ReadStringKey(element))) continue;
byte[] copy = element.ToArray();
result.Add((TLLangPackString)new TLBytes(copy, 0, copy.Length));
}
return new(result);
}
private TLLangPackDifference? GetDifferenceInternal(string langPack,
string langCode, int fromVersion)
{
int currentVersion = fromVersion;
var strings = new Dictionary<string, byte[]>();
foreach (byte[] bytes in _storeStrings.Iterate(langPack, langCode))
{
TLLangPackDifference stored = ReadDifference(bytes);
var difference = stored.AsLangPackDifference();
if (difference.Version <= currentVersion) continue;
currentVersion = difference.Version;
var vector = difference.Strings;
for (int i = 0; i < vector.Count; i++)
{
Span<byte> element = vector.ReadTLObject();
strings[ReadStringKey(element)] = element.ToArray();
}
}
if (fromVersion == 0 && strings.Count == 0) return null;
var resultStrings = new Vector();
foreach (byte[] value in strings.Values) resultStrings.AppendTLObject(value);
LangPackDifference result = LangPackDifference.Builder()
.LangCode(Encoding.UTF8.GetBytes(langCode))
.FromVersion(fromVersion)
.Version(currentVersion)
.Strings(resultStrings)
.Build();
return result;
}
private static TLLangPackLanguage ReadLanguage(byte[] bytes)
{
var value = new TLBytes(bytes, 0, bytes.Length);
if (value.Constructor != Constructors.baseLayer_LangPackLanguage)
throw new InvalidDataException("Langpack language codec/version mismatch.");
return (TLLangPackLanguage)value;
}
private static TLLangPackDifference ReadDifference(byte[] bytes)
{
var value = new TLBytes(bytes, 0, bytes.Length);
if (value.Constructor != Constructors.baseLayer_LangPackDifference)
throw new InvalidDataException("Langpack difference codec/version mismatch.");
return (TLLangPackDifference)value;
}
private static string ReadStringKey(Span<byte> bytes)
{
var view = (LangPackStringView)bytes;
ReadOnlySpan<byte> key = view.Type switch
{
TLLangPackString.LangPackStringType.LangPackString =>
view.AsLangPackString().Key,
TLLangPackString.LangPackStringType.LangPackStringPluralized =>
view.AsLangPackStringPluralized().Key,
TLLangPackString.LangPackStringType.LangPackStringDeleted =>
view.AsLangPackStringDeleted().Key,
_ => throw new InvalidDataException("Invalid langpack string row."),
};
return Encoding.UTF8.GetString(key);
}
}