Skip to content

Commit

Permalink
refactor: split code
Browse files Browse the repository at this point in the history
and:
* add some documentation
* fix linter issues
  • Loading branch information
kucaahbe committed Jan 18, 2024
1 parent 8548523 commit 685f578
Show file tree
Hide file tree
Showing 6 changed files with 361 additions and 297 deletions.
310 changes: 13 additions & 297 deletions source/app.d
Original file line number Diff line number Diff line change
@@ -1,316 +1,32 @@
import std.stdio;
import core.stdc.stdlib;
import std.array;
import std.conv: to;
import std.path: asNormalizedPath, baseName;
import cli;
import config_parser;
import path;
import config;

private immutable auto CONFIG_FILENAME = "install.sdl";
private string progname = "myconfigs";
int status_variant = 1;
private Config app_config;

int main(string[] args)
{
import std.path: absolutePath, asNormalizedPath, baseName;
import std.file: exists, readText, FileException;
import std.utf: UTFException;

progname = args[0].asNormalizedPath.array.baseName();
const auto configFilePath = CONFIG_FILENAME.absolutePath();
const auto configFile = Path("install.sdl");

if (!configFilePath.exists()) {
print_error("config file " ~ configFilePath ~ " does not exist");
return 1;
}

import sdlite.parser;
try
{
parseSDLDocument!(processSDLNode)(configFilePath.readText(), configFilePath);
}
catch (FileException e)
{
print_error("failed to open config file:\n " ~ e.msg);
return 1;
}
catch (UTFException e)
{
print_error("failed to read config file:\n " ~ e.msg);
return 1;
}
catch (SDLParserException e)
{
print_error(
"failed to parse config file at "
~ configFilePath
~ ":" ~ to!string(e.location.line+1) ~ ":" ~ to!string(e.location.column+1)
~ ":\n "
~ e.error
);
return 1;
}
parse_config(configFile, app_config);

if (args.length >= 2) {
switch (args[1]) {
case "install":
makeInstall();
cli_install(app_config);
break;
default:
showStatus(args);
cli_status(args, app_config);
break;
}
} else {
showStatus(args);
}

return 0;
}

void showStatus(ref string[] args)
{
parseCliOptionsForStatus(args);

switch (status_variant) {
case 1:
printStatus1();
break;
case 2:
printStatus2();
break;
default:
printStatus1();
}
}

void parseCliOptionsForStatus(ref string[] args)
{
import std.getopt;


auto helpInformation = getopt(
args,
"l", "status variant", &status_variant,
);

if (helpInformation.helpWanted)
{
defaultGetoptPrinter("Some information about the program.",
helpInformation.options);
}
}

private immutable INSTALL_DIRECTIVE = "install";
private immutable LN_DIRECTIVE = "ln";

private struct Path
{
string orig;
string absolute;

this(string path)
{
import std.path: expandTilde, absolutePath, asNormalizedPath;
orig = path;
absolute = orig.expandTilde.absolutePath().asNormalizedPath().array;
}

bool exists()
{
import std.file: exists;
return absolute.exists;
}

bool isDir()
{
import std.file: isDir;
return absolute.isDir;
}

bool isSymlink()
{
import std.file: isSymlink;
return absolute.isSymlink;
}
}

private struct Symlink {
Path source;
Path destination;

import std.file: readLink, isSymlink, symlink, rename, exists, FileException;
import std.path: absolutePath, dirName, asNormalizedPath;
import std.functional: memoize;

bool ok()
{
return source.absolute == destinationActual;
}

void link()
{
symlink(source.absolute, destination.absolute);
}

string backup()
{
auto backup = destination.absolute ~ ".bak";
try
{
rename(destination.absolute, backup);
} catch (FileException e)
{
writeln("DEBUG:", destination.absolute, " ", backup);
writeln(e.message, " ", e.errno);
backup = "FAILED BACKUP";
}
return backup;
}

Path actual()
{
return Path(destinationActual);
}

private string destinationActual()
{
if (!destination.absolute.exists) {
return [];
} else if (destination.absolute.isSymlink) {
return readLink(destination.absolute).absolutePath(destination.absolute.dirName).asNormalizedPath.array;
} else {
return destination.absolute;
}
}
}

private struct Config {
//string[string] variables;
Symlink[] symlinks;
}

private Config config;

import sdlite.ast: SDLNode, SDLValue;
private void processSDLNode(ref SDLNode node)
{
if (node.qualifiedName == INSTALL_DIRECTIVE) {
foreach (child_node ; node.children) {
switch (child_node.qualifiedName) {
case LN_DIRECTIVE:
auto values = child_node.values;
import std.algorithm.searching: all;
if (values.length == 2 && values.all!"a.isText") {
config.symlinks ~= [Symlink(
Path(values[0].textValue),
Path(values[1].textValue)
)];
} else {
// TODO: replace TODO with ln content: "ln "val1""
print_warning("ignoring incorrect \"" ~ LN_DIRECTIVE ~ "\" directive at TODO (must have 2 text values)");
}
break;
default:
print_warning("ignoring unknown \"" ~ child_node.qualifiedName ~ "\" directive");
break;
}
}
}
}

private void makeInstall()
{
import std.file: getcwd;
import std.format: format;
import std.algorithm.iteration: map, filter;
import std.algorithm.searching: maxElement;

string output = getcwd ~ " install:\n";

foreach (symlink ; config.symlinks) {

if (!symlink.source.exists) {
output ~= "# " ~ symlink.source.absolute ~ ": no such file or directory\n";
continue;
}

output ~= " ";
output ~= symlink.destination.absolute;

if (symlink.ok) {
output ~= " -> ";
output ~= symlink.source.absolute;
} else {
string backup;
if (symlink.destination.exists) {
backup = symlink.backup();
symlink.link();
} else {
symlink.link();
}
output ~= " -> ";
output ~= symlink.source.absolute;
if (backup)
output ~= " (backup: " ~ backup ~ ")";
}
output ~= "\n";
cli_status(args, app_config);
}

write(output);
}

private void printStatus1()
{
import std.file: getcwd;
import std.format: format;
import std.algorithm.iteration: map, filter;
import std.algorithm.searching: maxElement;

auto symlinksLengths = ((config.symlinks.filter!(s => s.source.exists))
.map!(s => s.source.orig.length));
auto max_src_length = to!string(symlinksLengths.empty ? 0 : symlinksLengths.maxElement);

string output = getcwd ~ ":\n";

foreach (symlink ; config.symlinks) {
output ~= symlink.ok ? "+ " : "- ";
output ~= symlink.destination.absolute;

if (symlink.ok) {
output ~= " -> ";
output ~= symlink.source.absolute;
} else {
output ~= " # ";
if (symlink.destination.exists) {
if (symlink.destination.isDir) {
output ~= "is a directory";
} else if (symlink.destination.isSymlink) {
output ~= "-> ";
output ~= symlink.actual.absolute;
}
} else {
output ~= "no such file or directory";
}
output ~= " (need -> ";
output ~= symlink.source.absolute;
if (!symlink.source.exists) {
output ~= ": no such file or directory";
}
output ~= ")";
}
output ~= "\n";
}

write(output);
}

private void printStatus2()
{
writeln("TODO: status 2");
}

private void print_error(const string msg)
{
stderr.writeln(progname ~ ": " ~ msg);
}

private void print_warning(const string msg)
{
stderr.writeln(progname ~ ": WARNING: " ~ msg);
return EXIT_SUCCESS;
}
Loading

0 comments on commit 685f578

Please sign in to comment.