import std.experimental.logger; import std.traits; import article; import page; import project; /** * Default ordering and list with pointers to ordered articles. * (Note: this is code which will actually be compiled and passed on!) */ GenericCache!(Article, "a.firstPublished > b.firstPublished") articles; GenericCache!(Page, "a.title < b.title") pages; GenericCache!(Project, "a.title < b.title") projects; /** * In memory cache of T (where T is like a page). Right now it simply holds everything in memory. * * At a later date, this cache might start unloading lesser-accessed items, and load them later * again if needed. */ struct GenericCache(T, string sortOrder) if (isImplicitlyConvertible!(T, Page)) { public: void addItem(T item) { logf("Added %s '%s'", T.stringof, item.slug); m_map[item.slug] = item; sortItems(); } void removeItem(T item) { logf("Removed %s '%s'", T.stringof, item.slug); m_map.remove(item.slug); sortItems(); } void removeItemByName(string name) { foreach(item; m_map.byValue) { if (item.name == name) removeItem(item); } } void changeItem(T item) { import std.algorithm; import std.range; auto r = m_map.byValue.find!((a,b) => a.name == b.name)(item); if (r.empty()) { warningf("Could not find old %s with name %s", T.stringof, item.name); return; } T oldItem = r.front; if (oldItem.slug != item.slug) { logf("Slug of %s '%s' changed to '%s'", T.stringof, oldItem.slug, item.slug); m_map.remove(oldItem.slug); } m_map[item.slug] = item; sortItems(); } /** * Overload []-operator to redirect to our internal map. */ T opIndex(string key) { return m_map[key]; } T* opBinaryRight(string op)(const scope string key) { static if (op == "in") { return key in m_map; } else static assert(false, "Operation " ~ op ~ "is not supported on this class"); } T[] sortedList() { return m_publicItems; } private: T[string] m_map; T[] m_publicItems; void sortItems() { import std.algorithm; import std.array; m_publicItems = sort!sortOrder(m_map.values) .filter!(x => !x.isHidden) .array; } }