Skip to content

Commit 3fa4bc8

Browse files
committed
add formatter
1 parent 4f51a8c commit 3fa4bc8

File tree

6 files changed

+248
-25
lines changed

6 files changed

+248
-25
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <string_view>
5+
6+
#include "SevenBit/DI/LibraryConfig.hpp"
7+
8+
#include "SevenBit/DI/Details/Utils/Formatter.hpp"
9+
10+
namespace sb::di::details
11+
{
12+
class Format
13+
{
14+
public:
15+
template <class... Args> static std::string fmt(std::string_view formatString, Args &&...args)
16+
{
17+
Formatter formatter{formatString};
18+
formatter.format(args...);
19+
return std::move(formatter).getResult();
20+
}
21+
};
22+
} // namespace sb::di::details
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <string_view>
5+
6+
#include "SevenBit/DI/LibraryConfig.hpp"
7+
8+
#include "SevenBit/DI/Details/Utils/String.hpp"
9+
10+
namespace sb::di::details
11+
{
12+
class Formatter
13+
{
14+
std::string_view formatString;
15+
std::string_view::size_type current = 0;
16+
std::string result;
17+
18+
public:
19+
explicit Formatter(std::string_view formatString) : formatString(formatString)
20+
{
21+
result.reserve(formatString.size());
22+
}
23+
24+
template <class... Args> int format(Args &&...args)
25+
{
26+
auto used = (... + process(args));
27+
result += formatString.substr(current);
28+
return used;
29+
}
30+
31+
std::string getResult() && { return std::move(result); }
32+
33+
private:
34+
template <class Arg> int process(Arg &&arg)
35+
{
36+
if (auto start = formatString.find_first_of('{', current); start != std::string_view::npos)
37+
{
38+
result += formatString.substr(current, start - current);
39+
auto end = formatString.find_first_of('}', start++);
40+
if (end == std::string_view::npos)
41+
{
42+
throw std::runtime_error("Invalid format string '{' should end with '}'");
43+
}
44+
result += String::toString(std::forward<Arg>(arg), formatString.substr(start, end - start));
45+
current = end + 1;
46+
return 1;
47+
}
48+
result += formatString.substr(current);
49+
current = formatString.size();
50+
return 0;
51+
}
52+
};
53+
} // namespace sb::di::details
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#pragma once
2+
3+
#include "SevenBit/DI/LibraryConfig.hpp"
4+
5+
#include "SevenBit/DI/Details/Utils/String.hpp"
6+
7+
namespace sb::di::details
8+
{
9+
INLINE std::string String::toString(const char *arg, std::string_view fmt)
10+
{
11+
if (!fmt.empty())
12+
{
13+
auto format = makeArgFmt(fmt, "s");
14+
return dataToString(format.data(), const_cast<char *>(arg));
15+
}
16+
return std::string(arg);
17+
}
18+
19+
INLINE std::string String::toString(const std::string &arg, std::string_view fmt)
20+
{
21+
if (!fmt.empty())
22+
{
23+
auto format = makeArgFmt(fmt, "s");
24+
return dataToString(format.data(), const_cast<std::string &>(arg).data());
25+
}
26+
return arg;
27+
}
28+
29+
INLINE std::string String::toString(std::string &&arg, std::string_view fmt)
30+
{
31+
if (!fmt.empty())
32+
{
33+
auto format = makeArgFmt(fmt, "s");
34+
return dataToString(format.data(), arg.data());
35+
}
36+
return std::move(arg);
37+
}
38+
39+
INLINE std::string String::toString(std::string_view arg, std::string_view fmt)
40+
{
41+
std::string strArg{arg};
42+
if (!fmt.empty())
43+
{
44+
auto format = makeArgFmt(fmt, "s");
45+
return dataToString(format.data(), strArg.data());
46+
}
47+
return strArg;
48+
}
49+
50+
INLINE std::string String::toString(int arg, std::string_view fmt) { return toString(arg, fmt, "d"); }
51+
INLINE std::string String::toString(long arg, std::string_view fmt) { return toString(arg, fmt, "ld"); }
52+
INLINE std::string String::toString(long long arg, std::string_view fmt) { return toString(arg, fmt, "lld"); }
53+
INLINE std::string String::toString(unsigned arg, std::string_view fmt) { return toString(arg, fmt, "u"); }
54+
INLINE std::string String::toString(unsigned long arg, std::string_view fmt) { return toString(arg, fmt, "lu"); }
55+
INLINE std::string String::toString(unsigned long long arg, std::string_view fmt)
56+
{
57+
return toString(arg, fmt, "llu");
58+
}
59+
INLINE std::string String::toString(float arg, std::string_view fmt) { return toString(arg, fmt, "f"); }
60+
INLINE std::string String::toString(double arg, std::string_view fmt) { return toString(arg, fmt, "f"); }
61+
INLINE std::string String::toString(long double arg, std::string_view fmt) { return toString(arg, fmt, "Lf"); }
62+
63+
INLINE std::string String::makeArgFmt(std::string_view fmt, std::string_view type)
64+
{
65+
return "%" + std::string{fmt} + std::string{type};
66+
}
67+
} // namespace sb::di::details

Include/SevenBit/DI/Details/Utils/String.hpp

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,45 @@
77

88
namespace sb::di::details
99
{
10-
class String
10+
class EXPORT String
1111
{
1212
public:
13-
template <class... Args> static std::string join(std::string &&start, Args... strings)
13+
static std::string toString(const char *arg, std::string_view fmt = "");
14+
static std::string toString(const std::string &arg, std::string_view fmt = "");
15+
static std::string toString(std::string &&arg, std::string_view fmt = "");
16+
static std::string toString(std::string_view arg, std::string_view fmt = "");
17+
static std::string toString(int arg, std::string_view fmt = "");
18+
static std::string toString(long arg, std::string_view fmt = "");
19+
static std::string toString(long long arg, std::string_view fmt = "");
20+
static std::string toString(unsigned arg, std::string_view fmt = "");
21+
static std::string toString(unsigned long arg, std::string_view fmt = "");
22+
static std::string toString(unsigned long long arg, std::string_view fmt = "");
23+
static std::string toString(float arg, std::string_view fmt = "");
24+
static std::string toString(double arg, std::string_view fmt = "");
25+
static std::string toString(long double arg, std::string_view fmt = "");
26+
27+
private:
28+
template <class T> static std::string dataToString(const char *const fmt, T data)
1429
{
15-
return (start + ... + (std::string{" "} + strings));
30+
char buffer[200];
31+
auto size = std::snprintf(buffer, sizeof(buffer), fmt, data);
32+
return std::string(buffer, size);
1633
}
1734

18-
static std::string quote(std::string &&value) { return "'" + value + "'"; }
35+
template <class Number> static std::string toString(Number arg, std::string_view fmt, std::string_view type)
36+
{
37+
if (!fmt.empty())
38+
{
39+
auto format = makeArgFmt(fmt, type);
40+
return dataToString(format.data(), arg);
41+
}
42+
return std::to_string(arg);
43+
}
44+
45+
static std::string makeArgFmt(std::string_view fmt, std::string_view type);
1946
};
2047
} // namespace sb::di::details
48+
49+
#ifdef _7BIT_DI_ADD_IMPL
50+
#include "SevenBit/DI/Details/Utils/Impl/String.hpp"
51+
#endif

Include/SevenBit/DI/Impl/Exceptions.hpp

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
#include "SevenBit/DI/LibraryConfig.hpp"
66

7-
#include "SevenBit/DI/Details/Utils/String.hpp"
7+
#include "SevenBit/DI/Details/Utils/Format.hpp"
88
#include "SevenBit/DI/Exceptions.hpp"
99

1010
namespace sb::di
@@ -19,56 +19,50 @@ namespace sb::di
1919
}
2020

2121
INLINE InvalidServiceException::InvalidServiceException(const TypeId typeId)
22-
: InjectorException{
23-
details::String::join("Service:", details::String::quote(typeId.name()), "is in not valid state")}
22+
: InjectorException{details::Format::fmt("Service: '{}' is in not valid state.", typeId.name())}
2423
{
2524
}
2625

2726
INLINE CannotReleaseServiceException::CannotReleaseServiceException(const TypeId typeId, const std::string &reason)
28-
: InjectorException{details::String::join(
29-
"Cannot release ownership of service:", details::String::quote(typeId.name()), ", reason:", reason)}
27+
: InjectorException{
28+
details::Format::fmt("Cannot release ownership of service: '{}', reason: {}.", typeId.name(), reason)}
3029
{
3130
}
3231

3332
INLINE CannotMoveOutServiceException::CannotMoveOutServiceException(const TypeId typeId, const std::string &reason)
34-
: InjectorException{details::String::join("Cannot move out service:", details::String::quote(typeId.name()),
35-
", reason:", reason)}
33+
: InjectorException{details::Format::fmt("Cannot move out service: '{}', reason: {}.", typeId.name(), reason)}
3634
{
3735
}
3836

3937
INLINE ServiceNotFoundException::ServiceNotFoundException(const TypeId typeId, const std::string &reason)
40-
: InjectorException{details::String::join("Service:", details::String::quote(typeId.name()),
41-
"was not found, reason:", reason)}
38+
: InjectorException{details::Format::fmt("Service: '{}' was not found, reason: {}.", typeId.name(), reason)}
4239
{
4340
}
4441

4542
INLINE CircularDependencyException::CircularDependencyException(const TypeId typeId)
46-
: InjectorException{details::String::join("Circular dependency detected while creating service:",
47-
details::String::quote(typeId.name()))}
43+
: InjectorException{
44+
details::Format::fmt("Circular dependency detected while creating service: '{}'.", typeId.name())}
4845
{
4946
}
5047

5148
INLINE ServiceAlreadyRegisteredException::ServiceAlreadyRegisteredException(const TypeId typeId)
52-
: InjectorException{
53-
details::String::join("Service:", details::String::quote(typeId.name()), "was already registered")}
49+
: InjectorException{details::Format::fmt("Service: '{}' was already registered.", typeId.name())}
5450
{
5551
}
5652

5753
INLINE ServiceAliasMismatchException::ServiceAliasMismatchException(const TypeId typeId, const TypeId interface,
5854
const bool shouldBeAlias)
59-
: InjectorException{details::String::join(
60-
"Service:", details::String::quote(typeId.name()), "should", (shouldBeAlias ? "be" : "not be"),
61-
"alias as other services implementing this interface", details::String::quote(interface.name()),
62-
"that are already registered")}
55+
: InjectorException{details::Format::fmt("Service: '{}' should {} alias as other services implementing this "
56+
"interface '{}' that are already registered.",
57+
typeId.name(), (shouldBeAlias ? "be" : "not be"), interface.name())}
6358
{
6459
}
6560

6661
INLINE ServiceLifeTimeMismatchException::ServiceLifeTimeMismatchException(const TypeId typeId,
6762
const TypeId interface)
68-
: InjectorException{
69-
details::String::join("Service:", details::String::quote(typeId.name()),
70-
"should have same scope as other services implementing this interface",
71-
details::String::quote(interface.name()), "that are already registered")}
63+
: InjectorException{details::Format::fmt(
64+
"Service: '{}' should have same scope as other services implementing this interface '{}'.", typeId.name(),
65+
interface.name())}
7266
{
7367
}
7468
} // namespace sb::di

Tests/Unit/Utils/FormatTest.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#include <gtest/gtest.h>
2+
3+
#include "../../Helpers/Classes/Basic.hpp"
4+
#include <SevenBit/DI/Details/Utils/Format.hpp>
5+
#include <SevenBit/DI/Exceptions.hpp>
6+
7+
class FormatTest : public testing::Test
8+
{
9+
protected:
10+
static void TearUpTestSuite() {}
11+
12+
FormatTest() {}
13+
14+
void SetUp() override {}
15+
16+
void TearDown() override {}
17+
18+
~FormatTest() override = default;
19+
20+
static void TearDownTestSuite() {}
21+
};
22+
23+
TEST_F(FormatTest, ShouldFormatInt)
24+
{
25+
EXPECT_EQ(sb::di::details::Format::fmt("", 8), "");
26+
EXPECT_EQ(sb::di::details::Format::fmt("{}", 8u), "8");
27+
EXPECT_EQ(sb::di::details::Format::fmt("{04}", 8u), "0008");
28+
EXPECT_EQ(sb::di::details::Format::fmt("{}{}", 8, 2l), "82");
29+
EXPECT_EQ(sb::di::details::Format::fmt("{04}{}", 8, 2l), "00082");
30+
EXPECT_EQ(sb::di::details::Format::fmt("alice", 8, 2), "alice");
31+
EXPECT_EQ(sb::di::details::Format::fmt("alice {}", 8ll, 2), "alice 8");
32+
EXPECT_EQ(sb::di::details::Format::fmt("{} alice '{}'", 8, 2ull), "8 alice '2'");
33+
EXPECT_EQ(sb::di::details::Format::fmt("{} alice '{}' {} hop", 8ul, 2), "8 alice '2' {} hop");
34+
}
35+
36+
TEST_F(FormatTest, ShouldFormatFloat)
37+
{
38+
EXPECT_EQ(sb::di::details::Format::fmt("", 8.f), "");
39+
EXPECT_EQ(sb::di::details::Format::fmt("{}", 8.f), "8.000000");
40+
EXPECT_EQ(sb::di::details::Format::fmt("{}{}", 8.2, 2.2f), "8.2000002.200000");
41+
EXPECT_EQ(sb::di::details::Format::fmt("alice", 8.4, 2), "alice");
42+
EXPECT_EQ(sb::di::details::Format::fmt("alice {}", 8.0, 2.5f), "alice 8.000000");
43+
EXPECT_EQ(sb::di::details::Format::fmt("{} alice '{}'", 8.4, 2.1), "8.400000 alice '2.100000'");
44+
EXPECT_EQ(sb::di::details::Format::fmt("{} alice '{}' {} hop", 8.2, 2.6), "8.200000 alice '2.600000' {} hop");
45+
}
46+
47+
TEST_F(FormatTest, ShouldFormatString)
48+
{
49+
EXPECT_EQ(sb::di::details::Format::fmt("", ""), "");
50+
EXPECT_EQ(sb::di::details::Format::fmt("{}", "yes"), "yes");
51+
EXPECT_EQ(sb::di::details::Format::fmt("{}{}", "yes", std::string_view{"no"}), "yesno");
52+
EXPECT_EQ(sb::di::details::Format::fmt("alice", std::string("yes"), "no"), "alice");
53+
EXPECT_EQ(sb::di::details::Format::fmt("alice {}", std::string("yes"), "no"), "alice yes");
54+
EXPECT_EQ(sb::di::details::Format::fmt("{} alice '{}'", "yes", "no"), "yes alice 'no'");
55+
EXPECT_EQ(sb::di::details::Format::fmt("{} alice '{}' {} hop", std::string("yes"), "no"), "yes alice 'no' {} hop");
56+
}

0 commit comments

Comments
 (0)