summaryrefslogtreecommitdiffstats
path: root/src/include/str_map.h
blob: 7f354fd4696033b6c50809786b6e6094d11ebf51 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
 *
 * Author: Loic Dachary <loic@dachary.org>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 * 
 */

#ifndef CEPH_STRMAP_H
#define CEPH_STRMAP_H

#define CONST_DELIMS ",;\t\n "

#include <map>
#include <string>
#include <sstream>

template <typename Func>
void for_each_pair(std::string_view s, const char* delims, Func&& f)
{
  auto pos = s.find_first_not_of(delims);
  while (pos != s.npos) {
    s.remove_prefix(pos); // trim delims from the front
    auto end = s.find_first_of(delims);
    auto kv = s.substr(0, end);
    if (auto equal = kv.find('='); equal != kv.npos) {
      f(kv.substr(0, equal), kv.substr(equal + 1));
    } else {
      f(kv.substr(0, equal), std::string_view());
    }
    pos = s.find_first_not_of(delims, end);
  }
}

using str_map_t = std::map<std::string,std::string>;

/**
 * Parse **str** and set **str_map** with the key/value pairs read
 * from it. The format of **str** is either a well formed JSON object
 * or a custom key[=value] plain text format.
 *
 * JSON is tried first. If successfully parsed into a JSON object, it
 * is copied into **str_map** verbatim. If it is not a JSON object ( a
 * string, integer etc. ), -EINVAL is returned and **ss** is set to
 * a human readable error message.
 *
 * If **str** is no valid JSON and if **fallback_to_plain** is set to true
 * (default: true) it is assumed to be a string containing white space
 * separated key=value pairs. A white space is either space, tab or newline.
 * Function **get_str_map** will be leveraged to parse the plain-text
 * key/value pairs.
 * 
 * @param [in] str JSON or plain text key/value pairs
 * @param [out] ss human readable message on error
 * @param [out] str_map key/value pairs read from str
 * @param [in] fallback_to_plain attempt parsing as plain-text if json fails
 * @return **0** on success or a -EINVAL on error.
 */
int get_json_str_map(
    const std::string &str,
    std::ostream &ss,
    str_map_t* str_map,
    bool fallback_to_plain = true);

/**
 * Parse **str** and set **str_map** with the key/value pairs read from
 * it.  The format of **str** is a number of custom key[=value] pairs in
 * plain text format.
 *
 * The string will be parsed taking **delims** as field delimiters for
 * key/values.  The value is optional resulting in an empty string when
 * not provided.  For example, using white space as delimiters:
 *
 *     insert your own=political/ideological    statement=here 
 *
 * will be parsed into:
 *
 *     { "insert": "", 
 *       "your": "", 
 *       "own": "political/ideological",
 *       "statement": "here" }
 *
 * Alternative delimiters may be provided.  For instance, specifying
 * "white space and slash", for the above statement, would be parsed
 * into:
 *
 *     { "insert": "",
 *       "your": "",
 *       "own": "political",
 *       "ideological": "",
 *       "statement": "here" }
 *
 * See how adding '/' to the delimiters field will spawn a new key without
 * a set value.
 *
 * Always returns 0, as there is no condition for failure.
 *
 * @param [in] str plain text key/value pairs
 * @param [in] delims field delimiters to be used for parsing str
 * @param [out] str_map key/value pairs parsed from str
 * @return **0**
 */
int get_str_map(
    const std::string &str,
    str_map_t* str_map,
    const char *delims = CONST_DELIMS);

// an alternate form (as we never fail):
str_map_t get_str_map(
    const std::string& str,
    const char* delim = CONST_DELIMS);

/**
 * Returns the value of **key** in **str_map** if available.
 *
 * If **key** is not available in **str_map**, and if **def_val** is
 * not-NULL then returns **def_val**. Otherwise checks if the value of
 * **key** is an empty string and if so will return **key**.
 * If the map contains **key**, the function returns the value of **key**.
 *
 * @param[in] str_map Map to obtain **key** from
 * @param[in] key The key to search for in the map
 * @param[in] def_val The value to return in case **key** is not present
 */
std::string get_str_map_value(
    const str_map_t& str_map,
    const std::string &key,
    const std::string *def_val = nullptr);

/**
 * Returns the value of **key** in **str_map** if available.
 *
 * If **key** is available in **str_map** returns the value of **key**.
 *
 * If **key** is not available in **str_map**, and if **def_key**
 * is not-NULL and available in **str_map**, then returns the value
 * of **def_key**.
 *
 * Otherwise returns an empty string.
 *
 * @param[in] str_map Map to obtain **key** or **def_key** from
 * @param[in] key Key to obtain the value of from **str_map**
 * @param[in] def_key Key to fallback to if **key** is not present
 *                    in **str_map**
 */
std::string get_str_map_key(
    const str_map_t& str_map,
    const std::string &key,
    const std::string *fallback_key = nullptr);

// This function's only purpose is to check whether a given map has only
// ONE key with an empty value (which would mean that 'get_str_map()' read
// a map in the form of 'VALUE', without any KEY/VALUE pairs) and, in such
// event, to assign said 'VALUE' to a given 'def_key', such that we end up
// with a map of the form "m = { 'def_key' : 'VALUE' }" instead of the
// original "m = { 'VALUE' : '' }".
int get_conf_str_map_helper(
    const std::string &str,
    std::ostringstream &oss,
    str_map_t* str_map,
    const std::string &default_key);

std::string get_value_via_strmap(
  const std::string& conf_string,
  std::string_view default_key);

std::string get_value_via_strmap(
  const std::string& conf_string,
  const std::string& key,
  std::string_view default_key);

#endif