add logger
This commit is contained in:
parent
f87a80b495
commit
6b124255a0
61
bin/main.cpp
61
bin/main.cpp
@ -1,30 +1,55 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "string/string.hpp"
|
#include "string/string.hpp"
|
||||||
#include "range/range.hpp"
|
#include "range/range.hpp"
|
||||||
#include "container/container.hpp"
|
#include "container/container.hpp"
|
||||||
|
#include "logger/logger.hpp"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
{// ex: split_str
|
// {// ex: split_str
|
||||||
std::string str { "asdf,qwer,zxcv" };
|
// std::string str { "asdf,qwer,zxcv" };
|
||||||
hack::string::v_str v = hack::string::split_str(str, ',');
|
// hack::string::v_str v = hack::string::split_str(str, ',');
|
||||||
for (const auto& c : v) std::cout << c << std::endl;
|
// for (const auto& c : v) std::cout << c << std::endl;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
// {// ex: within
|
||||||
|
// std::cout << std::boolalpha << hack::range::within(12, 34, 12, 23, 31, 17, 22, 33) << std::endl;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {// ex: v_multiset
|
||||||
|
// std::vector<std::string> v;
|
||||||
|
// hack::container::v_multiset(v, "asdf", "qwer", "zcv");
|
||||||
|
// for(const auto& c : v) std::cout << c << std::endl;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {// ex: s_multiset
|
||||||
|
// std::set<int> s;
|
||||||
|
// hack::container::s_multiset(s, 1, 2, 3, 3, 2, 1);
|
||||||
|
// for(const auto& c : s) std::cout << c << std::endl;
|
||||||
|
// }
|
||||||
|
|
||||||
{// ex: within
|
{// ex: log
|
||||||
std::cout << std::boolalpha << hack::range::within(12, 34, 12, 23, 31, 17, 22, 33) << std::endl;
|
hack::log()(1234, "run in main", 1234);
|
||||||
}
|
hack::warn(" # ")(1234, "run in main", 1234);
|
||||||
|
hack::error(" - ")(1234, "run in main", 1234);
|
||||||
{// ex: v_multiset
|
|
||||||
std::vector<std::string> v;
|
|
||||||
hack::container::v_multiset(v, "asdf", "qwer", "zcv");
|
|
||||||
for(const auto& c : v) std::cout << c << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
{// ex: s_multiset
|
std::string str { "hi" };
|
||||||
std::set<int> s;
|
hack::log()(str);
|
||||||
hack::container::s_multiset(s, 1, 2, 3, 3, 2, 1);
|
|
||||||
for(const auto& c : s) std::cout << c << std::endl;
|
std::vector<std::string> vs { "asdf", "qwer", "zxcv" };
|
||||||
|
hack::log()("vector", vs, 1, 2, 'a');
|
||||||
|
|
||||||
|
std::list<std::string> ls { "asdf", "qwer", "zxcv" };
|
||||||
|
hack::log()(vs, ls);
|
||||||
|
|
||||||
|
std::map<int, std::string> m { { 1, "asdf" }, { 2, "qwer" }, { 3, "zxcv" } };
|
||||||
|
hack::log()(vs, ls, m);
|
||||||
|
|
||||||
|
std::tuple<int, std::string, bool> tp { 1, "tuple test", false };
|
||||||
|
hack::log()(tp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
deps += view_dep
|
||||||
|
deps += iterators_dep
|
||||||
deps += string_dep
|
deps += string_dep
|
||||||
deps += range_dep
|
deps += range_dep
|
||||||
deps += range_dep
|
deps += container_dep
|
||||||
|
deps += logger_dep
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
'hack', 'main.cpp',
|
'hack', 'main.cpp',
|
||||||
|
@ -3,7 +3,7 @@ project(
|
|||||||
'hack',
|
'hack',
|
||||||
'cpp',
|
'cpp',
|
||||||
version : '0.0.1',
|
version : '0.0.1',
|
||||||
default_options : ['cpp_std=c++20']
|
default_options : ['cpp_std=c++2a']
|
||||||
)
|
)
|
||||||
|
|
||||||
add_project_arguments (
|
add_project_arguments (
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
namespace hack::container
|
namespace hack::container
|
||||||
{
|
{
|
||||||
template<typename Range, typename... Args>
|
template<typename Range, typename... Args>
|
||||||
void v_multiset(Range& r, Args... args)
|
void vector_multiset(Range& r, Args... args)
|
||||||
{
|
{
|
||||||
constexpr std::size_t t = sizeof... (args);
|
constexpr std::size_t t = sizeof... (args);
|
||||||
r.reserve(t);
|
r.reserve(t);
|
||||||
@ -14,7 +14,7 @@ namespace hack::container
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename Range, typename... Args>
|
template<typename Range, typename... Args>
|
||||||
void s_multiset(Range& r, Args... args)
|
void set_multiset(Range& r, Args... args)
|
||||||
{
|
{
|
||||||
(r.insert(args), ...);
|
(r.insert(args), ...);
|
||||||
}
|
}
|
||||||
|
39
src/iterators/associative_ostream_iterator.hpp
Normal file
39
src/iterators/associative_ostream_iterator.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace hack::iterators
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
class associative_ostream_iterator : public std::iterator<std::output_iterator_tag, void, void, void, void>
|
||||||
|
{
|
||||||
|
using traits = std::char_traits<char>;
|
||||||
|
using ostream_type = std::basic_ostream<char, traits>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::basic_ostream<char, traits>* os;
|
||||||
|
const std::string devider = ", ";
|
||||||
|
std::size_t size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
associative_ostream_iterator(std::size_t size, ostream_type& s) : os { &s }, size { size } { }
|
||||||
|
|
||||||
|
auto& operator=(T const& item)
|
||||||
|
{
|
||||||
|
--size;
|
||||||
|
const auto& [key, value] = item;
|
||||||
|
*os << "{ " << key << ":" << value << " }" << (size != 0 ? devider : "");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& operator*()
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& operator++()
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
14
src/iterators/meson.build
Normal file
14
src/iterators/meson.build
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
headers = ['sequence_ostream_iterator.hpp', 'associative_ostream_iterator.hpp']
|
||||||
|
sources = []
|
||||||
|
|
||||||
|
lib = library(
|
||||||
|
'iterators',
|
||||||
|
include_directories : inc,
|
||||||
|
install : true,
|
||||||
|
sources: [headers, sources]
|
||||||
|
)
|
||||||
|
|
||||||
|
iterators_dep = declare_dependency(
|
||||||
|
include_directories: inc,
|
||||||
|
link_with: lib
|
||||||
|
)
|
38
src/iterators/sequence_ostream_iterator.hpp
Normal file
38
src/iterators/sequence_ostream_iterator.hpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace hack::iterators
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
class sequence_ostream_iterator : public std::iterator<std::output_iterator_tag, void, void, void, void>
|
||||||
|
{
|
||||||
|
using traits = std::char_traits<char>;
|
||||||
|
using ostream_type = std::basic_ostream<char, traits>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::basic_ostream<char, traits>* os;
|
||||||
|
const std::string devider = ", ";
|
||||||
|
std::size_t size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
sequence_ostream_iterator(std::size_t size, ostream_type& s) : os { &s }, size { size } { }
|
||||||
|
|
||||||
|
auto& operator=(T const& item)
|
||||||
|
{
|
||||||
|
--size;
|
||||||
|
*os << item << (size != 0 ? devider : "");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& operator*()
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& operator++()
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
24
src/logger/logger.cpp
Normal file
24
src/logger/logger.cpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
|
namespace hack
|
||||||
|
{
|
||||||
|
std::string log::devider = " ";
|
||||||
|
int log::count = 0;
|
||||||
|
|
||||||
|
log::log(const std::string devider, std::experimental::source_location location) : location { location }
|
||||||
|
{
|
||||||
|
this->devider = devider;
|
||||||
|
}
|
||||||
|
|
||||||
|
warn::warn(const std::string devider, std::experimental::source_location location) : location { location }
|
||||||
|
{
|
||||||
|
this->devider = devider;
|
||||||
|
}
|
||||||
|
|
||||||
|
error::error(const std::string devider, std::experimental::source_location location) : location { location }
|
||||||
|
{
|
||||||
|
this->devider = devider;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log::print() { std::cout << std::endl; }
|
||||||
|
}
|
179
src/logger/logger.hpp
Normal file
179
src/logger/logger.hpp
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <experimental/source_location>
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "view/color.hpp"
|
||||||
|
#include "iterators/sequence_ostream_iterator.hpp"
|
||||||
|
#include "iterators/associative_ostream_iterator.hpp"
|
||||||
|
|
||||||
|
namespace hack
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
concept is_sequence_container = std::same_as<T, std::vector<typename T::value_type>> ||
|
||||||
|
std::same_as<T, std::list<typename T::value_type>>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept is_tuple = requires (T t)
|
||||||
|
{
|
||||||
|
std::tuple_cat(t, std::make_tuple(1, "tuple"));
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
concept is_string = std::is_convertible_v<T, std::string_view>;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
concept is_map = std::same_as<T, std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>> ||
|
||||||
|
std::same_as<T, std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::hasher, typename T::key_equal, typename T::allocator_type>>;
|
||||||
|
|
||||||
|
class log
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
log(const std::string devider = ", ", std::experimental::source_location location = std::experimental::source_location::current());
|
||||||
|
log(log&) = delete;
|
||||||
|
log(log&&) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename... Args>
|
||||||
|
void operator() (const Args&... args)
|
||||||
|
{
|
||||||
|
std::cout << make_type_view
|
||||||
|
<< location.file_name() << ":" << view::color::reset
|
||||||
|
<< view::color::italic << view::color::yellow << location.function_name() << "()" << view::color::reset
|
||||||
|
<< view::color::bold << view::color::blue << "[" << location.line() << "]" << view::color::reset << ": ";
|
||||||
|
count = sizeof...(Args);
|
||||||
|
print(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::experimental::source_location location;
|
||||||
|
static int count;
|
||||||
|
static std::string devider;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void print();
|
||||||
|
|
||||||
|
static std::ostream& make_type_view(std::ostream &os)
|
||||||
|
{
|
||||||
|
os << view::color::bold << view::color::green << "[ok]" << view::color::reset << view::color::green;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
static void print(const T& data, const Args&... args)
|
||||||
|
{
|
||||||
|
count--;
|
||||||
|
print_t(data);
|
||||||
|
print(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<is_string T>
|
||||||
|
static void print_t(const T& data)
|
||||||
|
{
|
||||||
|
std::cout << data << (count != 0 ? devider : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::integral T>
|
||||||
|
static void print_t(const T& data)
|
||||||
|
{
|
||||||
|
std::cout << data << (count != 0 ? devider : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<is_sequence_container T>
|
||||||
|
static void print_t(const T& data)
|
||||||
|
{
|
||||||
|
std::cout << "{ ";
|
||||||
|
std::copy(data.cbegin(), data.cend(), iterators::sequence_ostream_iterator<typename T::value_type>(data.size(), std::cout));
|
||||||
|
std::cout << " }" << (count != 0 ? devider : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<is_map T>
|
||||||
|
static void print_t(const T& data)
|
||||||
|
{
|
||||||
|
std::cout << "{";
|
||||||
|
std::copy(data.cbegin(), data.cend(), iterators::associative_ostream_iterator<typename T::value_type>(data.size(), std::cout));
|
||||||
|
std::cout << "}" << (count != 0 ? devider : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<is_tuple T, typename std::size_t... idx>
|
||||||
|
static void print_t(const T& data)
|
||||||
|
{
|
||||||
|
print_t(data, std::make_index_sequence<std::tuple_size<T>::value>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename std::size_t... idx>
|
||||||
|
static void print_t(const T& data, std::index_sequence<idx...>)
|
||||||
|
{
|
||||||
|
std::cout << "{ ";
|
||||||
|
((std::cout << std::get<idx>(data) << (idx != std::tuple_size<T>::value - 1 ? devider : "")), ...);
|
||||||
|
std::cout << " }" << (count != 0 ? devider : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class warn;
|
||||||
|
friend class error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class warn : public log
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
warn(const std::string devider = ", ", std::experimental::source_location location = std::experimental::source_location::current());
|
||||||
|
warn(log&) = delete;
|
||||||
|
warn(log&&) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename... Args>
|
||||||
|
void operator() (const Args&... args)
|
||||||
|
{
|
||||||
|
std::cout << make_type_view
|
||||||
|
<< location.file_name() << ":" << view::color::reset
|
||||||
|
<< view::color::italic << view::color::yellow << location.function_name() << "()" << view::color::reset
|
||||||
|
<< view::color::bold << view::color::blue << "[" << location.line() << "]" << view::color::reset << ": ";
|
||||||
|
count = sizeof...(Args);
|
||||||
|
print(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::experimental::source_location location;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::ostream& make_type_view(std::ostream &os)
|
||||||
|
{
|
||||||
|
os << view::color::bold << view::color::yellow << "[WARN]" << view::color::reset << view::color::yellow;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class error : public log
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
error(const std::string devider = ", ", std::experimental::source_location location = std::experimental::source_location::current());
|
||||||
|
error(log&) = delete;
|
||||||
|
error(log&&) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename... Args>
|
||||||
|
void operator() (const Args&... args)
|
||||||
|
{
|
||||||
|
std::cout << make_type_view
|
||||||
|
<< location.file_name() << ":" << view::color::reset
|
||||||
|
<< view::color::italic << view::color::yellow << location.function_name() << "()" << view::color::reset
|
||||||
|
<< view::color::bold << view::color::blue << "[" << location.line() << "]" << view::color::reset << ": ";
|
||||||
|
count = sizeof...(Args);
|
||||||
|
print(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::experimental::source_location location;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::ostream& make_type_view(std::ostream &os)
|
||||||
|
{
|
||||||
|
os << view::color::bold << view::color::red << "[ERROR]" << view::color::reset << view::color::red;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
14
src/logger/meson.build
Normal file
14
src/logger/meson.build
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
headers = ['logger.hpp']
|
||||||
|
sources = ['logger.cpp']
|
||||||
|
|
||||||
|
lib = library(
|
||||||
|
'logger',
|
||||||
|
include_directories : inc,
|
||||||
|
install : true,
|
||||||
|
sources: [headers, sources]
|
||||||
|
)
|
||||||
|
|
||||||
|
logger_dep = declare_dependency(
|
||||||
|
include_directories: inc,
|
||||||
|
link_with: lib
|
||||||
|
)
|
@ -1,5 +1,8 @@
|
|||||||
inc += include_directories('.')
|
inc += include_directories('.')
|
||||||
|
|
||||||
|
subdir('view')
|
||||||
|
subdir('iterators')
|
||||||
subdir('string')
|
subdir('string')
|
||||||
subdir('range')
|
subdir('range')
|
||||||
subdir('container')
|
subdir('container')
|
||||||
|
subdir('logger')
|
||||||
|
72
src/view/color.hpp
Normal file
72
src/view/color.hpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace hack::view::color
|
||||||
|
{
|
||||||
|
template<typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& reset(std::basic_ostream<CharT, Traits> &os)
|
||||||
|
{
|
||||||
|
return os << "\033[0m";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& bold(std::basic_ostream<CharT, Traits> &os)
|
||||||
|
{
|
||||||
|
return os << "\033[1m";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& italic(std::basic_ostream<CharT, Traits> &os)
|
||||||
|
{
|
||||||
|
return os << "\033[3m";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& black(std::basic_ostream<CharT, Traits> &os)
|
||||||
|
{
|
||||||
|
return os << "\033[30m";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& red(std::basic_ostream<CharT, Traits> &os)
|
||||||
|
{
|
||||||
|
return os << "\033[31m";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& green(std::basic_ostream<CharT, Traits> &os)
|
||||||
|
{
|
||||||
|
return os << "\033[32m";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& yellow(std::basic_ostream<CharT, Traits> &os)
|
||||||
|
{
|
||||||
|
return os << "\033[33m";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& blue(std::basic_ostream<CharT, Traits> &os)
|
||||||
|
{
|
||||||
|
return os << "\033[34m";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& magenta(std::basic_ostream<CharT, Traits> &os)
|
||||||
|
{
|
||||||
|
return os << "\033[35m";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& cyan(std::basic_ostream<CharT, Traits> &os)
|
||||||
|
{
|
||||||
|
return os << "\033[36m";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& white(std::basic_ostream<CharT, Traits> &os)
|
||||||
|
{
|
||||||
|
return os << "\033[37m";
|
||||||
|
}
|
||||||
|
}
|
14
src/view/meson.build
Normal file
14
src/view/meson.build
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
headers = ['color.hpp']
|
||||||
|
sources = []
|
||||||
|
|
||||||
|
lib = library(
|
||||||
|
'view',
|
||||||
|
include_directories : inc,
|
||||||
|
install : true,
|
||||||
|
sources: [headers, sources]
|
||||||
|
)
|
||||||
|
|
||||||
|
view_dep = declare_dependency(
|
||||||
|
include_directories: inc,
|
||||||
|
link_with: lib
|
||||||
|
)
|
@ -5,6 +5,6 @@
|
|||||||
TEST(v_multiset, check)
|
TEST(v_multiset, check)
|
||||||
{
|
{
|
||||||
std::vector<int> v;
|
std::vector<int> v;
|
||||||
hack::container::v_multiset(v, 1, 2, 3);
|
hack::container::vector_multiset(v, 1, 2, 3);
|
||||||
ASSERT_EQ(v.at(0), 1);
|
ASSERT_EQ(v.at(0), 1);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user