using System.IO; using ErsatzTV.Core.Domain; namespace ErsatzTV.Core.Graphics; public static class GraphicsElementDefaults { // Built-in "On Now / Next" text element filename -- a component of OnNowNextSeededPath below, // never itself an identity check (#568: a filename-only comparison is folder-agnostic). public const string OnNowNextFileName = "on-now-next.yml"; // Display name only. Never use it for identity -- that is IsOnNowNext below (#67 / #74 / #568). public const string OnNowNextName = "On Now / Next"; // The full path the seeder writes the built-in template to (GraphicsElementSeeder.SeedOnNowNext // builds `target` the same way). A `builtIn` discriminator must match THIS, not // `Path.GetFileName(...) == OnNowNextFileName` -- a filename-only comparison is folder-agnostic: // a user element named exactly `on-now-next.yml` in any of the other four template folders // (image/motion/subtitle/script) would also report `builtIn:true` (#568). `Kind == Text` alone // does not close this either, since a second text template could share the filename in principle. public static string OnNowNextSeededPath => Path.Combine(FileSystemLayout.GraphicsElementsTextTemplatesFolder, OnNowNextFileName); /// /// The one identity test for the built-in On Now / Next element: the exact file the seeder /// wrote, at the exact path it wrote it to, of the kind it wrote it as. Ordinal on purpose, /// and so case-sensitive on purpose. /// /// /// /// Kind is part of the identity rather than a second test any caller may add or /// skip: the seeder's own "does the built-in row exist yet?" check resolves through this /// predicate, so a row of another kind at the seeded path answering yes would suppress /// the Text row every consumer resolves. A caller applying only the path half would /// report that wrong-kind row as the built-in element while the seeder refused to treat /// it as one -- the two sites disagreeing about the same row, which is the defect this /// predicate exists to make impossible (#568). /// /// /// Callers compare in memory rather than in a Where clause, because in SQL the /// answer would be the PROVIDER's to give: GraphicsElement.Path takes no explicit /// collation (TvContext.OnModelCreating pins one only on the listed name/title /// columns), so SQLite compares it case-sensitively while MySQL uses the server default, /// which is normally case-INsensitive. Evaluating one discriminator site in SQL and the /// other in memory would let the two disagree on MySQL alone. The SQLite test suite /// cannot tell the two apart -- BINARY collation and an ordinal comparison agree on /// every input -- so this is held by keeping the comparison out of SQL, not by a test. /// /// public static bool IsOnNowNext(string path, GraphicsElementKind kind) => kind == GraphicsElementKind.Text && string.Equals(path, OnNowNextSeededPath, StringComparison.Ordinal); }