unstrict_config.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. #include "unstrict_config.h"
  2. #include <util/string/util.h>
  3. namespace {
  4. template <class T, class TStringType>
  5. T FromStringWithDefaultForEmpty(const TStringType s, const T& def) {
  6. if (s) {
  7. return FromString<T>(s);
  8. } else {
  9. return def;
  10. }
  11. }
  12. }
  13. const TYandexConfig::Section* TUnstrictConfig::GetSection(const TString& path) {
  14. TVector<TString> vPath = SplitString(path, ".");
  15. return GetSection(GetRootSection(), vPath.begin(), vPath.end());
  16. }
  17. bool TUnstrictConfig::AddSection(const TString& path) {
  18. TVector<TString> vPath = SplitString(path, ".");
  19. if (GetSection(GetRootSection(), vPath.begin(), vPath.end()))
  20. return false;
  21. GetSection(GetRootSection(), vPath.begin(), vPath.end(), true);
  22. return true;
  23. }
  24. TString TUnstrictConfig::GetValue(const TString& path) const {
  25. try {
  26. TVector<TString> vPath = SplitString(path, ".");
  27. TVector<TString>::const_iterator dir = vPath.end() - 1;
  28. const TYandexConfig::Section* section = GetSection(GetRootSection(), vPath.begin(), dir);
  29. if (!section)
  30. return "__not_found__";
  31. const Directives& directives = section->GetDirectives();
  32. TString result;
  33. if (!directives.GetValue(*dir, result))
  34. return "__not_found__";
  35. return result;
  36. } catch (const yexception& e) {
  37. ythrow yexception() << "Error while parse path " << path << ": " << e.what();
  38. }
  39. }
  40. bool TUnstrictConfig::SetValue(const TString& path, const TString& value) {
  41. try {
  42. TVector<TString> vPath = SplitString(path, ".");
  43. TVector<TString>::const_iterator dir = vPath.end() - 1;
  44. auto sections = GetSections(GetRootSection(), vPath.begin(), dir, true);
  45. bool result = false;
  46. for (auto&& section : sections) {
  47. Directives& directives = section->GetDirectives();
  48. Directives::iterator iter = directives.find(*dir);
  49. if (iter == directives.end()) {
  50. Strings.push_back(value);
  51. directives.insert(Directives::value_type(*dir, Strings.back().data()));
  52. result = true;
  53. continue;
  54. };
  55. if (!TCiString::compare(iter->second, value))
  56. continue;
  57. Strings.push_back(value);
  58. iter->second = Strings.back().data();
  59. result = true;
  60. }
  61. return result;
  62. } catch (const yexception& e) {
  63. ythrow yexception() << "Error while parse path " << path << ": " << e.what();
  64. }
  65. }
  66. bool TUnstrictConfig::Remove(const TString& path) {
  67. TVector<TString> vPath = SplitString(path, ".");
  68. TVector<TString>::const_iterator dir = vPath.end() - 1;
  69. TVector<TYandexConfig::Section*> sections = GetSections(GetRootSection(), vPath.begin(), dir, false);
  70. bool result = false;
  71. for (auto&& section : sections) {
  72. result = RemoveSection(*section, *dir) || RemoveDirective(*section, *dir);
  73. }
  74. return result;
  75. }
  76. bool TUnstrictConfig::RemoveAll(const TString& path) {
  77. TVector<TString> vPath = SplitString(path, ".");
  78. TVector<TString>::const_iterator dir = vPath.end() - 1;
  79. TVector<TYandexConfig::Section*> sections = GetSections(GetRootSection(), vPath.begin(), dir, false);
  80. bool result = false;
  81. for (auto&& section : sections) {
  82. result = RemoveAllSections(*section, *dir);
  83. }
  84. return result;
  85. }
  86. void TUnstrictConfig::ToJsonPatch(const Section& section, NJson::TJsonValue& result, const TString& preffix) {
  87. const TString& newPreffix = preffix ? preffix + "." : TString();
  88. for (const auto& i : section.GetDirectives())
  89. result.InsertValue(newPreffix + i.first, i.second);
  90. TSectionsMap children = section.GetAllChildren();
  91. for (TSectionsMap::const_iterator i = children.begin(), e = children.end(); i != e; ++i)
  92. ToJsonPatch(*i->second, result, newPreffix + i->first);
  93. }
  94. bool TUnstrictConfig::OnBeginSection(Section& sec) {
  95. sec.Cookie = new AnyDirectives;
  96. sec.Owner = true;
  97. if (!sec.Parent->Parsed())
  98. return true;
  99. ReportError(sec.Name, true, "section \'%s\' not allowed here and will be ignored", sec.Name);
  100. return true;
  101. }
  102. TUnstrictConfig::TPathUnit TUnstrictConfig::ProcessPathUnit(const TString& element) const {
  103. size_t indexBeginPos = element.find('[');
  104. if (indexBeginPos == TString::npos) {
  105. return TPathUnit(element, 0);
  106. }
  107. size_t indexEndPos = element.find(']', indexBeginPos);
  108. if (indexEndPos == TString::npos) {
  109. ythrow yexception() << "Syntax error: ] symbol expected: " << element;
  110. }
  111. const TCiString name(element.data(), indexBeginPos);
  112. const TStringBuf range(element.data() + indexBeginPos + 1, indexEndPos - indexBeginPos - 1);
  113. if (range.find(':') == range.npos) {
  114. const size_t index = FromString<size_t>(range);
  115. return TPathUnit(name, index);
  116. } else {
  117. const size_t startIndex = FromStringWithDefaultForEmpty<size_t>(range.Before(':'), 0);
  118. const size_t endIndex = FromStringWithDefaultForEmpty<size_t>(range.After(':'), Max<size_t>());
  119. if (startIndex > endIndex) {
  120. ythrow yexception() << "Incorrect range " << range;
  121. }
  122. return TPathUnit(name, startIndex, endIndex);
  123. }
  124. }
  125. const TYandexConfig::Section* TUnstrictConfig::GetSection(const TYandexConfig::Section* section, const TVector<TString>::const_iterator& begin, const TVector<TString>::const_iterator& end) const {
  126. auto sections = GetSections(section, begin, end);
  127. if (sections.empty()) {
  128. return nullptr;
  129. } else if (sections.size() == 1) {
  130. return sections.front();
  131. } else {
  132. ythrow yexception() << "more than one sections matched: " << sections.size();
  133. }
  134. }
  135. TYandexConfig::Section* TUnstrictConfig::GetSection(TYandexConfig::Section* section, const TVector<TString>::const_iterator& begin, const TVector<TString>::const_iterator& end, bool add) {
  136. auto sections = GetSections(section, begin, end, add);
  137. if (sections.empty()) {
  138. return nullptr;
  139. } else if (sections.size() == 1) {
  140. return sections.front();
  141. } else {
  142. ythrow yexception() << "more than one sections matched: " << sections.size();
  143. }
  144. }
  145. TVector<const TYandexConfig::Section*> TUnstrictConfig::GetSections(const TYandexConfig::Section* section, TPathIterator begin, TPathIterator end) const {
  146. Y_ABORT_UNLESS(section);
  147. if (begin == end)
  148. return {section};
  149. TPathUnit pu = ProcessPathUnit(*begin);
  150. TSectionsMap sections = section->GetAllChildren();
  151. std::pair<TSectionsMap::const_iterator, TSectionsMap::const_iterator> range = sections.equal_range(pu.Name);
  152. TVector<const TYandexConfig::Section*> intermediate;
  153. size_t index = 0;
  154. for (auto i = range.first; i != range.second && index <= pu.EndIndex; ++i, ++index) {
  155. intermediate.push_back(i->second);
  156. }
  157. TVector<const TYandexConfig::Section*> result;
  158. for (auto i = pu.BeginIndex; i <= pu.EndIndex && i < intermediate.size(); ++i) {
  159. auto is = GetSections(intermediate[i], begin + 1, end);
  160. result.insert(result.end(), is.begin(), is.end());
  161. }
  162. return result;
  163. }
  164. TVector<TYandexConfig::Section*> TUnstrictConfig::GetSections(TYandexConfig::Section* section, TPathIterator begin, TPathIterator end, bool add) {
  165. Y_ABORT_UNLESS(section);
  166. if (begin == end)
  167. return {section};
  168. TPathUnit pu = ProcessPathUnit(*begin);
  169. TSectionsMap sections = section->GetAllChildren();
  170. std::pair<TSectionsMap::const_iterator, TSectionsMap::const_iterator> range = sections.equal_range(pu.Name);
  171. TVector<TYandexConfig::Section*> intermediate;
  172. size_t index = 0;
  173. for (auto i = range.first; i != range.second && index <= pu.EndIndex; ++i, ++index) {
  174. intermediate.push_back(i->second);
  175. }
  176. if (add && pu.EndIndex != Max<size_t>()) {
  177. for (; index <= pu.EndIndex; ++index) {
  178. auto next = new Section();
  179. Strings.push_back(pu.Name);
  180. next->Name = Strings.back().data();
  181. next->Cookie = new AnyDirectives;
  182. next->Owner = true;
  183. next->Parent = section;
  184. TYandexConfig::AddSection(next);
  185. intermediate.push_back(next);
  186. }
  187. }
  188. TVector<TYandexConfig::Section*> result;
  189. for (auto i = pu.BeginIndex; i <= pu.EndIndex && i < intermediate.size(); ++i) {
  190. auto is = GetSections(intermediate[i], begin + 1, end, add);
  191. result.insert(result.end(), is.begin(), is.end());
  192. }
  193. return result;
  194. }
  195. bool TUnstrictConfig::RemoveSection(Section& parent, const TString& name) {
  196. TPathUnit pu = ProcessPathUnit(name);
  197. TYandexConfig::Section* prevSection = nullptr;
  198. TYandexConfig::Section* curSection = parent.Child;
  199. size_t index = 0;
  200. bool deleted = false;
  201. while (curSection) {
  202. if (pu.Name == curSection->Name) {
  203. if (index >= pu.BeginIndex && index <= pu.EndIndex) {
  204. if (prevSection)
  205. prevSection->Next = curSection->Next;
  206. if (parent.Child == curSection)
  207. parent.Child = curSection->Next;
  208. curSection = curSection->Next;
  209. deleted = true;
  210. } else {
  211. prevSection = curSection;
  212. curSection = curSection->Next;
  213. }
  214. ++index;
  215. if (index > pu.EndIndex) {
  216. break;
  217. }
  218. } else {
  219. prevSection = curSection;
  220. curSection = curSection->Next;
  221. }
  222. }
  223. return deleted;
  224. }
  225. bool TUnstrictConfig::RemoveAllSections(Section& parent, const TString& name) {
  226. TPathUnit pu = ProcessPathUnit(name);
  227. if (pu.BeginIndex) {
  228. ythrow yexception() << "incorrect path for RemoveAllSections: " << name;
  229. }
  230. ui32 deleted = 0;
  231. while (RemoveSection(parent, name)) {
  232. deleted++;
  233. }
  234. return deleted;
  235. }
  236. bool TUnstrictConfig::RemoveDirective(Section& parent, const TString& name) {
  237. Directives::iterator i = parent.GetDirectives().find(name);
  238. if (i == parent.GetDirectives().end())
  239. return false;
  240. parent.Cookie->erase(i);
  241. return true;
  242. }
  243. bool TUnstrictConfig::PatchEntry(const TString& path, const TString& value, const TString& prefix /* = "" */) {
  244. if (value == "__remove__")
  245. return Remove(prefix + path);
  246. else if (value == "__remove_all__")
  247. return RemoveAll(prefix + path);
  248. else if (value == "__add_section__")
  249. return AddSection(prefix + path);
  250. else
  251. return SetValue(prefix + path, value);
  252. }
  253. [[nodiscard]] bool TUnstrictConfig::ParseJson(const NJson::TJsonValue& json) {
  254. Y_ABORT_UNLESS(ParseMemory(""));
  255. return ParseJson(json, TString());
  256. }
  257. [[nodiscard]] bool TUnstrictConfig::ParseJson(const NJson::TJsonValue& json, const TString& path) {
  258. if (json.IsNull()) {
  259. return true;
  260. }
  261. if (!json.IsMap()) {
  262. const TString& error = (path ? path : "Root") + "section must be a Json map";
  263. ReportError(nullptr, error.data());
  264. return false;
  265. }
  266. for (auto&& e : json.GetMap()) {
  267. const TString& name = e.first;
  268. const NJson::TJsonValue& node = e.second;
  269. if (node.IsArray()) {
  270. size_t index = 0;
  271. for (auto&& element : node.GetArray()) {
  272. AddSection(path + name);
  273. if (!ParseJson(element, path + name + "[" + ::ToString(index) + "].")) {
  274. return false;
  275. }
  276. ++index;
  277. }
  278. } else if (node.IsMap()) {
  279. const TString& error = path + name + " section must be either a Json array or a Json plain value";
  280. ReportError(nullptr, error.data());
  281. return false;
  282. } else {
  283. SetValue(path + name, node.GetStringRobust());
  284. }
  285. }
  286. return true;
  287. }
  288. TString TUnstrictConfig::ToString() const {
  289. TStringStream stringStream;
  290. const Section* root = GetRootSection();
  291. if (!root->Child)
  292. ythrow yexception() << "root element of the config has no children";
  293. SectionToStream(root->Child, stringStream, 0);
  294. return stringStream.Str();
  295. }
  296. NJson::TJsonValue TUnstrictConfig::ToJson() const {
  297. return ToJson(*this);
  298. }
  299. void TUnstrictConfig::ToJson(const Section& section, NJson::TJsonValue& result) {
  300. for (const auto& i : section.GetDirectives()) {
  301. result[i.first] = i.second;
  302. }
  303. TSectionsMap children = section.GetAllChildren();
  304. for (TSectionsMap::const_iterator i = children.begin(), e = children.end(); i != e; ++i)
  305. result[i->first].AppendValue(ToJson(*i->second));
  306. }
  307. void TUnstrictConfig::ToJson(const TYandexConfig& config, NJson::TJsonValue& result) {
  308. return ToJson(*config.GetRootSection(), result);
  309. }
  310. void TUnstrictConfig::ToJson(const TString& config, NJson::TJsonValue& result) {
  311. TUnstrictConfig uc;
  312. if (!uc.ParseMemory(config.data())) {
  313. TString errors;
  314. uc.PrintErrors(errors);
  315. throw yexception() << "Cannot parse YandexConfig: " << errors;
  316. }
  317. ToJson(uc, result);
  318. }
  319. void SectionToStream(const TYandexConfig::Section* section, IOutputStream& stream, ui16 level) {
  320. TString shift;
  321. shift.reserve(level * 4);
  322. for (int i = 0; i < level; ++i)
  323. shift += " ";
  324. TString childShift = shift + " ";
  325. if (!section || !section->Parsed())
  326. return;
  327. bool hasName = section->Name && *section->Name;
  328. if (hasName) {
  329. stream << shift << "<" << section->Name;
  330. for (const auto& attr : section->Attrs) {
  331. stream << " " << attr.first << "=\"" << attr.second << "\"";
  332. }
  333. stream << ">\n";
  334. }
  335. for (const auto& iter : section->GetDirectives()) {
  336. stream << childShift << iter.first;
  337. stream << " : " << TString(iter.second) << "\n";
  338. }
  339. if (section->Child) {
  340. SectionToStream(section->Child, stream, level + 1);
  341. }
  342. if (hasName)
  343. stream << shift << "</" << section->Name << ">\n";
  344. if (section->Next) {
  345. SectionToStream(section->Next, stream, level);
  346. }
  347. }