Mercurial > projects > dynamin
view dynamin/core/settings.d @ 61:f8f5d6244795
Edit the Settings unittest to load, save, then load the saved test data.
The data is then tested, verifying that it was saved and loaded correctly.
author | Jordan Miner <jminer7@gmail.com> |
---|---|
date | Sat, 08 Aug 2009 20:20:35 -0500 |
parents | aa4efef0f0b1 |
children | 73060bc3f004 |
line wrap: on
line source
// Written in the D programming language // www.digitalmars.com/d/ /* * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Dynamin library. * * The Initial Developer of the Original Code is Jordan Miner. * Portions created by the Initial Developer are Copyright (C) 2007-2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Jordan Miner <jminer7@gmail.com> * */ module dynamin.core.settings; import dynamin.core.string; import tango.io.device.Conduit; import tango.io.device.File; import tango.io.device.Array; import tango.io.stream.Text; import tango.io.Stdout; import tango.util.Convert; import tango.core.Exception; // TODO: align(1) struct Pixel32 { version(BigEndian) { ubyte A; ubyte R; ubyte G; ubyte B; } else { ubyte B; ubyte G; ubyte R; ubyte A; } } class Color2 { Pixel32 ToPixel32() { Pixel32 px; return px; } string ToSetting() { auto px = ToPixel32(); return format("{}, {}, {}", px.R, px.G, px.B); } static Color2 FromSetting(string str) { // allow "#AB00F2", "171, 0, 242" return null; } } string test = r" ; Here is a comment [Main] UndoLevels=500 # Here is another comment CaretColor=255, 0, 0 LineNumsVisible=true [RubyMode] TabSize=4 "; unittest { // test saving to the file auto settings = new Settings; settings.loadFromString(test); test = settings.saveToString(); // test reading from the file settings = new Settings; settings.loadFromString(test); assert(settings.get("UndoLevels") == "500"); assert(settings.get("TabSize", "RubyMode") == "4"); assert(getSetting!(int)(settings, "UndoLevels") == 500); assert(getSetting!(bool)(settings, "LineNumsVisible") == true); } /** * This class will read from and write to what is basically a Windows INI file. * Here is what a file would look like: * Example: * ----- * ; Here is a comment * [Main] * UndoLevels=500 * # Here is another comment * CaretColor=255, 0, 0 * LineNumsVisible=true * * [RubyMode] * TabSize=4 * ----- */ class Settings { protected: string[string][string] sections; string[] comment; void loadFromStream(InputStream stream) { auto input = new TextInput(stream); string section = MainSectionName; bool inStartComment = true; int lineNum = 0; foreach(string line; input) { lineNum++; // check for a line with just whitespace if(line.trim().length == 0) continue; // check for a comment if(line.trimLeft().startsWith(";") || line.trimLeft().startsWith("#")) { if(inStartComment) { comment.length = comment.length + 1; comment[$-1] = line.trimLeft()[1..$].dup; } continue; } inStartComment = false; // check for a section header if(line.startsWith("[")) { if(!line.endsWith("]") || line.length < 3) throw new Exception("Invalid section on line " ~ to!(string)(lineNum)); section = line[1..$-1].dup; continue; } // parse key=value line (quickly) int eqIndex; for(eqIndex = 0; eqIndex < line.length; ++eqIndex) if(line[eqIndex] == '=') break; if(eqIndex == line.length) throw new Exception("Invalid format on line " ~ to!(string)(lineNum)); string value = line[eqIndex+1..$].unescape(); sections[section][line[0..eqIndex].dup] = value; } } void saveToStream(OutputStream stream) { auto output = new TextOutput(stream); foreach(line; comment) output(';')(line).newline; foreach(string sectionName, string[string] sectionData; sections) { output('[')(sectionName)(']').newline;// TODO: newline before each section foreach(key, value; sectionData) output(key)('=')(value).newline; // TODO: escape? } } public: const string MainSectionName = "Main"; /** * Parses the specified file. */ void load(string file) { scope f = new File(file); loadFromStream(f); } void loadFromString(string str) { scope a = new Array(str); loadFromStream(a); } string saveToString() { scope a = new Array(256, 80); saveToStream(a); return cast(string)a.slice(a.readable); } /** * * Examples: * ----- * settings.get("UndoLevels"); * settings.get("TabSize", "RubyMode"); * ----- */ string get(string name, string section = MainSectionName) { auto sect = section in sections; if(sect) { auto val = name in *sect; if(val) return *val; } return ""; } /** * Examples: * ----- * settings.set("UndoLevels", "500") * settings.set("TabSize", "4", "RubyMode"); * ----- */ void set(string name, string value, string section = MainSectionName) { if(section.contains('[') || section.contains(']')) throw new IllegalArgumentException( "Section name cannot contain a bracket"); if(name.contains('=')) throw new IllegalArgumentException( "name cannot contain an equal sign"); sections[section][name] = value; } /** * Gets or sets the comment that appears at the beginning of the settings * file. This is set when a file is read in. */ string[] commentLines() { return comment; } /// ditto void commentLines(string[] lines) { comment = lines; } } /** * Examples: * ----- * getSetting!(int)(settings, "UndoLevels"); * getSetting!(int)(settings, "TabSize", "RubyMode"); * ----- */ T getSetting(T)(Settings settings, string name, string section = Settings.MainSectionName) { string str = settings.get(name, section); static if(is(T == bool)) return str == "true" || str == "yes" || str == "on"; else static if(is(T : long)) return cast(T)to!(T)(str); else return T.FromSetting(str); } /** * Examples: * ----- * setSetting!(int)(settings, "UndoLevels", 500); * setSetting!(int)(settings, "TabSize", 4, "RubyMode"); * ----- */ void setSetting(T)(Settings settings, string name, T value, string section = Settings.MainSectionName) { string str; static if(is(T == bool)) str = value ? "true" : "false"; else static if(is(T : long)) str = to!(string)(value); else str = T.ToSetting(); settings.Set(name, str, section); }