From b6be9468995b24905af315358157d2e3e8f000b3 Mon Sep 17 00:00:00 2001 From: KnifeLemon Date: Wed, 4 Nov 2026 11:12:09 +0900 Subject: [PATCH] Read DBML database schemas as text (#854) A .dbml file (Database Markup Language, dbdiagram.io) is plain text, so it joins the TXT entry like Dart and HiveQL did in #1047: TXT, Markdown, PDF and DOCX go through the text routes. Nothing translates the schema to SQL or draws it as a diagram. Schema 65: the text donut learns "dbml"; custom claims stay put. Co-Authored-By: Claude Opus 5.5 --- docs/ENGINES.md | 2 +- src/Filee.Core/Formats/FormatRegistry.cs | 7 ++--- src/Filee.Core/Settings/AppSettings.cs | 2 +- src/Filee.Core/Settings/UserDataStore.cs | 4 +++ tests/Filee.Core.Tests/ScriptTextTests.cs | 25 +++++++++++++++--- tests/Filee.Engines.Tests/ScriptTextTests.cs | 27 +++++++++++++++++++- 6 files changed, 58 insertions(+), 9 deletions(-) diff --git a/docs/ENGINES.md b/docs/ENGINES.md index f949bd2a..ea19f378 100644 --- a/docs/ENGINES.md +++ b/docs/ENGINES.md @@ -12,7 +12,7 @@ Filee chooses engines automatically. Settings → *Engines* shows their status, | PDFsharp | built in (library) | images → PDF (CMYK pictures stay CMYK), merge, split, page ranges, page organising, lossless rotate and crop-box edits, pages per sheet (N-up), spreadsheet print-studio assembly, **compression** (smaller PDFs, see below), PDFs without their metadata | | PDFium | built in (library) | PDF → images (multi-page TIFF/GIF supported); CMYK pages → CMYK JPG/TIFF through Ghostscript when it is installed | | Unhwp | built in (library) | HWP/HWPX → TXT, Markdown, HTML (pictures in a `_files` folder next to the Markdown, embedded in the HTML) | -| Text & Markdown (Markdig) | built in (library) | **Markdown → HTML, TXT**; **TXT, LOG, PY, PAC, BAT, CMD, COMMAND, LUA, CSS, PS1, SQL, AHK, INI, RSC, C, H, CPP, CC, CXX, HPP, HH, HXX, CS, DART, HQL → UTF-8 TXT**, preserving source text without running scripts (and → PDF / DOCX through the HWPX writer, rhwp or LibreOffice) | +| Text & Markdown (Markdig) | built in (library) | **Markdown → HTML, TXT**; **TXT, LOG, PY, PAC, BAT, CMD, COMMAND, LUA, CSS, PS1, SQL, AHK, INI, RSC, C, H, CPP, CC, CXX, HPP, HH, HXX, CS, DART, HQL, DBML → UTF-8 TXT**, preserving source text without running scripts (and → PDF / DOCX through the HWPX writer, rhwp or LibreOffice) | | Spreadsheets (ExcelDataReader for XLS) | built in (library) | **XLSX, XLS, ODS, CSV, TSV → XLSX, ODS, CSV, TSV** (one CSV / TSV per sheet) | | Office Open XML | built in | **DOCM / DOTX / DOTM ↔ DOCX, XLSM / XLTX ↔ XLSX, PPTM / POTX / POTM / PPSX ↔ PPTX** (macros removed for macro-free types) | | Fonts | built in | **TTF, OTF, WOFF, WOFF2, EOT ↔ each other**; **FON (NE monochrome FNT v2/v3, supported single-byte charsets) → TTF, OTF, WOFF, WOFF2, EOT only**; CFF (PostScript) outlines become TrueType for TTF and EOT | diff --git a/src/Filee.Core/Formats/FormatRegistry.cs b/src/Filee.Core/Formats/FormatRegistry.cs index 0d12acd4..f6578503 100644 --- a/src/Filee.Core/Formats/FormatRegistry.cs +++ b/src/Filee.Core/Formats/FormatRegistry.cs @@ -143,11 +143,12 @@ public static class FormatRegistry new("sda", "SDA", FormatCategory.Presentation, ["sda", "sdd"], Writable: false), // Plain text / markup - // Text logs, source code, proxy scripts, style sheets and settings files are read as plain text, never executed or - // compiled (issues #273, #284, #254, #388, #368, #607, #536, #630, #661, #660, #864, #828, #980, #997). + // Text logs, source code, database schemas (DBML), proxy scripts, style sheets and settings files are read as plain + // text, never executed, compiled or translated (issues #273, #284, #254, #388, #368, #607, #536, #630, #661, #660, + // #864, #828, #980, #997, #854). new("txt", "TXT", FormatCategory.Text, ["txt", "log", "py", "pac", "bat", "cmd", "command", "lua", "css", "ps1", "sql", "ahk", "ini", "rsc", - "c", "h", "cpp", "cc", "cxx", "hpp", "hh", "hxx", "cs", "dart", "hql"]), + "c", "h", "cpp", "cc", "cxx", "hpp", "hh", "hxx", "cs", "dart", "hql", "dbml"]), new("md", "Markdown", FormatCategory.Text, ["md", "markdown"]), new("html", "HTML", FormatCategory.Text, ["html", "htm", "xhtml"]), // A web page saved with its pictures in one MIME file (Chrome, Edge, Word); read as the page it holds. diff --git a/src/Filee.Core/Settings/AppSettings.cs b/src/Filee.Core/Settings/AppSettings.cs index fe687759..b19cd74e 100644 --- a/src/Filee.Core/Settings/AppSettings.cs +++ b/src/Filee.Core/Settings/AppSettings.cs @@ -123,7 +123,7 @@ public static bool IsFlipKeyAllowed(string key, IEnumerable gest public sealed class AppSettings { /// Current schema version. Increase when the format changes and add a migration. - public const int CurrentSchemaVersion = 64; + public const int CurrentSchemaVersion = 65; public int SchemaVersion { get; set; } = CurrentSchemaVersion; diff --git a/src/Filee.Core/Settings/UserDataStore.cs b/src/Filee.Core/Settings/UserDataStore.cs index 7c1fb847..c6f54dfc 100644 --- a/src/Filee.Core/Settings/UserDataStore.cs +++ b/src/Filee.Core/Settings/UserDataStore.cs @@ -1008,6 +1008,10 @@ public static bool ApplyToLibrary(int from, List presets, List Assert.Equal("txt", FormatRegistry.Detect(name)?.Id); [Fact] @@ -118,4 +119,22 @@ public void Version_60_donuts_learn_dart_hql_and_kra_without_taking_custom_claim Assert.Contains("kra", profiles.Single(p => p.Id == "images").Extensions); Assert.False(SettingsMigrations.ApplyToLibrary(60, presets, profiles)); } + + [Fact] + public void Version_64_text_donuts_learn_dbml_without_taking_custom_claims() + { + var presets = BuiltInData.CreatePresets(); + var profiles = BuiltInData.CreateProfiles(); + var text = profiles.Single(p => p.Id == "text").Extensions; + text.Remove("dbml"); + + Assert.True(SettingsMigrations.ApplyToLibrary(64, presets, profiles)); + Assert.Contains("dbml", text); + Assert.False(SettingsMigrations.ApplyToLibrary(64, presets, profiles)); + + text.Remove("dbml"); + profiles.Insert(0, new ToolbarProfile { Id = "my-schemas", Name = "Schemas", Extensions = ["dbml"] }); + Assert.False(SettingsMigrations.ApplyToLibrary(64, presets, profiles)); + Assert.DoesNotContain("dbml", text); // the user's own donut has it + } } diff --git a/tests/Filee.Engines.Tests/ScriptTextTests.cs b/tests/Filee.Engines.Tests/ScriptTextTests.cs index 80c651c1..150b66f4 100644 --- a/tests/Filee.Engines.Tests/ScriptTextTests.cs +++ b/tests/Filee.Engines.Tests/ScriptTextTests.cs @@ -1,4 +1,5 @@ -// Lua scripts and CSS style sheets (issues #388, #368) become a PDF of their text, never run. +// Lua scripts, CSS style sheets and DBML database schemas (issues #388, #368, #854) become a PDF of their text, never run +// or translated. using Filee.Core.Conversion; using Filee.Core.Presets; @@ -11,6 +12,7 @@ public class ScriptTextTests(EngineFixture fx) : IClassFixture [Theory] [InlineData("site.css", "body { color: #1d9e75; }")] [InlineData("init.lua", "local greeting = \"hello\" print(greeting)")] + [InlineData("shop.dbml", "Table customers { id integer [primary key] }")] public async Task The_code_becomes_a_pdf_of_its_text(string name, string code) { var available = fx.Converters.Where(c => fx.Catalog.StatusOf(c).IsAvailable).Select(c => c.Id).ToHashSet(); @@ -24,4 +26,27 @@ public async Task The_code_becomes_a_pdf_of_its_text(string name, string code) var text = PdfDocumentReader.PlainText(PdfDocumentReader.ReadText(Assert.Single(job.Outputs))); Assert.Contains(code.Split(' ')[1], text); } + + [Fact] + public async Task A_dbml_schema_becomes_text_as_it_was_written() + { + // #854: the schema is read as text; nothing turns it into SQL. + const string schema = """ + Table customers { + id integer [primary key] + } + + Ref: orders.customer_id > customers.id + """; + var path = Path.Combine(fx.NewFolder(), "shop.dbml"); + await File.WriteAllTextAsync(path, schema, TestContext.Current.CancellationToken); + + var job = await fx.ConvertAsync([path], new Preset { TargetFormat = "txt" }); + + Assert.True(job.State == JobState.Completed, string.Join("; ", job.Files.Select(f => $"{f.ErrorKey} {f.ErrorDetail}"))); + var output = Assert.Single(job.Outputs); + Assert.Equal("shop.txt", Path.GetFileName(output)); + var text = await File.ReadAllTextAsync(output, TestContext.Current.CancellationToken); + Assert.Equal(schema.ReplaceLineEndings(), text.ReplaceLineEndings().TrimEnd()); + } }