feat(fls): import the e-mail programs into the editor model

The e-mail client and server of every host come into the model (core:
EmailClientConfiguration, EmailServerConfiguration, EmailMessage), from
Java's view:

- the server's accounts are those of /mailserver/konten.txt, as Java reads
  them at every simulation start; without the file those of the bean list,
  which Java would drop;
- the client's account is that of /konten.txt; without it the first of an
  older file's kontoListe, which FILIUS 2.14 no longer reads;
- messages are numbered per program in folder order, as Swift does; the
  sent folder's are sent; a date is taken only when the file gives one.

Departures from Swift: accounts and messages stay where Swift drops what
fails its checks; first and last name stay apart; a port that is no number
is the default, with a warning. On the corpus the model matches Swift's on
all 13 files; the one Swift error is named (it numbers a mailbox's messages
as their received time and writes that into the konten.txt it rewrites).

The Swift dump now carries the e-mail state, and the file system
comparison leaves out only Swift's hidden state files (and the directories
it makes for them) instead of all of /mail, /mailserver and /peer2peer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-30 07:36:05 +00:00
co-authored by Claude Opus 5.5
parent 8f9930f1ff
commit 1efbb8a660
11 changed files with 1404 additions and 27 deletions
+22 -6
View File
@@ -11,7 +11,7 @@
// the account files of test/email-*.cases.txt, where Java's errors in reading
// them are not copied (see readEmailServerFile, readEmailClientFile).
import { isJavaObject, listItems, simpleClassName, type JavaObject, type JavaValue } from './javabeans.js';
import { isJavaObject, listItems, mapEntries, simpleClassName, type JavaObject, type JavaValue } from './javabeans.js';
import { javaSplit, javaTrim } from './javaString.js';
/** AddressEntry: a display name, an address, or both. */
@@ -73,7 +73,23 @@ type Warn = (message: string) => void;
// JavaBeans
export function javaEmailAccounts(list: JavaValue | undefined, warn: Warn): JavaEmailAccount[] {
return objectsOf(list, 'EmailKonto', warn).map((account) => {
return emailAccountsOf(listItems(list), warn);
}
/**
* The client accounts of older files (EmailAnwendung's kontoListe, a map by
* address). FILIUS 2.14 has no such property and loses them; they are read
* for files without /konten.txt.
*/
export function javaLegacyEmailAccounts(map: JavaValue | undefined, warn: Warn): JavaEmailAccount[] {
return emailAccountsOf(
mapEntries(map).map(([, account]) => account),
warn,
);
}
function emailAccountsOf(items: JavaValue[], warn: Warn): JavaEmailAccount[] {
return objectsOf(items, 'EmailKonto', warn).map((account) => {
const at = (property: string) => text(account.properties.get(property), `EmailKonto ${property}`, warn);
return {
username: at('benutzername'),
@@ -92,7 +108,7 @@ export function javaEmailAccounts(list: JavaValue | undefined, warn: Warn): Java
}
export function javaEmails(list: JavaValue | undefined, warn: Warn): JavaEmail[] {
return objectsOf(list, 'Email', warn).map((email) => {
return objectsOf(listItems(list), 'Email', warn).map((email) => {
const at = (property: string) => email.properties.get(property);
const flag = (property: string, fallback: boolean) => {
const value = at(property);
@@ -117,7 +133,7 @@ export function javaEmails(list: JavaValue | undefined, warn: Warn): JavaEmail[]
}
export function javaContacts(list: JavaValue | undefined, warn: Warn): JavaContact[] {
return objectsOf(list, 'Kontakt', warn).map((contact) => {
return objectsOf(listItems(list), 'Kontakt', warn).map((contact) => {
const at = (property: string) => text(contact.properties.get(property), `Kontakt ${property}`, warn);
const number = (property: string) => {
const value = contact.properties.get(property);
@@ -172,8 +188,8 @@ function beanAddressEntry(entry: JavaObject, warn: Warn): JavaAddressEntry {
};
}
function objectsOf(list: JavaValue | undefined, simpleClass: string, warn: Warn): JavaObject[] {
return listItems(list).flatMap((item) => {
function objectsOf(items: JavaValue[], simpleClass: string, warn: Warn): JavaObject[] {
return items.flatMap((item) => {
if (isJavaObject(item) && simpleClassName(item.className) === simpleClass) return [item];
warn(`a list of ${simpleClass} holds something else, left out`);
return [];