// Kyoo - A portable and vast media library solution. // Copyright (c) Kyoo. // // See AUTHORS.md and LICENSE file in the project root for full license information. // // Kyoo is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // Kyoo is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . using System; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Linq.Expressions; using System.Reflection; using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models.Attributes; using Kyoo.Utils; namespace Kyoo.Abstractions.Controllers; public record Sort; /// /// Information about how a query should be sorted. What factor should decide the sort and in which order. /// /// For witch type this sort applies public record Sort : Sort where T : IQuery { /// /// Sort by a specific key /// /// The sort keys. This members will be used to sort the results. /// /// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendant order. /// public record By(string Key, bool Desendant = false) : Sort { /// /// Sort by a specific key /// /// The sort keys. This members will be used to sort the results. /// /// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendant order. /// public By(Expression> key, bool desendant = false) : this(Utility.GetPropertyName(key), desendant) { } } /// /// Sort by multiple keys. /// /// The list of keys to sort by. public record Conglomerate(params Sort[] List) : Sort; /// Sort randomly items public record Random(uint Seed) : Sort { public Random() : this(0) { uint seed = BitConverter.ToUInt32( BitConverter.GetBytes(new System.Random().Next(int.MinValue, int.MaxValue)), 0 ); Seed = seed; } } /// The default sort method for the given type. public record Default : Sort { public void Deconstruct(out Sort value) { value = (Sort)T.DefaultSort; } } /// /// Create a new instance from a key's name (case insensitive). /// /// A key name with an optional order specifier. Format: "key:asc", "key:desc" or "key". /// The random seed. /// An invalid key or sort specifier as been given. /// A for the given string public static Sort From(string? sortBy, uint seed) { if (string.IsNullOrEmpty(sortBy) || sortBy == "default") return new Default(); if (sortBy == "random") return new Random(seed); if (sortBy.Contains(',')) return new Conglomerate(sortBy.Split(',').Select(x => From(x, seed)).ToArray()); if (sortBy.StartsWith("random:")) return new Random(uint.Parse(sortBy["random:".Length..])); string key = sortBy.Contains(':') ? sortBy[..sortBy.IndexOf(':')] : sortBy; string? order = sortBy.Contains(':') ? sortBy[(sortBy.IndexOf(':') + 1)..] : null; bool desendant = order switch { "desc" => true, "asc" => false, null => false, _ => throw new ValidationException( $"The sort order, if set, should be :asc or :desc but it was :{order}." ) }; Type[] types = typeof(T).GetCustomAttribute()?.Types ?? new[] { typeof(T) }; PropertyInfo? property = types .Select(x => x.GetProperty( key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance ) ) .FirstOrDefault(x => x != null); if (property == null) throw new ValidationException("The given sort key is not valid."); return new By(property.Name, desendant); } }