diff --git a/src/skia/String.cpp b/src/skia/String.cpp new file mode 100644 index 000000000..1d4d5e231 --- /dev/null +++ b/src/skia/String.cpp @@ -0,0 +1,177 @@ +#include "common.h" + +namespace { + +py::str StringToPyStr(const SkString& string) { + return py::str(string.c_str(), string.size()); +} + +} // namespace + + +void initString(py::module &m) { + +py::class_(m, "String") + .def("__repr__", &StringToPyStr) + .def("__str__", &StringToPyStr) + .def(py::init<>()) + .def(py::init(), py::arg("size")) + .def(py::init( + [] (py::str str) { + ssize_t length = 0; + auto buffer = PyUnicode_AsUTF8AndSize(str.ptr(), &length); + if (!buffer) + throw py::value_error("Failed to get UTF-8 str."); + return SkString(buffer, length); + }), + py::arg("text")) + .def(py::init(), py::arg("text")) + .def(py::init(), py::arg("text"), py::arg("size")) + .def(py::init(), py::arg("other")) + .def("isEmpty", &SkString::isEmpty) + .def("size", &SkString::size) + .def("c_str", &SkString::c_str) + .def("__getitem__", + [] (const SkString& self, size_t index) { + if (index < 0 || self.size() <= index) + throw std::out_of_range("Invalid index."); + return self[index]; + }) + .def("__setitem__", + [] (SkString& self, size_t index, char value) { + if (index < 0 || self.size() <= index) + throw std::out_of_range("Invalid index."); + self[index] = value; + return self; + }) + .def("__len__", &SkString::size) + .def("equals", + py::overload_cast(&SkString::equals, py::const_), + py::arg("text")) + .def("equals", + py::overload_cast(&SkString::equals, py::const_), + py::arg("text")) + .def("equals", + py::overload_cast(&SkString::equals, py::const_), + py::arg("text"), py::arg("size")) + .def("__eq__", + py::overload_cast(&SkString::equals, py::const_), + py::arg("text"), py::is_operator()) + .def("startsWith", + py::overload_cast(&SkString::startsWith, py::const_), + py::arg("prefixStr")) + .def("startsWith", + py::overload_cast(&SkString::startsWith, py::const_), + py::arg("prefixChar")) + .def("endsWith", + py::overload_cast(&SkString::endsWith, py::const_), + py::arg("suffixStr")) + .def("endsWith", + py::overload_cast(&SkString::endsWith, py::const_), + py::arg("suffixChar")) + .def("contains", + py::overload_cast(&SkString::contains, py::const_), + py::arg("subStr")) + .def("contains", + py::overload_cast(&SkString::contains, py::const_), + py::arg("subChar")) + .def("__contains__", + py::overload_cast(&SkString::contains, py::const_), + py::arg("subStr")) + .def("find", &SkString::find, py::arg("substring")) + .def("findLastOf", &SkString::findLastOf, py::arg("subchar")) + // .def("writable_str", &SkString::writable_str) + .def("reset", &SkString::reset) + .def("resize", &SkString::resize) + .def("set", py::overload_cast(&SkString::set), + py::arg("src")) + .def("set", py::overload_cast(&SkString::set), + py::arg("text")) + .def("set", py::overload_cast(&SkString::set), + py::arg("text"), py::arg("size")) + .def("insert", + py::overload_cast(&SkString::insert), + py::arg("offset"), py::arg("src")) + .def("insert", py::overload_cast(&SkString::insert), + py::arg("offset"), py::arg("text")) + .def("insert", + py::overload_cast(&SkString::insert), + py::arg("offset"), py::arg("text"), py::arg("size")) + .def("insertUnichar", &SkString::insertUnichar, + py::arg("offset"), py::arg("value")) + .def("insertS32", &SkString::insertS32, + py::arg("offset"), py::arg("value")) + .def("insertS64", &SkString::insertS64, + py::arg("offset"), py::arg("value"), py::arg("minDigits") = 0) + .def("insertU32", &SkString::insertU32, + py::arg("offset"), py::arg("value")) + .def("insertU64", &SkString::insertU64, + py::arg("offset"), py::arg("value"), py::arg("minDigits") = 0) + .def("insertHex", &SkString::insertHex, + py::arg("offset"), py::arg("value"), py::arg("minDigits") = 0) + .def("insertScalar", &SkString::insertScalar, + py::arg("offset"), py::arg("value")) + .def("append", + py::overload_cast(&SkString::append), + py::arg("str")) + .def("append", + py::overload_cast(&SkString::append), + py::arg("text")) + .def("append", + py::overload_cast(&SkString::append), + py::arg("text"), py::arg("size")) + .def("appendUnichar", &SkString::appendUnichar, + py::arg("value")) + .def("appendS32", &SkString::appendS32, + py::arg("value")) + .def("appendS64", &SkString::appendS64, + py::arg("value"), py::arg("minDigits") = 0) + .def("appendU32", &SkString::appendU32, + py::arg("value")) + .def("appendU64", &SkString::appendU64, + py::arg("value"), py::arg("minDigits") = 0) + .def("appendHex", &SkString::appendHex, + py::arg("value"), py::arg("minDigits") = 0) + .def("appendScalar", &SkString::appendScalar, + py::arg("value")) + .def("prepend", + py::overload_cast(&SkString::prepend), + py::arg("str")) + .def("prepend", + py::overload_cast(&SkString::prepend), + py::arg("text")) + .def("prepend", + py::overload_cast(&SkString::prepend), + py::arg("text"), py::arg("size")) + .def("prependUnichar", &SkString::prependUnichar, + py::arg("value")) + .def("prependS32", &SkString::prependS32, + py::arg("value")) + .def("prependS64", &SkString::prependS64, + py::arg("value"), py::arg("minDigits") = 0) + .def("prependHex", &SkString::prependHex, + py::arg("value"), py::arg("minDigits") = 0) + .def("prependScalar", &SkString::prependScalar, + py::arg("value")) + .def("remove", &SkString::remove, py::arg("offset"), py::arg("size")) + .def("__iadd__", + [] (SkString& s, const SkString& other) { return s += other; }, + py::arg("other"), py::is_operator()) + .def("__iadd__", + [] (SkString& s, const char* other) { return s += other; }, + py::arg("other"), py::is_operator()) + .def("__iadd__", + [] (SkString& s, const char other) { return s += other; }, + py::arg("other"), py::is_operator()) + .def("swap", &SkString::swap, + R"docstring( + Swap contents between this and other. + + This function is guaranteed to never fail or throw. + )docstring", + py::arg("other")) + ; + +py::implicitly_convertible(); + +} diff --git a/tests/test_string.py b/tests/test_string.py new file mode 100644 index 000000000..31b93b4d2 --- /dev/null +++ b/tests/test_string.py @@ -0,0 +1,242 @@ +import skia +import pytest + + +@pytest.fixture +def string(): + return skia.String('foo') + + +def test_String_str(string): + assert isinstance(str(string), str) + + +def test_String_repr(string): + assert isinstance(repr(string), str) + + +@pytest.mark.parametrize('args', [ + tuple(), + (4,), + ('foo',), + ('foo', 3), + (skia.String('foo'),), +]) +def test_String_init(args): + assert isinstance(skia.String(*args), skia.String) + + +def test_String_isEmpty(string): + assert not string.isEmpty() + + +def test_String_size(string): + assert string.size() == 3 + + +def test_String_c_str(string): + assert string.c_str() == 'foo' + + +def test_String_getitem(string): + assert string[0] == 'f' + + +def test_String_setitem(): + string = skia.String(1) + string[0] = 'f' + assert string.c_str() == 'f' + + +def test_String_len(string): + assert len(string) == 3 + + +def test_String_iter(string): + assert isinstance(list(string), list) + + +@pytest.mark.parametrize('args', [ + (skia.String('foo'),), + ('foo',), + ('foo', 3), +]) +def test_String_equals(string, args): + assert isinstance(string.equals(*args), bool) + + +def test_String___eq__(string): + assert string == string + + +@pytest.mark.parametrize('args', [ + ('foo',), + ('f',), +]) +def test_String_startsWith(string, args): + assert isinstance(string.startsWith(*args), bool) + + +@pytest.mark.parametrize('args', [ + ('foo',), + ('f',), +]) +def test_String_endsWith(string, args): + assert isinstance(string.endsWith(*args), bool) + + +@pytest.mark.parametrize('args', [ + ('foo',), + ('f',), +]) +def test_String_contains(string, args): + assert isinstance(string.contains(*args), bool) + + +def test_String___contains__(string): + assert 'foo' in string + + +def test_String_find(string): + assert isinstance(string.find('oo'), int) + + +def test_String_findLastOf(string): + assert isinstance(string.findLastOf('o'), int) + + +def test_String_reset(string): + string.reset() + + +def test_String_resize(string): + string.resize(3) + + +@pytest.mark.parametrize('args', [ + (skia.String('foo'),), + ('foo',), + ('foo', 3), +]) +def test_String_set(string, args): + string.set(*args) + + +@pytest.mark.parametrize('args', [ + (0, skia.String('foo'),), + (0, 'foo',), + (0, 'foo', 3), +]) +def test_String_insert(string, args): + string.insert(*args) + + +def test_String_insertUnichar(string): + string.insertUnichar(0, ord('x')) + + +def test_String_insertS32(string): + string.insertS32(0, 0) + + +def test_String_insertS64(string): + string.insertS64(0, 0) + + +def test_String_insertU32(string): + string.insertU32(0, 0) + + +def test_String_insertU64(string): + string.insertU64(0, 0) + + +def test_String_insertHex(string): + string.insertHex(0, 0) + + +def test_String_insertScalar(string): + string.insertScalar(0, 0.0) + + +@pytest.mark.parametrize('args', [ + (skia.String('foo'),), + ('foo',), + ('foo', 3), +]) +def test_String_append(string, args): + string.append(*args) + + +def test_String_appendUnichar(string): + string.appendUnichar(ord('x')) + + +def test_String_appendS32(string): + string.appendS32(0) + + +def test_String_appendS64(string): + string.appendS64(0) + + +def test_String_appendU32(string): + string.appendU32(0) + + +def test_String_appendU64(string): + string.appendU64(0) + + +def test_String_appendHex(string): + string.appendHex(0) + + +def test_String_appendScalar(string): + string.appendScalar(0.0) + + +@pytest.mark.parametrize('args', [ + (skia.String('foo'),), + ('foo',), + ('foo', 3), +]) +def test_String_prepend(string, args): + string.prepend(*args) + + +def test_String_prependUnichar(string): + string.prependUnichar(ord('x')) + + +def test_String_prependS32(string): + string.prependS32(0) + + +def test_String_prependS64(string): + string.prependS64(0) + + +def test_String_prependHex(string): + string.prependHex(0) + + +def test_String_prependScalar(string): + string.prependScalar(0.0) + + +def test_String_remove(string): + string.remove(0, 1) + + +@pytest.mark.parametrize('operand', [ + skia.String('foo'), + 'foo', + 'f', +]) +def test_String___iadd__(string, operand): + string += operand + + +def test_String_remove(string): + string.swap('bar')