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 }