1 module easysettings;
2 private import siryul;
3 private import standardpaths;
4 
5 private enum settingsFilename = "settings";
6 private enum settingsExtension = "yml";
7 private alias settingsFormat = YAML;
8 /**
9  * Load settings. Will create settings file by default. Searches all system-wide
10  * settings dirs as well as the user's settings dir, and loads the first file
11  * found.
12  * Params:
13  * T = Type of settings struct to load
14  * name = Subdirectory of settings dir to save config to. Created if nonexistent.
15  * filename = The filename the settings will be loaded from.
16  */
17 auto loadSettings(T)(string name, string filename = settingsFilename) {
18 	import std.algorithm : filter, map;
19 	import std.range : chain;
20 	import std.file : exists;
21 	import std.path : buildPath, setExtension;
22 	auto paths = standardPaths(StandardPath.config).chain(["."]).map!(x => buildPath(x, name, filename.setExtension(settingsExtension))).filter!exists;
23 	if (!paths.empty) {
24 		return fromFile!(T, settingsFormat, DeSiryulize.optionalByDefault)(paths.front);
25 	} else {
26 		saveSettings(T.init, name, filename);
27 	}
28 	return T.init;
29 }
30 ///
31 unittest {
32 	struct Settings {
33 		bool blah;
34 		string text;
35 		string[] texts;
36 	}
37 	auto settings = loadSettings!Settings("testapp");
38 	settings.texts = ["a", "b", "c"];
39 	saveSettings(settings, "testapp");
40 
41 	auto reloadedSettings = loadSettings!Settings("testapp");
42 	assert(reloadedSettings == settings);
43 	assert(reloadedSettings.texts == ["a", "b", "c"]);
44 }
45 /**
46  * Saves settings. Uses user's settings dir.
47  * Params:
48  * data = The data that will be saved to the settings file.
49  * name = The subdirectory of the settings dir to save the config to. Created if nonexistent.
50  * filename = The filename the settings will be saved to.
51  */
52 void saveSettings(T)(T data, string name, string filename = settingsFilename) {
53 	import std.path : buildPath, setExtension;
54 	import std.file : exists, mkdirRecurse;
55 	string configPath = buildPath(writablePath(StandardPath.config), name);
56     if (!configPath.exists) {
57         mkdirRecurse(configPath);
58     }
59 	data.toFile!settingsFormat(buildPath(configPath, filename.setExtension(settingsExtension)));
60 }
61 ///
62 unittest {
63 	struct Settings {
64 		bool blah;
65 		string text;
66 		string[] texts;
67 	}
68 	saveSettings(Settings(true, "some words", ["c", "b", "a"]), "testapp");
69 
70 	assert(loadSettings!Settings("testapp") == Settings(true, "some words", ["c", "b", "a"]));
71 }
72 /**
73  * Deletes settings files for the specified app that are handled by this
74  * library. Also removes directory if empty.
75  * Params:
76  * name = App name.
77  * filename = The settings file that will be deleted.
78  */
79 void deleteSettings(string name, string filename = settingsFilename) {
80 	import std.path : buildPath, dirName, setExtension;
81 	import std.file : exists, remove, dirEntries, SpanMode, rmdir;
82 	auto path = buildPath(writablePath(StandardPath.config), name, filename.setExtension(settingsExtension));
83 	if (path.exists) {
84 		remove(path);
85 	}
86 	if (path.dirName.exists && path.dirName.dirEntries(SpanMode.shallow).empty) {
87 		rmdir(path.dirName);
88 	}
89 }
90 ///
91 unittest {
92 	deleteSettings("testapp");
93 }