misc/importsort-d

src/main.d in master
Repositories | Summary | Log | Files | README.md

main.d (3808B) download


  1// (c) 2022-2023 Friedel Schon <[email protected]>
  2
  3module importsort.main;
  4
  5import argparse;
  6import core.stdc.stdlib : exit;
  7import importsort.sort : Import, sortImports;
  8import std.array : replace;
  9import std.file : DirEntry, SpanMode, dirEntries, exists, isDir, isFile;
 10import std.functional : unaryFun;
 11import std.range : empty;
 12import std.stdio : File, stderr, stdin, stdout;
 13import std.string : endsWith;
 14import std.typecons : nullable;
 15
 16/// current version (and something I always forget to update oops)
 17enum VERSION = "0.3.2";
 18
 19/// configuration for sorting imports
 20@(Command("importsort-d").Description("Sorts dlang imports").Epilog("Version: v" ~ VERSION))
 21struct SortConfig {
 22	@(ArgumentGroup("Input/Output arguments")
 23			.Description(
 24				"Define in- and output behavior")) {
 25		@(NamedArgument(["recursive", "r"]).Description("recursively search in directories"))
 26		bool recursive = false;
 27
 28		@(NamedArgument(["inplace", "i"]).Description("writes to the input"))
 29		bool inplace = false;
 30
 31		@(NamedArgument(["force", "f"])
 32				.Description("always write file, don't check if sorting is required"))
 33		bool force = false;
 34
 35		@(NamedArgument(["output", "o"]).Description("writes to `path` instead of stdout"))
 36		string output;
 37
 38		@(PositionalArgument(0).Description("inputfiles or directories, use - for stdin"))
 39		string[] inputs;
 40	}
 41
 42	@(ArgumentGroup("Sorting arguments").Description("Tune import sorting algorithms")) {
 43		/// won't format the line, keep it as-is
 44		@(NamedArgument(["keep", "k"]).Description("keeps the line as-is instead of formatting"))
 45		bool keepLine = false;
 46
 47		@(NamedArgument(["attribute", "a"]).Description("public and static imports first"))
 48		 /// sort by attributes (public/static first)
 49		bool byAttribute = false;
 50
 51		@(NamedArgument(["binding", "b"]).Description("sorts by binding rather then the original"))
 52		 /// sort by binding instead of the original
 53		bool byBinding = false;
 54
 55		@(NamedArgument(["merge", "m"]).Description("merge imports which uses same file"))
 56		 /// merges imports of the same source
 57		bool merge = false;
 58
 59		/// ignore case when sorting
 60		@(NamedArgument(["ignore-case", "c"]).Description("ignore case when comparing elements"))
 61		bool ignoreCase = false;
 62	}
 63}
 64
 65/// list entries (`ls`) from all arguments
 66DirEntry[] listEntries(alias F = "true")(string[] input, bool recursive) {
 67	alias filterFunc = unaryFun!F;
 68
 69	DirEntry[] entries;
 70
 71	foreach (path; input) {
 72		if (!exists(path)) {
 73			stderr.writef("error: '%s' does not exist\n", path);
 74			exit(19);
 75		} else if (isDir(path)) {
 76			foreach (entry; dirEntries(path, recursive ? SpanMode.depth : SpanMode.shallow)) {
 77				if (entry.isFile && entry.name.endsWith(".d") && filterFunc(entry.name))
 78					entries ~= entry;
 79			}
 80		} else if (isFile(path)) {
 81			if (!path.endsWith(".d")) {
 82				stderr.writef("error: '%s' is not a .d-file\n", path);
 83				exit(11);
 84			}
 85			if (filterFunc(path))
 86				entries ~= DirEntry(path);
 87		} else {
 88			stderr.writef("error: '%s' is not a file or directory\n", path);
 89			exit(12);
 90		}
 91	}
 92	return entries;
 93}
 94
 95mixin CLI!(SortConfig).main!((SortConfig config) {
 96	if (config.recursive && config.inputs.empty) {
 97		stderr.writeln("error: cannot use '--recursive' and specify no input");
 98		exit(1);
 99	}
100
101	if (config.inplace && config.inputs.empty) {
102		stderr.writeln("error: cannot use inplace and read from stdin");
103		exit(2);
104	}
105
106	if (!config.inputs.empty && (!config.inplace || !config.output.empty)) {
107		stderr.writeln(
108			"error: if you use inputs you must use inplace sorting or provide an output");
109		exit(3);
110	}
111
112	if (config.inputs.empty) {
113		auto outfile = config.output.empty ? stdout : File(config.output);
114		sortImports(config, stdin, nullable(outfile));
115	} else {
116		listEntries(config.inputs, config.recursive).sortImports(config);
117	}
118	return 0;
119});