feat(fls): import host file systems into the model
importProject turns a PC's or notebook's Java file tree into Swift's virtual file system, as Swift's registerVirtualFileSystem does: images by type or extension from Base64, text by type, unknown types as binary data or text, unsafe names and collisions left out with a warning, and the whole import rejected over the project quota. NSString.pathExtension is measured in the oracle, which now writes every imported file system. On the 13 files Swift reads completely every file matches, images compared by hash. Intended: a host whose file has no file system stays empty, as in Java, where Swift adds the demo files of a new device. Pending the applications step, the comparison reads our /webserver as Swift's /www and leaves out the e-mail and Gnutella state files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -69,6 +69,19 @@ export function swiftHasPrefix(value: string, prefix: string): boolean {
|
||||
return value.startsWith(prefix) || value.normalize('NFC').startsWith(prefix.normalize('NFC'));
|
||||
}
|
||||
|
||||
/**
|
||||
* `(path as NSString).pathExtension` (measured): what follows the last dot
|
||||
* of the last component, trailing slashes ignored — empty when the dot
|
||||
* starts or ends the name (".bashrc", "a.").
|
||||
*/
|
||||
export function swiftPathExtension(path: string): string {
|
||||
const trimmed = path.replace(/\/+$/, '');
|
||||
const name = trimmed.slice(trimmed.lastIndexOf('/') + 1);
|
||||
const dot = name.lastIndexOf('.');
|
||||
if (dot <= 0) return '';
|
||||
return name.slice(dot + 1);
|
||||
}
|
||||
|
||||
const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
|
||||
/**
|
||||
|
||||
@@ -126,5 +126,23 @@
|
||||
"\ufeff",
|
||||
"a\ud83d\ude00",
|
||||
"a\ue000"
|
||||
],
|
||||
"pathExtension": [
|
||||
"a.txt",
|
||||
"a.tar.gz",
|
||||
".bashrc",
|
||||
"a.",
|
||||
"a",
|
||||
"/x/a.png",
|
||||
"a.PNG",
|
||||
"a..b",
|
||||
"a b.c d",
|
||||
"x.y/z",
|
||||
"/x.y/",
|
||||
"webserver/index.html",
|
||||
"..",
|
||||
"a.b.",
|
||||
".a.b",
|
||||
"\u00e9.PNG"
|
||||
]
|
||||
}
|
||||
@@ -2253,6 +2253,24 @@
|
||||
"äöü",
|
||||
"k"
|
||||
],
|
||||
"pathExtension" : [
|
||||
"txt",
|
||||
"gz",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"png",
|
||||
"PNG",
|
||||
"b",
|
||||
"c d",
|
||||
"",
|
||||
"y",
|
||||
"html",
|
||||
"",
|
||||
"",
|
||||
"b",
|
||||
"PNG"
|
||||
],
|
||||
"sort" : [
|
||||
[
|
||||
"B",
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
swiftCompare,
|
||||
swiftEqual,
|
||||
swiftLowercased,
|
||||
swiftPathExtension,
|
||||
swiftTrim,
|
||||
} from '../src/index.js';
|
||||
|
||||
@@ -23,6 +24,7 @@ const cases = JSON.parse(readFileSync(join(here, 'strings.cases.json'), 'utf8'))
|
||||
sort: string[][];
|
||||
base64: string[];
|
||||
controlCharacters: string[];
|
||||
pathExtension: string[];
|
||||
};
|
||||
const swift = JSON.parse(readFileSync(join(here, 'strings.swift.json'), 'utf8')) as {
|
||||
trim: string[];
|
||||
@@ -31,6 +33,7 @@ const swift = JSON.parse(readFileSync(join(here, 'strings.swift.json'), 'utf8'))
|
||||
sort: string[][];
|
||||
base64: (number[] | null)[];
|
||||
controlCharacters: boolean[];
|
||||
pathExtension: string[];
|
||||
whitespacesAndNewlinesSet: [number, number][];
|
||||
controlCharactersSet: [number, number][];
|
||||
};
|
||||
@@ -76,6 +79,10 @@ describe('Swift string semantics', () => {
|
||||
expect(cases.controlCharacters.map(containsSwiftControlCharacter)).toEqual(swift.controlCharacters);
|
||||
});
|
||||
|
||||
it('finds the extension like NSString.pathExtension', () => {
|
||||
expect(cases.pathExtension.map(swiftPathExtension)).toEqual(swift.pathExtension);
|
||||
});
|
||||
|
||||
it('trims exactly the scalars Swift trims, all of Unicode', () => {
|
||||
expect(scalarRanges((scalar) => swiftTrim(`${scalar}a`) === 'a')).toEqual(swift.whitespacesAndNewlinesSet);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user