common: implement get_str_map to parse key/values

It is capable of parsing json or key=value pairs. The prototype is made
to look like get_str_list. The implementation is in common + include and
use .h. It will probably be moved to common and use .hpp instead, along
with str_list.{cc,h}.

Signed-off-by: Loic Dachary <loic@dachary.org>
This commit is contained in:
Loic Dachary 2013-12-21 13:58:44 +01:00
parent df1704eeb0
commit a44a57a7c3
6 changed files with 204 additions and 0 deletions

View File

@ -29,6 +29,7 @@ libcommon_la_SOURCES = \
common/safe_io.c \
common/snap_types.cc \
common/str_list.cc \
common/str_map.cc \
common/errno.cc \
common/RefCountedObj.cc \
common/blkdev.cc \

68
src/common/str_map.cc Normal file
View File

@ -0,0 +1,68 @@
// -*- 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.
*
*/
#include <errno.h>
#include "include/str_map.h"
#include "include/str_list.h"
#include "json_spirit/json_spirit.h"
using namespace std;
int get_str_map(const string &str,
stringstream &ss,
map<string,string> *str_map)
{
json_spirit::mValue json;
try {
// try json parsing first
json_spirit::read_or_throw(str, json);
if (json.type() != json_spirit::obj_type) {
ss << str << " must be a JSON object but is of type "
<< json.type() << " instead";
return -EINVAL;
}
json_spirit::mObject o = json.get_obj();
for (map<string, json_spirit::mValue>::iterator i = o.begin();
i != o.end();
++i) {
(*str_map)[i->first] = i->second.get_str();
}
} catch (json_spirit::Error_position &e) {
// fallback to key=value format
list<string> pairs;
get_str_list(str, "\t\n ", pairs);
for (list<string>::iterator i = pairs.begin(); i != pairs.end(); i++) {
size_t equal = i->find('=');
if (equal == string::npos)
(*str_map)[*i] = string();
else {
const string key = i->substr(0, equal);
equal++;
const string value = i->substr(equal);
(*str_map)[key] = value;
}
}
}
return 0;
}

View File

@ -59,6 +59,7 @@ noinst_HEADERS += \
include/rbd_types.h \
include/statlite.h \
include/str_list.h \
include/str_map.h \
include/stringify.h \
include/triple.h \
include/types.h \

59
src/include/str_map.h Normal file
View File

@ -0,0 +1,59 @@
// -*- 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
#include <map>
#include <string>
#include <sstream>
/**
* 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, it is assumed to be a string
* containing white space separated key=value pairs. A white space is
* either space, tab or newline. The value is optional, in which case
* it defaults to an empty string. For example:
*
* insert your own=political statement=here
*
* will be parsed into:
*
* { "insert": "",
* "your": "",
* "own": "policital",
* "statement": "here" }
*
* Returns 0 on success.
*
* @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
* @return **0** on success or a -EINVAL on error.
*/
extern int get_str_map(const std::string &str,
std::stringstream &ss,
std::map<std::string,std::string> *str_map);
#endif

View File

@ -268,6 +268,11 @@ unittest_bloom_filter_CXXFLAGS = $(UNITTEST_CXXFLAGS)
unittest_bloom_filter_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL)
check_PROGRAMS += unittest_bloom_filter
unittest_str_map_SOURCES = test/common/test_str_map.cc
unittest_str_map_CXXFLAGS = $(UNITTEST_CXXFLAGS)
unittest_str_map_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL)
check_PROGRAMS += unittest_str_map
unittest_sharedptr_registry_SOURCES = test/common/test_sharedptr_registry.cc
unittest_sharedptr_registry_CXXFLAGS = $(UNITTEST_CXXFLAGS)
unittest_sharedptr_registry_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL)

View File

@ -0,0 +1,70 @@
// -*- 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.
*
*/
#include <errno.h>
#include <gtest/gtest.h>
#include "include/str_map.h"
using namespace std;
TEST(str_map, json) {
map<string,string> str_map;
stringstream ss;
// well formatted
ASSERT_EQ(0, get_str_map("{\"key\": \"value\"}", ss, &str_map));
ASSERT_EQ("value", str_map["key"]);
// well formatted but not a JSON object
ASSERT_EQ(-EINVAL, get_str_map("\"key\"", ss, &str_map));
ASSERT_NE(string::npos, ss.str().find("must be a JSON object"));
}
TEST(str_map, plaintext) {
stringstream ss;
{
map<string,string> str_map;
ASSERT_EQ(0, get_str_map(" foo=bar\t\nfrob=nitz yeah right= \n\t",
ss, &str_map));
ASSERT_EQ(4u, str_map.size());
ASSERT_EQ("bar", str_map["foo"]);
ASSERT_EQ("nitz", str_map["frob"]);
ASSERT_EQ("", str_map["yeah"]);
ASSERT_EQ("", str_map["right"]);
}
{
map<string,string> str_map;
ASSERT_EQ(0, get_str_map("that", ss, &str_map));
ASSERT_EQ(1u, str_map.size());
ASSERT_EQ("", str_map["that"]);
}
{
map<string,string> str_map;
ASSERT_EQ(0, get_str_map(" \t \n ", ss, &str_map));
ASSERT_EQ(0u, str_map.size());
ASSERT_EQ(0, get_str_map("", ss, &str_map));
ASSERT_EQ(0u, str_map.size());
}
}
/*
* Local Variables:
* compile-command: "cd ../.. ; make -j4 &&
* make unittest_str_map &&
* valgrind --tool=memcheck --leak-check=full \
* ./unittest_str_map
* "
* End:
*/