11/*
2- * cppIni - C++20/23 library for dealing with settings files
2+ * cppIni - A C++20 library for reading and writing INI files
33 * Copyright (C) 2023 Nils Hofmann <nils.friedchen@googlemail.com>
44 *
55 * This program is free software: you can redistribute it and/or modify
1818
1919#include < cppIni/File.h>
2020
21+ #include < algorithm>
2122#include < fstream>
2223#include < stdexcept>
2324
@@ -28,6 +29,15 @@ File::File(std::string_view filename)
2829 open ();
2930}
3031
32+ File::~File ()
33+ {
34+ for (auto & section : m_sections) {
35+ delete section;
36+ }
37+
38+ m_sections.clear ();
39+ }
40+
3141// / \param filename The filename of the file to open.
3242File File::open (std::string_view filename)
3343{
@@ -44,17 +54,53 @@ void File::open()
4454 parse ();
4555}
4656
57+ void File::flush ()
58+ {
59+ std::ofstream file{m_filename};
60+
61+ for (const auto & section: m_sections) {
62+ file << std::format (" [{}]\n " , section->fqTitle ());
63+
64+ for (const auto & [_, entry]: section->entries ()) {
65+ file << std::format (" {}={}\n " , entry.key (), entry.data ());
66+ }
67+
68+ file << " \n " ;
69+ }
70+ }
71+
72+ // / \details Scans the vector of Sections for the parent and creates the tree including the new Section if it does not exist.
73+ // / \param fqTitle The fully qualified title of the Section to create.
74+ // / \returns A pointer to the created Section.
75+ auto File::getSection (std::string_view fqTitle) -> Section*
76+ {
77+ if (const auto section = findSection (fqTitle); section) {
78+ return const_cast <Section*>(section);
79+ }
80+
81+ if (fqTitle.find (' .' ) == std::string_view::npos) {
82+ m_sections.emplace_back (new Section (fqTitle));
83+ return m_sections.back ();
84+ } else {
85+ const auto parent = getSection (fqTitle.substr (0 , fqTitle.find_last_of (' .' )));
86+ m_sections.emplace_back (new Section (fqTitle.substr (fqTitle.find_last_of (' .' ) + 1 ), parent));
87+ return m_sections.back ();
88+ }
89+ }
90+
4791// / \param title The title of the Section to find.
4892// / \returns A pointer to the Section if found, nullptr otherwise.
4993auto File::findSection (std::string_view title) const -> const Section*
5094{
51- for (const auto & section : m_sections) {
52- if (section.fqTitle () == title) {
53- return §ion;
54- }
95+ const auto section = std::find_if (std::cbegin (m_sections), std::cend (m_sections), [&title](const auto & section) {
96+ return section->fqTitle () == title;
97+ });
98+
99+ if (section == std::cend (m_sections)) {
100+ return nullptr ;
55101 }
56102
57- return nullptr ;
103+ return *section ;
58104}
59105
60106// / \param name The name of the Entry to find.
@@ -75,6 +121,13 @@ auto File::findEntry(std::string_view name) const -> const Entry*
75121 return nullptr ;
76122}
77123
124+ auto File::operator ==(const File& other) const -> bool
125+ {
126+ return std::equal (std::cbegin (m_sections), std::cend (m_sections), std::cbegin (other.m_sections ), std::cend (other.m_sections ), [](const auto & lhs, const auto & rhs) {
127+ return *lhs == *rhs;
128+ });
129+ }
130+
78131// / \details This function is called by the constructor. It should not be called directly.
79132// / \throws std::runtime_error if the file cannot be opened.
80133// / \see File::open for the public function.
@@ -83,20 +136,29 @@ auto File::parse() -> void
83136 auto content = std::ifstream{m_filename};
84137
85138 for (std::string line; std::getline (content, line);) {
86- const std::string_view lineView{line};
139+ std::string_view lineView{line};
87140
88141 if (lineView.empty ()) {
89142 continue ;
90143 }
91144
92- if (line[0 ] == ' [' ) {
93- if (line.find (' .' ) != std::string::npos) {
94- m_sections.emplace_back (lineView.substr (lineView.find_last_of (' .' ) + 1 , lineView.find_last_of (' ]' ) - 1 ), &m_sections.back ());
95- } else {
96- m_sections.emplace_back (lineView.substr (1 , lineView.find_last_of (' ]' ) - 1 ));
145+ if (lineView[0 ] == ' [' ) {
146+ lineView = lineView.substr (1 );
147+
148+ Section* parent = lineView.at (0 ) == ' .' ? m_sections.back () : nullptr ;
149+
150+ if (lineView[0 ] == ' .' ) {
151+ lineView = lineView.substr (1 );
152+ } else if (const auto section = std::find_if (std::begin (m_sections), std::end (m_sections), [&lineView](const auto & section) {
153+ return section->fqTitle () == lineView.substr (0 , lineView.find_last_of (' .' ));
154+ }); section != std::end (m_sections)) {
155+ parent = *section;
156+ lineView = lineView.substr (lineView.find_last_of (' .' ) + 1 );
97157 }
158+
159+ m_sections.emplace_back (new Section (lineView.substr (0 , lineView.find (' ]' )), parent));
98160 } else {
99- m_sections.back (). createEntry (lineView.substr (0 , lineView.find (' =' )), lineView.substr (lineView.find (' =' ) + 1 ));
161+ m_sections.back ()-> createEntry (lineView.substr (0 , lineView.find (' =' )), lineView.substr (lineView.find (' =' ) + 1 ));
100162 }
101163 }
102164}
0 commit comments