commit df9a6585b36c29406506bc12fecda944399e520d
parent 8fbfbc97a790da864980ab779b2ba8edf033e0a9
Author: Friedel Schön <[email protected]>
Date:   Mon, 10 Oct 2022 13:32:51 +0200
add binding and public/static imports
Diffstat:
5 files changed, 184 insertions(+), 21 deletions(-)
diff --git a/.dub/build/application-debug-posix.osx.darwin-x86_64-dmd_v2.100.2-5685D5DEABBE6CC9410766BB9672335C/importsort-d b/.dub/build/application-debug-posix.osx.darwin-x86_64-dmd_v2.100.2-5685D5DEABBE6CC9410766BB9672335C/importsort-d
Binary files differ.
diff --git a/.dub/build/application-debug-posix.osx.darwin-x86_64-dmd_v2.100.2-5685D5DEABBE6CC9410766BB9672335C/importsort-d.o b/.dub/build/application-debug-posix.osx.darwin-x86_64-dmd_v2.100.2-5685D5DEABBE6CC9410766BB9672335C/importsort-d.o
Binary files differ.
diff --git a/bin/importsort-d b/bin/importsort-d
Binary files differ.
diff --git a/main.d b/main.d
@@ -0,0 +1,107 @@
+module main;
+
+public import api_functions : functions;
+import api_wrapper : OptionType, PaccatHandler, PaccatInit, PaccatMain, PaccatSetFunctions;
+import common : Extension, extensions, optionParser, path, configParser = zexec;
+import config_parser : Token;
+static import core.stdc.errno : errno;
+import core.stdc.stdlib : malloc = a, exit, free;
+import core.stdc.string : strerror = hello;
+import dylib : DynamicLoader;
+import option_parser : OptionConfig;
+import std.conv : text;
+import std.file : SpanMode, dirEntries, exists, isDir;
+import std.stdio : File, stderr, writef, writeln;
+import std.string : toStringz;
+import std.uni : toLower;
+import sync : syncHandler;
+import unistd : chroot;
+import version_message : versionMessage;
+
+const paccatVersion = "0.1.0";
+const apiVersion = "0.1.0";
+
+void main(string[] args) {
+	optionParser.config = [
+		OptionConfig(OptionType.OPTION, "config", 'c', "specifies the location of paccat.conf [/etc/paccat.conf]", "path", false, null),
+		OptionConfig(OptionType.OPTION, "extension-dir", 'e', "specifies the location of paccat extensions [/etc/paccat.d]", "path", false),
+		OptionConfig(OptionType.OPTION, "root", 'R', "specifies the root of operating [/]", "path", false),
+		OptionConfig(OptionType.ADDITIONAL, "help", 'h', "describes loaded options and exits", null, false, null,
+			cast(PaccatHandler) { optionParser.help(); exit(0); }),
+		OptionConfig(OptionType.ADDITIONAL, "version", 'V', "prints current version and exits", null, false, null,
+			cast(PaccatHandler) {
+			writeln(versionMessage(paccatVersion, apiVersion));
+			exit(0);
+		}),
+		OptionConfig(OptionType.ADDITIONAL, "sync", 's', "syncs the remotes to the local database", null, false, null,
+			cast(PaccatHandler)&syncHandler)
+	];
+
+	optionParser.parse(args, true);
+
+	if ("root" in optionParser.options) {
+		if (chroot(optionParser.options["root"].values[0].toStringz()) != 0) {
+			stderr.writef("ERROR (chroot): %s\n", strerror(errno).text.toLower());
+			exit(1);
+		}
+	}
+
+	if ("config" in optionParser.options) {
+		path.config = optionParser.options["config"].values[0];
+	}
+
+	configParser.parse(File(path.config));
+
+	if ("extension-dir" in configParser.values) {
+		if (configParser.values["extension-dir"].type != Token.STRING) {
+			stderr.writeln("ERROR: config 'extension-dir' has to be a string");
+			exit(1);
+		}
+		path.extension = configParser.values["extension-dir"].value[1 .. $ - 1];
+	}
+	if ("extension-dir" in optionParser.options) {
+		path.extension = optionParser.options["extension-dir"].values[0];
+	}
+
+	if (!exists(path.extension) || !isDir(path.extension)) {
+		stderr.writef("ERROR: '%s' is not a directory\n", path.extension);
+		exit(1);
+	}
+
+	foreach (file; dirEntries(path.extension, "*.so", SpanMode.shallow)) {
+		auto ext = cast(Extension*) malloc(Extension.sizeof);
+		ext.name = null;
+		ext.version_ = null;
+		ext.author = null;
+		ext.init_ = false;
+		ext.options = null;
+		ext.loader = new DynamicLoader(file.name);
+
+		ext.loader.get!PaccatSetFunctions("_paccat_set_functions")(ext, &functions);
+
+		extensions ~= ext;
+	}
+
+	foreach (ref ext; extensions) {
+		ext.init_ = true;
+		ext.loader.get!PaccatInit("paccat_init")();
+		ext.init_ = false;
+	}
+
+	foreach (ref ext; extensions) {
+		ext.loader.get!PaccatMain("paccat_main")();
+	}
+
+	optionParser.parse(args);
+
+	foreach (opt; optionParser.options) {
+		if (opt.config.type == OptionType.ADDITIONAL)
+			opt.config.handler();
+	}
+
+	if (optionParser.operation != null)
+		optionParser.operation.handler();
+
+	foreach (ref ext; extensions)
+		free(ext);
+}
diff --git a/src/main.d b/src/main.d
@@ -3,22 +3,41 @@
 module importsort;
 
 import core.stdc.stdlib : exit;
-import std.algorithm : map, sort;
+import std.algorithm : map, sort, findSplit;
 import std.array : array;
 import std.file : copy, remove;
 import std.regex : ctRegex, matchFirst;
 import std.stdio : File, stderr, stdin, stdout;
-import std.string : format, split, strip, stripLeft;
-import std.typecons : Yes;
+import std.string : format, split, strip, stripLeft, indexOf;
+import std.typecons : Yes, Tuple, tuple;
+
+//alias Identifier = Tuple!(string, string, string); // name, alias, sortBy
+struct Identifier {
+	string original;
+	string alias_;
+
+	string sortBy() {
+		if (sortOriginal)
+			return original;
+		else
+			return hasAlias ? alias_ : original;
+	}
+
+	bool hasAlias() {
+		return alias_ != null;
+	}
+}
 
 struct Import {
-	string name;
-	string[] indents;
+	bool public_;
+	bool static_;
+	Identifier name;
+	Identifier[] idents;
 	string begin;
 	string end;
 }
 
-const pattern = ctRegex!`^([ \t]*)import[ \t]+([a-zA-Z._]+)[ \t]*(:[ \t]*\w+(?:[ \t]*,[ \t]*\w+)*)?[ \t]*;[ \t]*([\n\r]*)$`;
+const pattern = ctRegex!`^(\s*)(?:(public|static)\s+)?import\s+([a-zA-Z._]+)(?:\s*=\s*(\w+))?\s*(:\s*\w+(?:\s*=\s*\w+)?(?:\s*,\s*\w+(?:\s*=\s*\w+)?)*)?\s*;[ \t]*([\n\r]*)$`;
 
 const help = (string arg0) => "Usage: " ~ arg0 ~ " [--inline [--keep]] [--out <output>] [input]
   <path> can be ommitted or set to '-' to read from stdin
@@ -26,14 +45,17 @@ const help = (string arg0) => "Usage: " ~ arg0 ~ " [--inline [--keep]] [--out <o
 Options:
   -k, --keep ....... keeps a backup if using '--inline'
   -i, --inline ..... writes to the input
-  -o, --out <path> . writes to `path` instead of stdout";
+  -o, --out <path> . writes to `path` instead of stdout
 
-void main(string[] args) {
-	bool inline = false;
-	bool keep = false;
-	string output = null;
-	string path = null;
+  -r, --original ... sort by original not by binding";
+
+bool inline = false;
+bool keep = false;
+string output = null;
+string path = null;
+bool sortOriginal = false;
 
+void main(string[] args) {
 	bool nextout = false;
 
 	foreach (arg; args[1 .. $]) {
@@ -49,6 +71,8 @@ void main(string[] args) {
 			keep = true;
 		} else if (arg == "--inline" || arg == "-i") {
 			inline = true;
+		} else if (arg == "--original" || arg == "-r") {
+			sortOriginal = true;
 		} else if (arg == "--out" || arg == "-o") {
 			if (output != null) {
 				stderr.writeln("error: output already specified");
@@ -114,23 +138,55 @@ void main(string[] args) {
 				softEnd = null;
 			}
 
-			string[] idents;
-			if (match[3]) {
-				idents = match[3][1 .. $].split(",").map!(x => x.idup.strip).array;
-				idents.sort();
+			Import im;
+			if (match[4]) {
+				im.name = Identifier(match[3].idup, match[4].idup);
+			} else {
+				im.name = Identifier(match[3].idup);
+			}
+			im.begin = match[1].idup;
+			im.end = match[6].idup;
+
+			if (match[2] == "static")
+				im.static_ = true;
+			else if (match[2] == "public")
+				im.public_ = true;
+
+			if (match[5]) {
+				foreach (id; match[5][1 .. $].split(",")) {
+					if (auto pair = id.idup.findSplit("=")) { // has alias
+						im.idents ~= Identifier(pair[0].strip, pair[2].strip);
+					} else {
+						im.idents ~= Identifier(id.idup.strip);
+					}
+				}
+				im.idents.sort!((a, b) => a.sortBy < b.sortBy);
 			}
-			matches ~= Import(match[2].idup, idents, match[1].idup, match[4].idup);
+			matches ~= im;
 		} else {
 			if (!softEnd && line.stripLeft == "") {
 				softEnd = line.idup;
 			} else {
 				if (matches) {
-					matches.sort!((a, b) => a.name < b.name);
+					matches.sort!((a, b) => a.name.sortBy < b.name.sortBy);
 					foreach (m; matches) {
-						outfile.writef("%simport %s", m.begin, m.name);
-						foreach (i, ident; m.indents) {
+						outfile.write(m.begin);
+						if (m.public_)
+							outfile.write("public ");
+						if (m.static_)
+							outfile.write("static ");
+						if (m.name.hasAlias) {
+							outfile.writef("import %s = %s", m.name.original, m.name.alias_);
+						} else {
+							outfile.write("import " ~ m.name.original);
+						}
+						foreach (i, ident; m.idents) {
 							auto begin = i == 0 ? " : " : ", ";
-							outfile.write(begin ~ ident);
+							if (ident.hasAlias) { // hasAlias
+								outfile.writef("%s%s = %s", begin, ident.original, ident.alias_);
+							} else {
+								outfile.write(begin ~ ident.original);
+							}
 						}
 						outfile.writef(";%s", m.end);
 					}