mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-03-30 04:45:05 -04:00
Collect formatters into CommonFormatters
This commit is contained in:
63
Source/FileManager/NamingTemplate/CommonFormatters.cs
Normal file
63
Source/FileManager/NamingTemplate/CommonFormatters.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FileManager.NamingTemplate;
|
||||
|
||||
public static partial class CommonFormatters
|
||||
{
|
||||
public const string DefaultDateFormat = "yyyy-MM-dd";
|
||||
|
||||
public delegate string PropertyFormatter<in T>(ITemplateTag templateTag, T value, string formatString);
|
||||
|
||||
public static string StringFormatter(ITemplateTag _, string? value, string formatString)
|
||||
{
|
||||
if (value is null) return "";
|
||||
var culture = CultureInfo.CurrentCulture;
|
||||
|
||||
return formatString switch
|
||||
{
|
||||
"u" or "U" => value.ToUpper(culture),
|
||||
"l" or "L" => value.ToLower(culture),
|
||||
_ => value,
|
||||
};
|
||||
}
|
||||
|
||||
public static string FormattableFormatter(ITemplateTag _, IFormattable? value, string formatString)
|
||||
=> value?.ToString(formatString, null) ?? "";
|
||||
|
||||
public static string IntegerFormatter(ITemplateTag templateTag, int value, string formatString)
|
||||
=> FloatFormatter(templateTag, value, formatString);
|
||||
|
||||
public static string FloatFormatter(ITemplateTag _, float value, string formatString)
|
||||
{
|
||||
var culture = CultureInfo.CurrentCulture;
|
||||
if (!int.TryParse(formatString, out var numDigits) || numDigits <= 0) return value.ToString(formatString, culture);
|
||||
//Zero-pad the integer part
|
||||
var strValue = value.ToString(culture);
|
||||
var decIndex = culture.CompareInfo.IndexOf(strValue, culture.NumberFormat.NumberDecimalSeparator);
|
||||
var zeroPad = decIndex == -1 ? int.Max(0, numDigits - strValue.Length) : int.Max(0, numDigits - decIndex);
|
||||
|
||||
return new string('0', zeroPad) + strValue;
|
||||
}
|
||||
|
||||
public static string DateTimeFormatter(ITemplateTag _, DateTime value, string formatString)
|
||||
{
|
||||
var culture = CultureInfo.CurrentCulture;
|
||||
if (string.IsNullOrEmpty(formatString))
|
||||
formatString = CommonFormatters.DefaultDateFormat;
|
||||
return value.ToString(formatString, culture);
|
||||
}
|
||||
|
||||
public static string LanguageShortFormatter(string? language)
|
||||
{
|
||||
if (language is null)
|
||||
return "";
|
||||
|
||||
language = language.Trim();
|
||||
if (language.Length <= 3)
|
||||
return language.ToUpper();
|
||||
return language[..3].ToUpper();
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,10 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using PF = FileManager.NamingTemplate.CommonFormatters;
|
||||
|
||||
namespace FileManager.NamingTemplate;
|
||||
|
||||
public delegate string PropertyFormatter<T>(ITemplateTag templateTag, T value, string formatString);
|
||||
|
||||
public class PropertyTagCollection<TClass> : TagCollection
|
||||
{
|
||||
private readonly Dictionary<Type, MulticastDelegate> _defaultFormatters = new();
|
||||
@@ -39,7 +38,7 @@ public class PropertyTagCollection<TClass> : TagCollection
|
||||
/// <param name="formatter">Optional formatting function that accepts the <typeparamref name="TProperty"/> property
|
||||
/// and a formatting string and returns the value the formatted string. If <c>null</c>, use the default
|
||||
/// <typeparamref name="TProperty"/> formatter if present, or <see cref="object.ToString"/></param>
|
||||
public void Add<TProperty>(ITemplateTag templateTag, Func<TClass, TProperty?> propertyGetter, PropertyFormatter<TProperty>? formatter = null)
|
||||
public void Add<TProperty>(ITemplateTag templateTag, Func<TClass, TProperty?> propertyGetter, PF.PropertyFormatter<TProperty>? formatter = null)
|
||||
where TProperty : struct
|
||||
=> RegisterWithFormatter(templateTag, propertyGetter, formatter);
|
||||
|
||||
@@ -63,7 +62,7 @@ public class PropertyTagCollection<TClass> : TagCollection
|
||||
/// <param name="formatter">Optional formatting function that accepts the <typeparamref name="TProperty"/> property
|
||||
/// and a formatting string and returns the value formatted to string. If <c>null</c>, use the default
|
||||
/// <typeparamref name="TProperty"/> formatter if present, or <see cref="object.ToString"/></param>
|
||||
public void Add<TProperty>(ITemplateTag templateTag, Func<TClass, TProperty> propertyGetter, PropertyFormatter<TProperty>? formatter = null)
|
||||
public void Add<TProperty>(ITemplateTag templateTag, Func<TClass, TProperty> propertyGetter, PF.PropertyFormatter<TProperty>? formatter = null)
|
||||
=> RegisterWithFormatter(templateTag, propertyGetter, formatter);
|
||||
|
||||
/// <summary>
|
||||
@@ -77,7 +76,7 @@ public class PropertyTagCollection<TClass> : TagCollection
|
||||
=> RegisterWithToString(templateTag, propertyGetter, toString);
|
||||
|
||||
private void RegisterWithFormatter<TProperty, TPropertyValue>
|
||||
(ITemplateTag templateTag, Func<TClass, TProperty> propertyGetter, PropertyFormatter<TPropertyValue>? formatter)
|
||||
(ITemplateTag templateTag, Func<TClass, TProperty> propertyGetter, PF.PropertyFormatter<TPropertyValue>? formatter)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(templateTag, nameof(templateTag));
|
||||
ArgumentValidator.EnsureNotNull(propertyGetter, nameof(propertyGetter));
|
||||
@@ -102,12 +101,12 @@ public class PropertyTagCollection<TClass> : TagCollection
|
||||
|
||||
private static string ToStringFunc<T>(T propertyValue) => propertyValue?.ToString() ?? "";
|
||||
|
||||
private PropertyFormatter<T>? GetDefaultFormatter<T>()
|
||||
private PF.PropertyFormatter<T>? GetDefaultFormatter<T>()
|
||||
{
|
||||
try
|
||||
{
|
||||
var del = _defaultFormatters.FirstOrDefault(kvp => kvp.Key == typeof(T)).Value;
|
||||
return del is null ? null : Delegate.CreateDelegate(typeof(PropertyFormatter<T>), del.Target, del.Method) as PropertyFormatter<T>;
|
||||
return del is null ? null : Delegate.CreateDelegate(typeof(PF.PropertyFormatter<T>), del.Target, del.Method) as PF.PropertyFormatter<T>;
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
@@ -136,7 +135,7 @@ public class PropertyTagCollection<TClass> : TagCollection
|
||||
public override Regex NameMatcher { get; }
|
||||
private Func<Expression, string, Expression> CreateToStringExpression { get; }
|
||||
|
||||
public PropertyTag(ITemplateTag templateTag, RegexOptions options, Expression propertyGetter, PropertyFormatter<TPropertyValue> formatter)
|
||||
public PropertyTag(ITemplateTag templateTag, RegexOptions options, Expression propertyGetter, PF.PropertyFormatter<TPropertyValue> formatter)
|
||||
: base(templateTag, propertyGetter)
|
||||
{
|
||||
NameMatcher = new Regex(@$"^<{templateTag.TagName.Replace(" ", "\\s*?")}\s*?(?:\[([^\[\]]*?)\]\s*?)?>", options | RegexOptions.Compiled);
|
||||
|
||||
@@ -22,7 +22,7 @@ public class SeriesOrder : IFormattable
|
||||
public string ToString(string? format, IFormatProvider? formatProvider)
|
||||
=> string.Concat(OrderParts.Select(p => p switch
|
||||
{
|
||||
float f => f.ToString(format),
|
||||
float f => f.ToString(format, formatProvider ?? CultureInfo.InvariantCulture),
|
||||
_ => p.ToString(),
|
||||
})).Trim();
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace LibationFileManager.Templates;
|
||||
|
||||
public sealed class TemplateTags : ITemplateTag
|
||||
{
|
||||
public const string DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
|
||||
public string TagName { get; }
|
||||
public string DefaultValue { get; }
|
||||
public string Description { get; }
|
||||
@@ -48,9 +47,11 @@ public sealed class TemplateTags : ITemplateTag
|
||||
public static TemplateTags Language { get; } = new("language", "Book's language");
|
||||
public static TemplateTags LanguageShort { get; } = new("language short", "Book's language abbreviated. Eg: ENG");
|
||||
|
||||
public static TemplateTags FileDate { get; } = new TemplateTags("file date", "File date/time. e.g. yyyy-MM-dd HH-mm", $"<file date [{DEFAULT_DATE_FORMAT}]>", "<file date [...]>");
|
||||
public static TemplateTags DatePublished { get; } = new TemplateTags("pub date", "Publication date. e.g. yyyy-MM-dd", $"<pub date [{DEFAULT_DATE_FORMAT}]>", "<pub date [...]>");
|
||||
public static TemplateTags DateAdded { get; } = new TemplateTags("date added", "Date added to your Audible account. e.g. yyyy-MM-dd", $"<date added [{DEFAULT_DATE_FORMAT}]>", "<date added [...]>");
|
||||
public static TemplateTags FileDate { get; } = new TemplateTags("file date", "File date/time. e.g. yyyy-MM-dd HH-mm", $"<file date [{CommonFormatters.DefaultDateFormat}]>", "<file date [...]>");
|
||||
public static TemplateTags DatePublished { get; } = new TemplateTags("pub date", "Publication date. e.g. yyyy-MM-dd", $"<pub date [{CommonFormatters.DefaultDateFormat}]>", "<pub date [...]>");
|
||||
|
||||
public static TemplateTags DateAdded { get; } =
|
||||
new TemplateTags("date added", "Date added to your Audible account. e.g. yyyy-MM-dd", $"<date added [{CommonFormatters.DefaultDateFormat}]>", "<date added [...]>");
|
||||
public static TemplateTags IfSeries { get; } = new TemplateTags("if series", "Only include if part of a book series or podcast", "<if series-><-if series>", "<if series->...<-if series>");
|
||||
public static TemplateTags IfPodcast { get; } = new TemplateTags("if podcast", "Only include if part of a podcast", "<if podcast-><-if podcast>", "<if podcast->...<-if podcast>");
|
||||
public static TemplateTags IfPodcastParent { get; } = new TemplateTags("if podcastparent", "Only include if item is a podcast series parent", "<if podcastparent-><-if podcastparent>", "<if podcastparent->...<-if podcastparent>");
|
||||
|
||||
@@ -252,7 +252,7 @@ public abstract class Templates
|
||||
#region Registered Template Properties
|
||||
|
||||
private static readonly PropertyTagCollection<LibraryBookDto> filePropertyTags =
|
||||
new(caseSensitive: true, StringFormatter, DateTimeFormatter, IntegerFormatter, FloatFormatter)
|
||||
new(caseSensitive: true, CommonFormatters.StringFormatter, CommonFormatters.DateTimeFormatter, CommonFormatters.IntegerFormatter, CommonFormatters.FloatFormatter)
|
||||
{
|
||||
//Don't allow formatting of Id
|
||||
{ TemplateTags.Id, lb => lb.AudibleProductId, v => v },
|
||||
@@ -261,15 +261,15 @@ public abstract class Templates
|
||||
{ TemplateTags.AudibleTitle, lb => lb.Title },
|
||||
{ TemplateTags.AudibleSubtitle, lb => lb.Subtitle },
|
||||
{ TemplateTags.Author, lb => lb.Authors, NameListFormat.Formatter },
|
||||
{ TemplateTags.FirstAuthor, lb => lb.FirstAuthor, FormattableFormatter },
|
||||
{ TemplateTags.FirstAuthor, lb => lb.FirstAuthor, CommonFormatters.FormattableFormatter },
|
||||
{ TemplateTags.Narrator, lb => lb.Narrators, NameListFormat.Formatter },
|
||||
{ TemplateTags.FirstNarrator, lb => lb.FirstNarrator, FormattableFormatter },
|
||||
{ TemplateTags.FirstNarrator, lb => lb.FirstNarrator, CommonFormatters.FormattableFormatter },
|
||||
{ TemplateTags.Series, lb => lb.Series, SeriesListFormat.Formatter },
|
||||
{ TemplateTags.FirstSeries, lb => lb.FirstSeries, FormattableFormatter },
|
||||
{ TemplateTags.SeriesNumber, lb => lb.FirstSeries?.Order, FormattableFormatter },
|
||||
{ TemplateTags.FirstSeries, lb => lb.FirstSeries, CommonFormatters.FormattableFormatter },
|
||||
{ TemplateTags.SeriesNumber, lb => lb.FirstSeries?.Order, CommonFormatters.FormattableFormatter },
|
||||
{ TemplateTags.Language, lb => lb.Language },
|
||||
//Don't allow formatting of LanguageShort
|
||||
{ TemplateTags.LanguageShort, lb => lb.Language, GetLanguageShort },
|
||||
{ TemplateTags.LanguageShort, lb => lb.Language, CommonFormatters.LanguageShortFormatter },
|
||||
{ TemplateTags.Account, lb => lb.Account },
|
||||
{ TemplateTags.AccountNickname, lb => lb.AccountNickname },
|
||||
{ TemplateTags.Locale, lb => lb.Locale },
|
||||
@@ -280,7 +280,7 @@ public abstract class Templates
|
||||
};
|
||||
|
||||
private static readonly PropertyTagCollection<LibraryBookDto> audioFilePropertyTags =
|
||||
new(caseSensitive: true, StringFormatter, IntegerFormatter)
|
||||
new(caseSensitive: true, CommonFormatters.StringFormatter, CommonFormatters.IntegerFormatter)
|
||||
{
|
||||
{ TemplateTags.Bitrate, lb => lb.BitRate },
|
||||
{ TemplateTags.SampleRate, lb => lb.SampleRate },
|
||||
@@ -292,17 +292,16 @@ public abstract class Templates
|
||||
|
||||
private static readonly List<TagCollection> chapterPropertyTags =
|
||||
[
|
||||
new PropertyTagCollection<LibraryBookDto>(caseSensitive: true, StringFormatter)
|
||||
new PropertyTagCollection<LibraryBookDto>(caseSensitive: true, CommonFormatters.StringFormatter)
|
||||
{
|
||||
{ TemplateTags.Title, lb => lb.TitleWithSubtitle },
|
||||
{ TemplateTags.TitleShort, lb => GetTitleShort(lb.Title) },
|
||||
{ TemplateTags.AudibleTitle, lb => lb.Title },
|
||||
{ TemplateTags.AudibleSubtitle, lb => lb.Subtitle },
|
||||
{ TemplateTags.Series, lb => lb.Series, SeriesListFormat.Formatter },
|
||||
{ TemplateTags.FirstSeries, lb => lb.FirstSeries, FormattableFormatter },
|
||||
{ TemplateTags.FirstSeries, lb => lb.FirstSeries, CommonFormatters.FormattableFormatter },
|
||||
},
|
||||
|
||||
new PropertyTagCollection<MultiConvertFileProperties>(caseSensitive: true, StringFormatter, IntegerFormatter, DateTimeFormatter)
|
||||
new PropertyTagCollection<MultiConvertFileProperties>(caseSensitive: true, CommonFormatters.StringFormatter, CommonFormatters.IntegerFormatter, CommonFormatters.DateTimeFormatter)
|
||||
{
|
||||
{ TemplateTags.ChCount, m => m.PartsTotal },
|
||||
{ TemplateTags.ChNumber, m => m.PartsPosition },
|
||||
@@ -361,67 +360,17 @@ public abstract class Templates
|
||||
private static string? GetTitleShort(string? title)
|
||||
=> title?.IndexOf(':') > 0 ? title.Substring(0, title.IndexOf(':')) : title;
|
||||
|
||||
private static string GetLanguageShort(string? language)
|
||||
{
|
||||
if (language is null)
|
||||
return "";
|
||||
|
||||
language = language.Trim();
|
||||
if (language.Length <= 3)
|
||||
return language.ToUpper();
|
||||
return language[..3].ToUpper();
|
||||
}
|
||||
|
||||
private static string FormattableFormatter(ITemplateTag _, IFormattable? value, string formatString)
|
||||
=> value?.ToString(formatString, null) ?? "";
|
||||
|
||||
private static string StringFormatter(ITemplateTag _, string? value, string formatString)
|
||||
{
|
||||
if (value is null) return "";
|
||||
var culture = CultureInfo.CurrentCulture;
|
||||
|
||||
return formatString switch
|
||||
{
|
||||
"u" or "U" => value.ToUpper(culture),
|
||||
"l" or "L" => value.ToLower(culture),
|
||||
_ => value,
|
||||
};
|
||||
}
|
||||
|
||||
private static string IntegerFormatter(ITemplateTag templateTag, int value, string formatString)
|
||||
=> FloatFormatter(templateTag, value, formatString);
|
||||
|
||||
private static string FloatFormatter(ITemplateTag _, float value, string formatString)
|
||||
{
|
||||
var culture = CultureInfo.CurrentCulture;
|
||||
if (!int.TryParse(formatString, out var numDigits) || numDigits <= 0) return value.ToString(formatString, culture);
|
||||
//Zero-pad the integer part
|
||||
var strValue = value.ToString(culture);
|
||||
var decIndex = culture.CompareInfo.IndexOf(strValue, culture.NumberFormat.NumberDecimalSeparator);
|
||||
var zeroPad = decIndex == -1 ? int.Max(0, numDigits - strValue.Length) : int.Max(0, numDigits - decIndex);
|
||||
|
||||
return new string('0', zeroPad) + strValue;
|
||||
}
|
||||
|
||||
private static string DateTimeFormatter(ITemplateTag _, DateTime value, string formatString)
|
||||
{
|
||||
var culture = CultureInfo.CurrentCulture;
|
||||
if (string.IsNullOrEmpty(formatString))
|
||||
formatString = TemplateTags.DEFAULT_DATE_FORMAT;
|
||||
return value.ToString(formatString, culture);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public class FolderTemplate : Templates, ITemplate
|
||||
{
|
||||
public static string Name { get; } = "Folder Template";
|
||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.FolderTemplate)) ?? "";
|
||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.FolderTemplate));
|
||||
public static string DefaultTemplate { get; } = "<title short> [<id>]";
|
||||
public static IEnumerable<TagCollection> TagCollections { get; } = [filePropertyTags, audioFilePropertyTags, conditionalTags, folderConditionalTags, combinedConditionalTags];
|
||||
|
||||
public override IEnumerable<string> Errors
|
||||
=> TemplateText?.Length >= 2 && Path.IsPathFullyQualified(TemplateText) ? base.Errors.Append(ErrorFullPathIsInvalid) : base.Errors;
|
||||
=> TemplateText.Length >= 2 && Path.IsPathFullyQualified(TemplateText) ? base.Errors.Append(ErrorFullPathIsInvalid) : base.Errors;
|
||||
|
||||
protected override List<string> GetTemplatePartsStrings(List<TemplatePart> parts, ReplacementCharacters replacements)
|
||||
=> parts
|
||||
@@ -435,7 +384,7 @@ public abstract class Templates
|
||||
public class FileTemplate : Templates, ITemplate
|
||||
{
|
||||
public static string Name { get; } = "File Template";
|
||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.FileTemplate)) ?? "";
|
||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.FileTemplate));
|
||||
public static string DefaultTemplate { get; } = "<title> [<id>]";
|
||||
public static IEnumerable<TagCollection> TagCollections { get; } = [filePropertyTags, audioFilePropertyTags, conditionalTags, combinedConditionalTags];
|
||||
}
|
||||
@@ -443,7 +392,7 @@ public abstract class Templates
|
||||
public class ChapterFileTemplate : Templates, ITemplate
|
||||
{
|
||||
public static string Name { get; } = "Chapter File Template";
|
||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate)) ?? "";
|
||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate));
|
||||
public static string DefaultTemplate { get; } = "<title> [<id>] - <ch# 0> - <ch title>";
|
||||
public static IEnumerable<TagCollection> TagCollections { get; }
|
||||
= chapterPropertyTags.Append(filePropertyTags).Append(audioFilePropertyTags).Append(conditionalTags).Append(combinedConditionalTags);
|
||||
@@ -457,7 +406,7 @@ public abstract class Templates
|
||||
public class ChapterTitleTemplate : Templates, ITemplate
|
||||
{
|
||||
public static string Name { get; } = "Chapter Title Template";
|
||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.ChapterTitleTemplate)) ?? "";
|
||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.ChapterTitleTemplate));
|
||||
public static string DefaultTemplate => "<ch#> - <title short>: <ch title>";
|
||||
public static IEnumerable<TagCollection> TagCollections { get; } = chapterPropertyTags.Append(conditionalTags).Append(combinedConditionalTags);
|
||||
|
||||
|
||||
@@ -280,15 +280,7 @@ public class GetPortionFilename
|
||||
|
||||
string FormatString(ITemplateTag templateTag, string? value, string format)
|
||||
{
|
||||
if (value is null) return "";
|
||||
var culture = CultureInfo.CurrentCulture;
|
||||
|
||||
return format switch
|
||||
{
|
||||
"u" or "U" => value.ToUpper(culture),
|
||||
"l" or "L" => value.ToLower(culture),
|
||||
_ => value,
|
||||
};
|
||||
return CommonFormatters.StringFormatter(templateTag, value, format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user