Initial commit
This commit is contained in:
commit
0641a95770
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.cache
|
||||||
|
build
|
83
CMakeLists.txt
Normal file
83
CMakeLists.txt
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
cmake_minimum_required(VERSION 3.1...3.29)
|
||||||
|
|
||||||
|
project(docpp LANGUAGES CXX VERSION 0.0.1)
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_property(GLOBAL PROPERTY CXX_STANDARD 17)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} SHARED)
|
||||||
|
|
||||||
|
target_sources(${PROJECT_NAME} PRIVATE
|
||||||
|
"src/docpp.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE "${PROJECT_SOURCE_DIR}")
|
||||||
|
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
SOVERSION ${PROJECT_VERSION}
|
||||||
|
PUBLIC_HEADER "include/docpp.hpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS ${PROJECT_NAME}
|
||||||
|
EXPORT ${PROJECT_NAME}Targets
|
||||||
|
FILE_SET HEADERS
|
||||||
|
LIBRARY DESTINATION lib
|
||||||
|
ARCHIVE DESTINATION lib
|
||||||
|
RUNTIME DESTINATION bin
|
||||||
|
PUBLIC_HEADER DESTINATION include/docpp
|
||||||
|
INCLUDES DESTINATION include)
|
||||||
|
|
||||||
|
set(INCLUDE_INSTALL_DIR include/)
|
||||||
|
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
write_basic_package_version_file(
|
||||||
|
"${PROJECT_NAME}ConfigVersion.cmake"
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
COMPATIBILITY AnyNewerVersion)
|
||||||
|
|
||||||
|
install(EXPORT ${PROJECT_NAME}Targets
|
||||||
|
FILE ${PROJECT_NAME}Targets.cmake
|
||||||
|
NAMESPACE docpp::
|
||||||
|
DESTINATION lib/cmake/${PROJECT_NAME})
|
||||||
|
|
||||||
|
install(FILES "${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||||
|
DESTINATION lib/cmake/${PROJECT_NAME})
|
||||||
|
|
||||||
|
if (!${ENABLE_TESTS})
|
||||||
|
return()
|
||||||
|
else()
|
||||||
|
enable_testing()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(Catch2 3 REQUIRED)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME}_test
|
||||||
|
tests/test.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME}_test PRIVATE
|
||||||
|
"${PROJECT_SOURCE_DIR}"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME}_test PRIVATE
|
||||||
|
Catch2::Catch2WithMain
|
||||||
|
)
|
||||||
|
|
||||||
|
include(CTest)
|
||||||
|
include(Catch)
|
||||||
|
catch_discover_tests(${PROJECT_NAME}_test)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${PROJECT_NAME}_test
|
||||||
|
COMMENT "Run tests"
|
||||||
|
POST_BUILD
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
|
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure --rerun-failed -C
|
||||||
|
$<CONFIGURATION> -R "^${PROJECT_NAME}_test$"
|
||||||
|
)
|
165
LICENSE
Normal file
165
LICENSE
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based
|
||||||
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
27
README.md
Normal file
27
README.md
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# docpp
|
||||||
|
|
||||||
|
Small C++ library for generating XML, HTML and CSS.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To install the library, you can utilize the provided CMakeLists.txt file:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
cmake --build .
|
||||||
|
cmake --install . --prefix /usr
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Just include docpp.hpp in your project and link against the library. Examples can be found in the examples directory.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the GNU Lesser General Public License v3.0 - see the [LICENSE.md](LICENSE.md) file for details.
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
None. Just don't blow up my house.
|
304
cmake/Catch.cmake
Normal file
304
cmake/Catch.cmake
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||||
|
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||||
|
|
||||||
|
#[=======================================================================[.rst:
|
||||||
|
Catch
|
||||||
|
-----
|
||||||
|
|
||||||
|
This module defines a function to help use the Catch test framework.
|
||||||
|
|
||||||
|
The :command:`catch_discover_tests` discovers tests by asking the compiled test
|
||||||
|
executable to enumerate its tests. This does not require CMake to be re-run
|
||||||
|
when tests change. However, it may not work in a cross-compiling environment,
|
||||||
|
and setting test properties is less convenient.
|
||||||
|
|
||||||
|
This command is intended to replace use of :command:`add_test` to register
|
||||||
|
tests, and will create a separate CTest test for each Catch test case. Note
|
||||||
|
that this is in some cases less efficient, as common set-up and tear-down logic
|
||||||
|
cannot be shared by multiple test cases executing in the same instance.
|
||||||
|
However, it provides more fine-grained pass/fail information to CTest, which is
|
||||||
|
usually considered as more beneficial. By default, the CTest test name is the
|
||||||
|
same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
|
||||||
|
|
||||||
|
.. command:: catch_discover_tests
|
||||||
|
|
||||||
|
Automatically add tests with CTest by querying the compiled test executable
|
||||||
|
for available tests::
|
||||||
|
|
||||||
|
catch_discover_tests(target
|
||||||
|
[TEST_SPEC arg1...]
|
||||||
|
[EXTRA_ARGS arg1...]
|
||||||
|
[WORKING_DIRECTORY dir]
|
||||||
|
[TEST_PREFIX prefix]
|
||||||
|
[TEST_SUFFIX suffix]
|
||||||
|
[PROPERTIES name1 value1...]
|
||||||
|
[TEST_LIST var]
|
||||||
|
[REPORTER reporter]
|
||||||
|
[OUTPUT_DIR dir]
|
||||||
|
[OUTPUT_PREFIX prefix]
|
||||||
|
[OUTPUT_SUFFIX suffix]
|
||||||
|
[DISCOVERY_MODE <POST_BUILD|PRE_TEST>]
|
||||||
|
)
|
||||||
|
|
||||||
|
``catch_discover_tests`` sets up a post-build command on the test executable
|
||||||
|
that generates the list of tests by parsing the output from running the test
|
||||||
|
with the ``--list-test-names-only`` argument. This ensures that the full
|
||||||
|
list of tests is obtained. Since test discovery occurs at build time, it is
|
||||||
|
not necessary to re-run CMake when the list of tests changes.
|
||||||
|
However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
|
||||||
|
in order to function in a cross-compiling environment.
|
||||||
|
|
||||||
|
Additionally, setting properties on tests is somewhat less convenient, since
|
||||||
|
the tests are not available at CMake time. Additional test properties may be
|
||||||
|
assigned to the set of tests as a whole using the ``PROPERTIES`` option. If
|
||||||
|
more fine-grained test control is needed, custom content may be provided
|
||||||
|
through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES`
|
||||||
|
directory property. The set of discovered tests is made accessible to such a
|
||||||
|
script via the ``<target>_TESTS`` variable.
|
||||||
|
|
||||||
|
The options are:
|
||||||
|
|
||||||
|
``target``
|
||||||
|
Specifies the Catch executable, which must be a known CMake executable
|
||||||
|
target. CMake will substitute the location of the built executable when
|
||||||
|
running the test.
|
||||||
|
|
||||||
|
``TEST_SPEC arg1...``
|
||||||
|
Specifies test cases, wildcarded test cases, tags and tag expressions to
|
||||||
|
pass to the Catch executable with the ``--list-test-names-only`` argument.
|
||||||
|
|
||||||
|
``EXTRA_ARGS arg1...``
|
||||||
|
Any extra arguments to pass on the command line to each test case.
|
||||||
|
|
||||||
|
``WORKING_DIRECTORY dir``
|
||||||
|
Specifies the directory in which to run the discovered test cases. If this
|
||||||
|
option is not provided, the current binary directory is used.
|
||||||
|
|
||||||
|
``TEST_PREFIX prefix``
|
||||||
|
Specifies a ``prefix`` to be prepended to the name of each discovered test
|
||||||
|
case. This can be useful when the same test executable is being used in
|
||||||
|
multiple calls to ``catch_discover_tests()`` but with different
|
||||||
|
``TEST_SPEC`` or ``EXTRA_ARGS``.
|
||||||
|
|
||||||
|
``TEST_SUFFIX suffix``
|
||||||
|
Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
|
||||||
|
every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
|
||||||
|
be specified.
|
||||||
|
|
||||||
|
``PROPERTIES name1 value1...``
|
||||||
|
Specifies additional properties to be set on all tests discovered by this
|
||||||
|
invocation of ``catch_discover_tests``.
|
||||||
|
|
||||||
|
``TEST_LIST var``
|
||||||
|
Make the list of tests available in the variable ``var``, rather than the
|
||||||
|
default ``<target>_TESTS``. This can be useful when the same test
|
||||||
|
executable is being used in multiple calls to ``catch_discover_tests()``.
|
||||||
|
Note that this variable is only available in CTest.
|
||||||
|
|
||||||
|
``REPORTER reporter``
|
||||||
|
Use the specified reporter when running the test case. The reporter will
|
||||||
|
be passed to the Catch executable as ``--reporter reporter``.
|
||||||
|
|
||||||
|
``OUTPUT_DIR dir``
|
||||||
|
If specified, the parameter is passed along as
|
||||||
|
``--out dir/<test_name>`` to Catch executable. The actual file name is the
|
||||||
|
same as the test name. This should be used instead of
|
||||||
|
``EXTRA_ARGS --out foo`` to avoid race conditions writing the result output
|
||||||
|
when using parallel test execution.
|
||||||
|
|
||||||
|
``OUTPUT_PREFIX prefix``
|
||||||
|
May be used in conjunction with ``OUTPUT_DIR``.
|
||||||
|
If specified, ``prefix`` is added to each output file name, like so
|
||||||
|
``--out dir/prefix<test_name>``.
|
||||||
|
|
||||||
|
``OUTPUT_SUFFIX suffix``
|
||||||
|
May be used in conjunction with ``OUTPUT_DIR``.
|
||||||
|
If specified, ``suffix`` is added to each output file name, like so
|
||||||
|
``--out dir/<test_name>suffix``. This can be used to add a file extension to
|
||||||
|
the output e.g. ".xml".
|
||||||
|
|
||||||
|
``DL_PATHS path...``
|
||||||
|
Specifies paths that need to be set for the dynamic linker to find shared
|
||||||
|
libraries/DLLs when running the test executable (PATH/LD_LIBRARY_PATH respectively).
|
||||||
|
These paths will both be set when retrieving the list of test cases from the
|
||||||
|
test executable and when the tests are executed themselves. This requires
|
||||||
|
cmake/ctest >= 3.22.
|
||||||
|
|
||||||
|
`DISCOVERY_MODE mode``
|
||||||
|
Provides control over when ``catch_discover_tests`` performs test discovery.
|
||||||
|
By default, ``POST_BUILD`` sets up a post-build command to perform test discovery
|
||||||
|
at build time. In certain scenarios, like cross-compiling, this ``POST_BUILD``
|
||||||
|
behavior is not desirable. By contrast, ``PRE_TEST`` delays test discovery until
|
||||||
|
just prior to test execution. This way test discovery occurs in the target environment
|
||||||
|
where the test has a better chance at finding appropriate runtime dependencies.
|
||||||
|
|
||||||
|
``DISCOVERY_MODE`` defaults to the value of the
|
||||||
|
``CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not passed when
|
||||||
|
calling ``catch_discover_tests``. This provides a mechanism for globally selecting
|
||||||
|
a preferred test discovery behavior without having to modify each call site.
|
||||||
|
|
||||||
|
#]=======================================================================]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
function(catch_discover_tests TARGET)
|
||||||
|
|
||||||
|
cmake_parse_arguments(
|
||||||
|
""
|
||||||
|
""
|
||||||
|
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX;DISCOVERY_MODE"
|
||||||
|
"TEST_SPEC;EXTRA_ARGS;PROPERTIES;DL_PATHS"
|
||||||
|
${ARGN}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT _WORKING_DIRECTORY)
|
||||||
|
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
endif()
|
||||||
|
if(NOT _TEST_LIST)
|
||||||
|
set(_TEST_LIST ${TARGET}_TESTS)
|
||||||
|
endif()
|
||||||
|
if (_DL_PATHS)
|
||||||
|
if(${CMAKE_VERSION} VERSION_LESS "3.22.0")
|
||||||
|
message(FATAL_ERROR "The DL_PATHS option requires at least cmake 3.22")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(NOT _DISCOVERY_MODE)
|
||||||
|
if(NOT CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE)
|
||||||
|
set(CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD")
|
||||||
|
endif()
|
||||||
|
set(_DISCOVERY_MODE ${CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE})
|
||||||
|
endif()
|
||||||
|
if (NOT _DISCOVERY_MODE MATCHES "^(POST_BUILD|PRE_TEST)$")
|
||||||
|
message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
## Generate a unique name based on the extra arguments
|
||||||
|
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS} ${_REPORTER} ${_OUTPUT_DIR} ${_OUTPUT_PREFIX} ${_OUTPUT_SUFFIX}")
|
||||||
|
string(SUBSTRING ${args_hash} 0 7 args_hash)
|
||||||
|
|
||||||
|
# Define rule to generate test list for aforementioned test executable
|
||||||
|
set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-${args_hash}")
|
||||||
|
set(ctest_include_file "${ctest_file_base}_include.cmake")
|
||||||
|
set(ctest_tests_file "${ctest_file_base}_tests.cmake")
|
||||||
|
|
||||||
|
get_property(crosscompiling_emulator
|
||||||
|
TARGET ${TARGET}
|
||||||
|
PROPERTY CROSSCOMPILING_EMULATOR
|
||||||
|
)
|
||||||
|
|
||||||
|
if(_DISCOVERY_MODE STREQUAL "POST_BUILD")
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${TARGET} POST_BUILD
|
||||||
|
BYPRODUCTS "${ctest_tests_file}"
|
||||||
|
COMMAND "${CMAKE_COMMAND}"
|
||||||
|
-D "TEST_TARGET=${TARGET}"
|
||||||
|
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
|
||||||
|
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
|
||||||
|
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
|
||||||
|
-D "TEST_SPEC=${_TEST_SPEC}"
|
||||||
|
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
|
||||||
|
-D "TEST_PROPERTIES=${_PROPERTIES}"
|
||||||
|
-D "TEST_PREFIX=${_TEST_PREFIX}"
|
||||||
|
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
|
||||||
|
-D "TEST_LIST=${_TEST_LIST}"
|
||||||
|
-D "TEST_REPORTER=${_REPORTER}"
|
||||||
|
-D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}"
|
||||||
|
-D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}"
|
||||||
|
-D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}"
|
||||||
|
-D "TEST_DL_PATHS=${_DL_PATHS}"
|
||||||
|
-D "CTEST_FILE=${ctest_tests_file}"
|
||||||
|
-P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
file(WRITE "${ctest_include_file}"
|
||||||
|
"if(EXISTS \"${ctest_tests_file}\")\n"
|
||||||
|
" include(\"${ctest_tests_file}\")\n"
|
||||||
|
"else()\n"
|
||||||
|
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
|
||||||
|
"endif()\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST")
|
||||||
|
|
||||||
|
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL
|
||||||
|
PROPERTY GENERATOR_IS_MULTI_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
if(GENERATOR_IS_MULTI_CONFIG)
|
||||||
|
set(ctest_tests_file "${ctest_file_base}_tests-$<CONFIG>.cmake")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
string(CONCAT ctest_include_content
|
||||||
|
"if(EXISTS \"$<TARGET_FILE:${TARGET}>\")" "\n"
|
||||||
|
" if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n"
|
||||||
|
" NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$<TARGET_FILE:${TARGET}>\" OR\n"
|
||||||
|
" NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"\${CMAKE_CURRENT_LIST_FILE}\")\n"
|
||||||
|
" include(\"${_CATCH_DISCOVER_TESTS_SCRIPT}\")" "\n"
|
||||||
|
" catch_discover_tests_impl(" "\n"
|
||||||
|
" TEST_EXECUTABLE" " [==[" "$<TARGET_FILE:${TARGET}>" "]==]" "\n"
|
||||||
|
" TEST_EXECUTOR" " [==[" "${crosscompiling_emulator}" "]==]" "\n"
|
||||||
|
" TEST_WORKING_DIR" " [==[" "${_WORKING_DIRECTORY}" "]==]" "\n"
|
||||||
|
" TEST_SPEC" " [==[" "${_TEST_SPEC}" "]==]" "\n"
|
||||||
|
" TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n"
|
||||||
|
" TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n"
|
||||||
|
" TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n"
|
||||||
|
" TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n"
|
||||||
|
" TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n"
|
||||||
|
" TEST_REPORTER" " [==[" "${_REPORTER}" "]==]" "\n"
|
||||||
|
" TEST_OUTPUT_DIR" " [==[" "${_OUTPUT_DIR}" "]==]" "\n"
|
||||||
|
" TEST_OUTPUT_PREFIX" " [==[" "${_OUTPUT_PREFIX}" "]==]" "\n"
|
||||||
|
" TEST_OUTPUT_SUFFIX" " [==[" "${_OUTPUT_SUFFIX}" "]==]" "\n"
|
||||||
|
" CTEST_FILE" " [==[" "${ctest_tests_file}" "]==]" "\n"
|
||||||
|
" TEST_DL_PATHS" " [==[" "${_DL_PATHS}" "]==]" "\n"
|
||||||
|
" CTEST_FILE" " [==[" "${CTEST_FILE}" "]==]" "\n"
|
||||||
|
" )" "\n"
|
||||||
|
" endif()" "\n"
|
||||||
|
" include(\"${ctest_tests_file}\")" "\n"
|
||||||
|
"else()" "\n"
|
||||||
|
" add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)" "\n"
|
||||||
|
"endif()" "\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(GENERATOR_IS_MULTI_CONFIG)
|
||||||
|
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
|
||||||
|
file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $<CONFIG:${_config}>)
|
||||||
|
endforeach()
|
||||||
|
string(CONCAT ctest_include_multi_content
|
||||||
|
"if(NOT CTEST_CONFIGURATION_TYPE)" "\n"
|
||||||
|
" message(\"No configuration for testing specified, use '-C <cfg>'.\")" "\n"
|
||||||
|
"else()" "\n"
|
||||||
|
" include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")" "\n"
|
||||||
|
"endif()" "\n"
|
||||||
|
)
|
||||||
|
file(GENERATE OUTPUT "${ctest_include_file}" CONTENT "${ctest_include_multi_content}")
|
||||||
|
else()
|
||||||
|
file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}")
|
||||||
|
file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")
|
||||||
|
# Add discovered tests to directory TEST_INCLUDE_FILES
|
||||||
|
set_property(DIRECTORY
|
||||||
|
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
# Add discovered tests as directory TEST_INCLUDE_FILE if possible
|
||||||
|
get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET)
|
||||||
|
if (NOT ${test_include_file_set})
|
||||||
|
set_property(DIRECTORY
|
||||||
|
PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Cannot set more than one TEST_INCLUDE_FILE")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
set(_CATCH_DISCOVER_TESTS_SCRIPT
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake
|
||||||
|
CACHE INTERNAL "Catch2 full path to CatchAddTests.cmake helper file"
|
||||||
|
)
|
2
cmake/docppConfig.cmake
Normal file
2
cmake/docppConfig.cmake
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
include(CMakeFindDependencyMacro)
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/docppTargets.cmake)
|
1
compile_commands.json
Symbolic link
1
compile_commands.json
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
build/compile_commands.json
|
116
examples/hello-world.cpp
Normal file
116
examples/hello-world.cpp
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
// Compile with: g++ hello-world.cpp -o hello-world -ldocpp
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <docpp/docpp.hpp>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
/* This is your root document. It can hold *one* HTML section, and that section can hold any number of elements and/or sections.
|
||||||
|
* By default, the root document will prepend a doctype declaration. If you don't want that (e.g., if you're writing XML), you can
|
||||||
|
* use docpp::HTML::HTMLDocument::setDocType() to set the doctype to your preferred value.
|
||||||
|
*
|
||||||
|
* To get the document as an std::string object, call doc.get().
|
||||||
|
*/
|
||||||
|
docpp::HTML::HTMLDocument doc{};
|
||||||
|
|
||||||
|
/* This is an HTML section. It can hold any number of elements and/or sections.
|
||||||
|
* The first argument is the type of section, and this can either be a predefined value (e.g., docpp::HTML::SECTION_HTML) or a
|
||||||
|
* custom value in the form of an std::string object.
|
||||||
|
*
|
||||||
|
* The second argument is an HTMLElementProperties object, which is a collection of HTMLProperty objects. Each property is a std::pair of an
|
||||||
|
* attribute name and an attribute value. If you don't want to specify any attributes, you can pass an empty HTMLElementAttributes object.
|
||||||
|
* If you need to change the tag and/or attributes later, you can use the set() method.
|
||||||
|
*
|
||||||
|
* Note that it is very important that you do not append a section or element to a section until you have finished its construction,
|
||||||
|
* because push_back() makes a copy of the object you pass to it. If you need to make changes later, you can use methods such as find() and swap(),
|
||||||
|
* or construct a new object.
|
||||||
|
*
|
||||||
|
* To get the section as an std::string object, call section.get().
|
||||||
|
*/
|
||||||
|
docpp::HTML::HTMLSection htmlSection(docpp::HTML::SECTION_HTML, {}); // <html></html>
|
||||||
|
docpp::HTML::HTMLSection headSection(docpp::HTML::SECTION_HEAD, {}); // <head></head>
|
||||||
|
docpp::HTML::HTMLSection bodySection(docpp::HTML::SECTION_BODY, {}); // <body></body>
|
||||||
|
docpp::HTML::HTMLSection footerSection(docpp::HTML::SECTION_FOOTER, {}); // <footer></footer>
|
||||||
|
|
||||||
|
/* This is an HTML element. Unlike a section, an element cannot hold any other elements or sections, rather it holds text and/or attributes.
|
||||||
|
* The first argument is the type of element, and this should simply be the tag name (e.g., "p", "h1", "a", etc.).
|
||||||
|
*
|
||||||
|
* The second argument is an HTMLElementProperties object, which is a collection of HTMLProperty objects. Each property is a std::pair of an
|
||||||
|
* attribute name and an attribute value. If you don't want to specify any attributes, you can pass an empty HTMLElementAttributes object.
|
||||||
|
* If you need to change the element's tag, attributes, type or text later, you can use the set() method.
|
||||||
|
*
|
||||||
|
* The third argument is the text content of the element. If you don't want to specify any text, you can pass an empty std::string object.
|
||||||
|
*
|
||||||
|
* The fourth argument is an integer representing the closing tag type. This can be one of the following:
|
||||||
|
*
|
||||||
|
* - docpp::HTML::TYPE_NON_CLOSED: No closing tag will be appended.
|
||||||
|
* Example: <img src="example.jpg">
|
||||||
|
* - docpp::HTML::TYPE_NON_SELF_CLOSING: A closing tag will be appended.
|
||||||
|
* Example: <p>Hello world</p>
|
||||||
|
* - docpp::HTML::TYPE_SELF_CLOSING: A self-closing tag will be appended.
|
||||||
|
* Example: <img src="example.jpg">
|
||||||
|
*
|
||||||
|
* To get the element as an std::string object, call element.get().
|
||||||
|
*/
|
||||||
|
docpp::HTML::HTMLElement titleElement("title", {}, "Hello world document"); // <title>Hello world document</title>
|
||||||
|
|
||||||
|
/* Add the title and meta elements to the head section. */
|
||||||
|
headSection.push_back(titleElement);
|
||||||
|
headSection.push_back(docpp::HTML::HTMLElement("meta", {{docpp::HTML::HTMLProperty("name", "description"), docpp::HTML::HTMLProperty("content", "Hello world document description!")}}, "", docpp::HTML::TYPE_NON_CLOSED));
|
||||||
|
|
||||||
|
/* This is a CSS document. It is essentially the CSS equivalent of an HTML section.
|
||||||
|
* It is essentially a collection of CSSElement objects, which is a collection of CSSProperty objects.
|
||||||
|
*
|
||||||
|
* In other words, a CSS document is a collection of CSS elements, which are collections of CSS properties.
|
||||||
|
*/
|
||||||
|
docpp::CSS::CSSStylesheet stylesheet{};
|
||||||
|
|
||||||
|
/* This is a CSS element. It is essentially a collection of CSS properties.
|
||||||
|
* The first argument is the type of element, and this should simply be the tag name (e.g., "body", "p", "h1", etc.).
|
||||||
|
*
|
||||||
|
* The second argument is a CSSProperties object, which is a collection of CSSProperty objects. Each property is a std::pair of a
|
||||||
|
* property name and a property value. If you don't want to specify any properties, you can pass an empty CSSProperties object, but... of course, that's not very useful.
|
||||||
|
*
|
||||||
|
* To get the element as an std::string object, call element.get().
|
||||||
|
*/
|
||||||
|
docpp::CSS::CSSElement bodyStyling("body", {{docpp::CSS::CSSProperty("background-color", "black"), docpp::CSS::CSSProperty("color", "white")}}); // body { background-color: black; color: white; }
|
||||||
|
|
||||||
|
/* Now, let's add the body style to the stylesheet. */
|
||||||
|
stylesheet.push_back(bodyStyling);
|
||||||
|
|
||||||
|
/* To get the stylesheet as an std::string object, call stylesheet.get(). It can then be used in an HTMLElement object. */
|
||||||
|
const std::string& css = stylesheet.get(); // body { background-color: black; color: white; }
|
||||||
|
|
||||||
|
headSection.push_back(docpp::HTML::HTMLElement("style", {}, css)); // <style>body { background-color: black; color: white; }</style>
|
||||||
|
|
||||||
|
/* Add a paragraph element to the body section. */
|
||||||
|
bodySection.push_back(docpp::HTML::HTMLElement("p", {}, "Hello, world!")); // <p>Hello, world!</p>
|
||||||
|
|
||||||
|
/* Likewise, add a paragraph element to the footer section. */
|
||||||
|
footerSection.push_back(docpp::HTML::HTMLElement("p", {}, "This is the footer.")); // <p>This is the footer.</p>
|
||||||
|
|
||||||
|
/* Now, let's add the header, body and footer section to the html section.
|
||||||
|
* The order does matter, because an identifier is used internally. You can get this identifier by e.g. using find().
|
||||||
|
*/
|
||||||
|
htmlSection.push_back(headSection); // <html><head>...</head></html>
|
||||||
|
htmlSection.push_back(bodySection); // <html><head>...</head><body>...</body></html>
|
||||||
|
htmlSection.push_back(footerSection); // <html><head>...</head><body>...</body><footer>...</footer></html>
|
||||||
|
|
||||||
|
/* Now, let's add the html section to the document. */
|
||||||
|
doc.set(htmlSection);
|
||||||
|
|
||||||
|
/* Finally, let's output the document to a file and print it to standard output. */
|
||||||
|
std::ofstream file("hello-world.html");
|
||||||
|
|
||||||
|
/* Optionally, you can use the get() method with the docpp::HTML::FORMATTING_PRETTY argument to get a *slightly* more readable document.
|
||||||
|
* It still doesn't look hand-made, but it's readable at least. The same goes for the CSS document.
|
||||||
|
*/
|
||||||
|
file << doc.get(docpp::HTML::FORMATTING_PRETTY);
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
std::cout << doc.get() << "\n";
|
||||||
|
|
||||||
|
/* And we're done! */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
556
include/docpp.hpp
Normal file
556
include/docpp.hpp
Normal file
|
@ -0,0 +1,556 @@
|
||||||
|
/**
|
||||||
|
* docpp - Small C++ library for generating XML, HTML and CSS.
|
||||||
|
* Licensed under the LGPL-3.0-or-later license.
|
||||||
|
*
|
||||||
|
* Author: speedie <speedie@speedie.site>
|
||||||
|
*
|
||||||
|
* @file docpp.hpp
|
||||||
|
* @brief Header file for docpp
|
||||||
|
* @author speedie
|
||||||
|
* @date 2024
|
||||||
|
* @copyright GNU Lesser General Public License 3.0
|
||||||
|
* @version 0.0.1
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A namespace to represent HTML elements and documents
|
||||||
|
*/
|
||||||
|
namespace docpp {
|
||||||
|
namespace HTML {
|
||||||
|
enum {
|
||||||
|
SECTION_HTML,
|
||||||
|
SECTION_HEAD,
|
||||||
|
SECTION_BODY,
|
||||||
|
SECTION_FOOTER,
|
||||||
|
SECTION_DIV,
|
||||||
|
TYPE_SELF_CLOSING,
|
||||||
|
TYPE_NON_SELF_CLOSING,
|
||||||
|
TYPE_NON_CLOSED,
|
||||||
|
FORMATTING_NONE,
|
||||||
|
FORMATTING_PRETTY,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A class to represent an HTML property
|
||||||
|
*/
|
||||||
|
class HTMLProperty {
|
||||||
|
private:
|
||||||
|
std::pair<std::string, std::string> property{};
|
||||||
|
protected:
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new HTMLProperty object
|
||||||
|
* @param key The key of the property
|
||||||
|
* @param value The value of the property
|
||||||
|
*/
|
||||||
|
HTMLProperty(const std::string& key, const std::string& value);
|
||||||
|
/**
|
||||||
|
* @brief Construct a new HTMLProperty object
|
||||||
|
* @param property The property to set
|
||||||
|
*/
|
||||||
|
HTMLProperty(const std::pair<std::string, std::string>& property);
|
||||||
|
HTMLProperty() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the key of the property
|
||||||
|
* @return std::string The key of the property
|
||||||
|
*/
|
||||||
|
std::string getKey() const;
|
||||||
|
/**
|
||||||
|
* @brief Get the value of the property
|
||||||
|
* @return std::string The value of the property
|
||||||
|
*/
|
||||||
|
std::string getValue() const;
|
||||||
|
/**
|
||||||
|
* @brief Get the property.
|
||||||
|
* @return std::pair<std::string, std::string> The value of the property
|
||||||
|
*/
|
||||||
|
std::pair<std::string, std::string> get() const;
|
||||||
|
/**
|
||||||
|
* @brief Set the key of the property.
|
||||||
|
* @param key The key.
|
||||||
|
*/
|
||||||
|
void setKey(const std::string& key);
|
||||||
|
/**
|
||||||
|
* @brief Set the value of the property.
|
||||||
|
* @param value The value.
|
||||||
|
*/
|
||||||
|
void setValue(const std::string& value);
|
||||||
|
/**
|
||||||
|
* @brief Set the property
|
||||||
|
* @param property The property.
|
||||||
|
*/
|
||||||
|
void set(const std::pair<std::string, std::string>& property);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A class to represent the properties of an HTML element
|
||||||
|
*/
|
||||||
|
class HTMLElementProperties {
|
||||||
|
private:
|
||||||
|
std::vector<HTMLProperty> properties{};
|
||||||
|
protected:
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Get the properties of the element
|
||||||
|
* @return std::vector<HTMLProperty> The properties of the element
|
||||||
|
*/
|
||||||
|
std::vector<HTMLProperty> get() const;
|
||||||
|
/**
|
||||||
|
* @brief Set the properties of the element
|
||||||
|
* @param properties The properties to set
|
||||||
|
*/
|
||||||
|
void set(const std::vector<HTMLProperty>& properties);
|
||||||
|
/**
|
||||||
|
* @brief Add a property to the element
|
||||||
|
* @param property The property to add
|
||||||
|
*/
|
||||||
|
void push_back(const HTMLProperty& property);
|
||||||
|
/**
|
||||||
|
* @brief Construct a new HTMLElementProperties object
|
||||||
|
* @param properties The properties to set
|
||||||
|
*/
|
||||||
|
HTMLElementProperties(const std::vector<HTMLProperty>& properties);
|
||||||
|
/**
|
||||||
|
* @brief Construct a new HTMLElementProperties object
|
||||||
|
* @param property The property to add
|
||||||
|
*/
|
||||||
|
HTMLElementProperties(const HTMLProperty& property);
|
||||||
|
/**
|
||||||
|
* @brief Construct a new HTMLElementProperties object
|
||||||
|
*/
|
||||||
|
HTMLElementProperties() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A class to represent an HTML element
|
||||||
|
*/
|
||||||
|
class HTMLElement {
|
||||||
|
private:
|
||||||
|
std::string tag{};
|
||||||
|
std::string data{};
|
||||||
|
int type{TYPE_NON_SELF_CLOSING};
|
||||||
|
HTMLElementProperties properties{};
|
||||||
|
protected:
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new HTMLElement object
|
||||||
|
* @param tag The tag of the element
|
||||||
|
* @param properties The properties of the element
|
||||||
|
* @param data The data of the element
|
||||||
|
*/
|
||||||
|
HTMLElement(const std::string& tag, const HTMLElementProperties& properties = {}, const std::string& data = {}, const int type = TYPE_NON_SELF_CLOSING);
|
||||||
|
/**
|
||||||
|
* @brief Construct a new HTMLElement object
|
||||||
|
*/
|
||||||
|
HTMLElement() = default;
|
||||||
|
/**
|
||||||
|
* @brief Set the tag, id, and classes of the element
|
||||||
|
* @param tag The tag of the element
|
||||||
|
* @param id The id of the element
|
||||||
|
* @param classes The classes of the element
|
||||||
|
*/
|
||||||
|
void set(const std::string& tag, const HTMLElementProperties& properties = {}, const std::string& data = {}, const int type = TYPE_NON_SELF_CLOSING);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the element in the form of an HTML tag.
|
||||||
|
* @return std::string The tag of the element
|
||||||
|
*/
|
||||||
|
std::string get(const int formatting = FORMATTING_NONE) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the tag of the element
|
||||||
|
* @return std::string The data of the element
|
||||||
|
*/
|
||||||
|
std::string getTag() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the data of the element
|
||||||
|
* @return std::string The data of the element
|
||||||
|
*/
|
||||||
|
std::string getData() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A class to represent an HTML section (head, body, etc.)
|
||||||
|
*/
|
||||||
|
class HTMLSection {
|
||||||
|
private:
|
||||||
|
int index{};
|
||||||
|
std::string tag{};
|
||||||
|
HTMLElementProperties properties{};
|
||||||
|
|
||||||
|
std::unordered_map<int, HTMLElement> elements{};
|
||||||
|
std::unordered_map<int, HTMLSection> sections{};
|
||||||
|
protected:
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Prepend an element to the section
|
||||||
|
* @param element The element to add
|
||||||
|
*/
|
||||||
|
void push_front(const HTMLElement& element);
|
||||||
|
/**
|
||||||
|
* @brief Prepend a section to the section
|
||||||
|
* @param section The section to add
|
||||||
|
*/
|
||||||
|
void push_front(const HTMLSection& section);
|
||||||
|
/**
|
||||||
|
* @brief Append an element to the section
|
||||||
|
* @param element The element to add
|
||||||
|
*/
|
||||||
|
void push_back(const HTMLElement& element);
|
||||||
|
/**
|
||||||
|
* @brief Append a section to the section
|
||||||
|
* @param section The section to add
|
||||||
|
*/
|
||||||
|
void push_back(const HTMLSection& section);
|
||||||
|
/**
|
||||||
|
* @brief Erase an element from the section. Note that this will NOT change the size/index.
|
||||||
|
* @param index The index of the element to erase
|
||||||
|
*/
|
||||||
|
void erase(const int index);
|
||||||
|
/**
|
||||||
|
* @brief Erase a section from the section, by reading from a section. The section will be erased if it's identical to section. Note that this will NOT change the size/index.
|
||||||
|
* @param section The section to erase
|
||||||
|
*/
|
||||||
|
void erase(const HTMLSection& section);
|
||||||
|
/**
|
||||||
|
* @brief Erase an element from the section, by reading from an element. The element will be erased if it's identical to element. Note that this will NOT change the size/index.
|
||||||
|
* @param element The element to erase
|
||||||
|
*/
|
||||||
|
void erase(const HTMLElement& element);
|
||||||
|
/**
|
||||||
|
* @brief Find an element in the section
|
||||||
|
* @param element The element to find
|
||||||
|
* @return int The index of the element
|
||||||
|
*/
|
||||||
|
int find(const HTMLElement& element);
|
||||||
|
/**
|
||||||
|
* @brief Find a section in the section
|
||||||
|
* @param section The section to find
|
||||||
|
* @return int The index of the section
|
||||||
|
*/
|
||||||
|
int find(const HTMLSection& section);
|
||||||
|
/**
|
||||||
|
* @brief Insert an element into the section
|
||||||
|
* @param index The index to insert the element
|
||||||
|
* @param element The element to insert
|
||||||
|
*/
|
||||||
|
void insert(const int index, const HTMLElement& element);
|
||||||
|
/**
|
||||||
|
* @brief Insert a section into the section
|
||||||
|
* @param index The index to insert the section
|
||||||
|
* @param section The section to insert
|
||||||
|
*/
|
||||||
|
void insert(const int index, const HTMLSection& section);
|
||||||
|
/**
|
||||||
|
* @brief Get the size of the section
|
||||||
|
* @return int The size of the section
|
||||||
|
*/
|
||||||
|
int size() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new HTMLSection object
|
||||||
|
* @param tag The tag of the section
|
||||||
|
* @param properties The properties of the section
|
||||||
|
*/
|
||||||
|
HTMLSection(const std::string& tag, const HTMLElementProperties& properties = {});
|
||||||
|
/**
|
||||||
|
* @brief Construct a new HTMLSection object
|
||||||
|
* @param tag The tag of the section
|
||||||
|
* @param properties The properties of the section
|
||||||
|
*/
|
||||||
|
HTMLSection(const int tag, const HTMLElementProperties& properties = {});
|
||||||
|
/**
|
||||||
|
* @brief Construct a new HTMLSection object
|
||||||
|
*/
|
||||||
|
HTMLSection() = default;
|
||||||
|
/**
|
||||||
|
* @brief Set the tag, id, and classes of the section
|
||||||
|
* @param tag The tag of the section
|
||||||
|
* @param id The id of the section
|
||||||
|
* @param classes The classes of the section
|
||||||
|
*/
|
||||||
|
void set(const std::string& tag, const HTMLElementProperties& properties = {});
|
||||||
|
/**
|
||||||
|
* @brief Set the tag, id, and classes of the section
|
||||||
|
* @param tag The tag of the section
|
||||||
|
* @param id The id of the section
|
||||||
|
* @param classes The classes of the section
|
||||||
|
*/
|
||||||
|
void set(const int tag, const HTMLElementProperties& properties = {});
|
||||||
|
/**
|
||||||
|
* @brief Swap two elements in the section
|
||||||
|
* @param index1 The index of the first element
|
||||||
|
* @param index2 The index of the second element
|
||||||
|
*/
|
||||||
|
void swap(const int index1, const int index2);
|
||||||
|
/**
|
||||||
|
* @brief Swap two elements in the section
|
||||||
|
* @param element1 The first element
|
||||||
|
* @param element2 The second element
|
||||||
|
*/
|
||||||
|
void swap(const HTMLElement& element1, const HTMLElement& element2);
|
||||||
|
/**
|
||||||
|
* @brief Swap two sections in the section
|
||||||
|
* @param index1 The index of the first section
|
||||||
|
* @param index2 The index of the second section
|
||||||
|
*/
|
||||||
|
void swap(const HTMLSection& section1, const HTMLSection& section2);
|
||||||
|
/**
|
||||||
|
* @brief Get the elements of the section
|
||||||
|
* @return std::vector<HTMLElement> The elements of the section
|
||||||
|
*/
|
||||||
|
std::vector<HTMLElement> getHTMLElements();
|
||||||
|
/**
|
||||||
|
* @brief Get the sections of the section
|
||||||
|
* @return std::vector<HTMLSection> The sections of the section
|
||||||
|
*/
|
||||||
|
std::vector<HTMLSection> getHTMLSections();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dump the entire section.
|
||||||
|
* @return std::string The section
|
||||||
|
*/
|
||||||
|
std::string get(const int formatting = FORMATTING_NONE) const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A class to represent an HTML document
|
||||||
|
*/
|
||||||
|
class HTMLDocument {
|
||||||
|
private:
|
||||||
|
std::string doctype{"<!DOCTYPE html>"};
|
||||||
|
HTMLSection document{};
|
||||||
|
protected:
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Get the document
|
||||||
|
* @param std::string The type to return
|
||||||
|
* @return std::string The document
|
||||||
|
*/
|
||||||
|
std::string get(const int formatting = FORMATTING_NONE) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the doctype of the document
|
||||||
|
* @return std::string The doctype of the document
|
||||||
|
*/
|
||||||
|
std::string getDoctype() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the document
|
||||||
|
* @param document The document to set
|
||||||
|
*/
|
||||||
|
void set(const HTMLSection& document);
|
||||||
|
/**
|
||||||
|
* @brief Set the doctype of the document
|
||||||
|
* @param doctype The doctype to set
|
||||||
|
*/
|
||||||
|
void setDoctype(const std::string& doctype);
|
||||||
|
/**
|
||||||
|
* @brief Construct a new HTMLDocument object
|
||||||
|
*/
|
||||||
|
HTMLDocument() = default;
|
||||||
|
/**
|
||||||
|
* @brief Construct a new HTMLDocument object
|
||||||
|
* @param document The document to set
|
||||||
|
*/
|
||||||
|
HTMLDocument(const HTMLSection& document, const std::string& doctype = "<!DOCTYPE html>");
|
||||||
|
};
|
||||||
|
} // namespace HTML
|
||||||
|
|
||||||
|
namespace CSS {
|
||||||
|
enum {
|
||||||
|
FORMATTING_NONE,
|
||||||
|
FORMATTING_PRETTY,
|
||||||
|
};
|
||||||
|
|
||||||
|
class CSSProperty {
|
||||||
|
private:
|
||||||
|
std::pair<std::string, std::string> property{};
|
||||||
|
protected:
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new CSSProperty object
|
||||||
|
* @param key The key of the property
|
||||||
|
* @param value The value of the property
|
||||||
|
*/
|
||||||
|
CSSProperty(const std::string& key, const std::string& value);
|
||||||
|
/**
|
||||||
|
* @brief Construct a new CSSProperty object
|
||||||
|
* @param property The property to set
|
||||||
|
*/
|
||||||
|
CSSProperty(const std::pair<std::string, std::string>& property);
|
||||||
|
CSSProperty() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the key of the property
|
||||||
|
* @return std::string The key of the property
|
||||||
|
*/
|
||||||
|
std::string getKey() const;
|
||||||
|
/**
|
||||||
|
* @brief Get the value of the property
|
||||||
|
* @return std::string The value of the property
|
||||||
|
*/
|
||||||
|
std::string getValue() const;
|
||||||
|
/**
|
||||||
|
* @brief Get the property.
|
||||||
|
* @return std::pair<std::string, std::string> The value of the property
|
||||||
|
*/
|
||||||
|
std::pair<std::string, std::string> get() const;
|
||||||
|
/**
|
||||||
|
* @brief Set the key of the property.
|
||||||
|
* @param key The key.
|
||||||
|
*/
|
||||||
|
void setKey(const std::string& key);
|
||||||
|
/**
|
||||||
|
* @brief Set the value of the property.
|
||||||
|
* @param value The value.
|
||||||
|
*/
|
||||||
|
void setValue(const std::string& value);
|
||||||
|
/**
|
||||||
|
* @brief Set the property
|
||||||
|
* @param property The property.
|
||||||
|
*/
|
||||||
|
void set(const std::pair<std::string, std::string>& property);
|
||||||
|
/**
|
||||||
|
* @brief Set the property
|
||||||
|
* @param key The key of the property
|
||||||
|
* @param value The value of the property
|
||||||
|
*/
|
||||||
|
void set(const std::string& key, const std::string& value);
|
||||||
|
};
|
||||||
|
|
||||||
|
class CSSElement {
|
||||||
|
private:
|
||||||
|
std::pair<std::string, std::vector<CSSProperty>> element{};
|
||||||
|
protected:
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new CSSElement object
|
||||||
|
* @param tag The tag of the element
|
||||||
|
* @param properties The properties of the element
|
||||||
|
*/
|
||||||
|
CSSElement(const std::string& tag, const std::vector<CSSProperty>& properties);
|
||||||
|
/**
|
||||||
|
* @brief Construct a new CSSElement object
|
||||||
|
* @param element The element to set
|
||||||
|
*/
|
||||||
|
CSSElement(const std::pair<std::string, std::vector<CSSProperty>>& element);
|
||||||
|
CSSElement() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Push a property to the element
|
||||||
|
* @param property The property to push
|
||||||
|
*/
|
||||||
|
void push_back(const CSSProperty& property);
|
||||||
|
/**
|
||||||
|
* @brief Set the properties of the element
|
||||||
|
* @param properties The properties to set
|
||||||
|
*/
|
||||||
|
void set(const std::string& tag, const std::vector<CSSProperty>& properties);
|
||||||
|
/**
|
||||||
|
* @brief Set the properties of the element
|
||||||
|
* @param element The element to set
|
||||||
|
*/
|
||||||
|
void set(const std::pair<std::string, std::vector<CSSProperty>>& element);
|
||||||
|
/**
|
||||||
|
* @brief Get the element
|
||||||
|
* @return std::pair<std::string, std::vector<CSSProperty>> The element
|
||||||
|
*/
|
||||||
|
std::string get(const int formatting = FORMATTING_NONE) const;
|
||||||
|
/**
|
||||||
|
* @brief Get the tag of the element
|
||||||
|
* @return std::string The tag of the element
|
||||||
|
*/
|
||||||
|
std::string getTag() const;
|
||||||
|
/**
|
||||||
|
* @brief Get the properties of the element
|
||||||
|
* @return std::vector<CSSProperty> The properties of the element
|
||||||
|
*/
|
||||||
|
std::vector<CSSProperty> getProperties() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A class to represent a CSS stylesheet
|
||||||
|
*/
|
||||||
|
class CSSStylesheet {
|
||||||
|
private:
|
||||||
|
std::vector<CSSElement> elements{};
|
||||||
|
protected:
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new CSSStylesheet object
|
||||||
|
* @param elements The elements to set
|
||||||
|
*/
|
||||||
|
CSSStylesheet(const std::vector<CSSElement>& elements);
|
||||||
|
CSSStylesheet() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prepend an element to the stylesheet
|
||||||
|
* @param element The element to add
|
||||||
|
*/
|
||||||
|
void push_front(const CSSElement& element);
|
||||||
|
/**
|
||||||
|
* @brief Append an element to the stylesheet
|
||||||
|
* @param element The element to add
|
||||||
|
*/
|
||||||
|
void push_back(const CSSElement& element);
|
||||||
|
/**
|
||||||
|
* @brief Insert an element into the stylesheet
|
||||||
|
* @param index The index to insert the element
|
||||||
|
* @param element The element to insert
|
||||||
|
*/
|
||||||
|
void insert(const int index, const CSSElement& element);
|
||||||
|
/**
|
||||||
|
* @brief Erase an element from the stylesheet. Note that this will NOT change the size/index.
|
||||||
|
* @param index The index of the element to erase
|
||||||
|
*/
|
||||||
|
void erase(const int index);
|
||||||
|
/**
|
||||||
|
* @brief Find an element in the stylesheet
|
||||||
|
* @param element The element to find
|
||||||
|
* @return int The index of the element
|
||||||
|
*/
|
||||||
|
int find(const CSSElement& element);
|
||||||
|
/**
|
||||||
|
* @brief Get the size of the stylesheet
|
||||||
|
* @return int The size of the stylesheet
|
||||||
|
*/
|
||||||
|
int size() const;
|
||||||
|
/**
|
||||||
|
* @brief Swap two elements in the stylesheet
|
||||||
|
* @param index1 The index of the first element
|
||||||
|
* @param index2 The index of the second element
|
||||||
|
*/
|
||||||
|
void swap(const int index1, const int index2);
|
||||||
|
/**
|
||||||
|
* @brief Swap two elements in the stylesheet
|
||||||
|
* @param element1 The first element
|
||||||
|
* @param element2 The second element
|
||||||
|
*/
|
||||||
|
void swap(const CSSElement& element1, const CSSElement& element2);
|
||||||
|
/**
|
||||||
|
* @brief Set the elements of the stylesheet
|
||||||
|
* @param elements The elements to set
|
||||||
|
*/
|
||||||
|
void set(const std::vector<CSSElement>& elements);
|
||||||
|
/**
|
||||||
|
* @brief Get the elements of the stylesheet
|
||||||
|
* @return std::vector<CSSElement> The elements of the stylesheet
|
||||||
|
*/
|
||||||
|
std::vector<CSSElement> getElements() const;
|
||||||
|
/**
|
||||||
|
* @brief Get the stylesheet
|
||||||
|
* @return std::string The stylesheet
|
||||||
|
*/
|
||||||
|
std::string get(const int formatting = FORMATTING_NONE) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
538
src/docpp.cpp
Normal file
538
src/docpp.cpp
Normal file
|
@ -0,0 +1,538 @@
|
||||||
|
/**
|
||||||
|
* docpp - Small C++ library for generating XML, HTML and CSS.
|
||||||
|
* Licensed under the LGPL-3.0-or-later license.
|
||||||
|
*
|
||||||
|
* Author: speedie <speedie@speedie.site>
|
||||||
|
*
|
||||||
|
* @file docpp.cpp
|
||||||
|
* @brief Implementation of the docpp library.
|
||||||
|
* @author speedie
|
||||||
|
* @date 2024
|
||||||
|
* @copyright GNU Lesser General Public License 3.0.
|
||||||
|
* @version 0.0.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <include/docpp.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
docpp::HTML::HTMLProperty::HTMLProperty(const std::string& key, const std::string& value) {
|
||||||
|
this->setKey(key);
|
||||||
|
this->setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
docpp::HTML::HTMLProperty::HTMLProperty(const std::pair<std::string, std::string>& property) {
|
||||||
|
this->set(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string docpp::HTML::HTMLProperty::getKey() const {
|
||||||
|
return this->property.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string docpp::HTML::HTMLProperty::getValue() const {
|
||||||
|
return this->property.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> docpp::HTML::HTMLProperty::get() const {
|
||||||
|
return this->property;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLProperty::setKey(const std::string& key) {
|
||||||
|
this->property.first = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLProperty::setValue(const std::string& value) {
|
||||||
|
this->property.second = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLProperty::set(const std::pair<std::string, std::string>& property) {
|
||||||
|
this->property = property;
|
||||||
|
}
|
||||||
|
|
||||||
|
docpp::HTML::HTMLElementProperties::HTMLElementProperties(const std::vector<docpp::HTML::HTMLProperty>& properties) {
|
||||||
|
this->set(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
docpp::HTML::HTMLElementProperties::HTMLElementProperties(const docpp::HTML::HTMLProperty& property) {
|
||||||
|
this->push_back(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<docpp::HTML::HTMLProperty> docpp::HTML::HTMLElementProperties::get() const {
|
||||||
|
return this->properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLElementProperties::set(const std::vector<docpp::HTML::HTMLProperty>& properties) {
|
||||||
|
this->properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLElementProperties::push_back(const docpp::HTML::HTMLProperty& property) {
|
||||||
|
this->properties.push_back(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
docpp::HTML::HTMLElement::HTMLElement(const std::string& tag, const HTMLElementProperties& properties, const std::string& data, const int type) {
|
||||||
|
this->set(tag, properties, data, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLElement::set(const std::string& tag, const HTMLElementProperties& properties, const std::string& data, const int type) {
|
||||||
|
this->tag = tag;
|
||||||
|
this->data = data;
|
||||||
|
this->properties = properties;
|
||||||
|
this->type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string docpp::HTML::HTMLElement::get(const int formatting) const {
|
||||||
|
std::string ret{};
|
||||||
|
|
||||||
|
ret += "<" + this->tag;
|
||||||
|
|
||||||
|
for (const auto& it : this->properties.get()) {
|
||||||
|
if (!it.getKey().compare("")) continue;
|
||||||
|
if (!it.getValue().compare("")) continue;
|
||||||
|
|
||||||
|
ret += " " + it.getKey() + "=\"" + it.getValue() + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->type != docpp::HTML::TYPE_SELF_CLOSING) {
|
||||||
|
ret += ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->type == docpp::HTML::TYPE_NON_SELF_CLOSING) {
|
||||||
|
ret += this->data + "</" + this->tag + ">";
|
||||||
|
} else if (this->type == docpp::HTML::TYPE_SELF_CLOSING) {
|
||||||
|
ret += this->data + "/>";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formatting == docpp::HTML::FORMATTING_PRETTY) {
|
||||||
|
ret += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string docpp::HTML::HTMLElement::getTag() const {
|
||||||
|
return this->tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string docpp::HTML::HTMLElement::getData() const {
|
||||||
|
return this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
docpp::HTML::HTMLSection::HTMLSection(const std::string& tag, const HTMLElementProperties& properties) {
|
||||||
|
this->tag = tag;
|
||||||
|
this->properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
docpp::HTML::HTMLSection::HTMLSection(const int tag, const HTMLElementProperties& properties) {
|
||||||
|
if (tag == docpp::HTML::SECTION_DIV) {
|
||||||
|
this->tag = "div";
|
||||||
|
} else if (tag == docpp::HTML::SECTION_BODY) {
|
||||||
|
this->tag = "body";
|
||||||
|
} else if (tag == docpp::HTML::SECTION_FOOTER) {
|
||||||
|
this->tag = "footer";
|
||||||
|
} else if (tag == docpp::HTML::SECTION_HEAD) {
|
||||||
|
this->tag = "head";
|
||||||
|
} else if (tag == docpp::HTML::SECTION_HTML) {
|
||||||
|
this->tag = "html";
|
||||||
|
}
|
||||||
|
|
||||||
|
this->properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::set(const std::string& tag, const HTMLElementProperties& properties) {
|
||||||
|
this->tag = tag;
|
||||||
|
this->properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::set(const int tag, const HTMLElementProperties& properties) {
|
||||||
|
if (tag == docpp::HTML::SECTION_DIV) {
|
||||||
|
this->tag = "div";
|
||||||
|
} else if (tag == docpp::HTML::SECTION_BODY) {
|
||||||
|
this->tag = "body";
|
||||||
|
} else if (tag == docpp::HTML::SECTION_FOOTER) {
|
||||||
|
this->tag = "footer";
|
||||||
|
} else if (tag == docpp::HTML::SECTION_HEAD) {
|
||||||
|
this->tag = "head";
|
||||||
|
} else if (tag == docpp::HTML::SECTION_HTML) {
|
||||||
|
this->tag = "html";
|
||||||
|
}
|
||||||
|
|
||||||
|
this->properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::push_front(const HTMLElement& element) {
|
||||||
|
for (int i{this->index}; i > 0; i--) {
|
||||||
|
this->elements[i] = this->elements.at(i - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->elements[0] = element;
|
||||||
|
this->index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::push_front(const HTMLSection& section) {
|
||||||
|
for (int i{this->index}; i > 0; i--) {
|
||||||
|
this->sections.at(i) = this->sections.at(i - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->sections[0] = section;
|
||||||
|
this->index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::push_back(const HTMLElement& element) {
|
||||||
|
this->elements[this->index++] = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::push_back(const HTMLSection& section) {
|
||||||
|
this->sections[this->index++] = section;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::erase(const int index) {
|
||||||
|
bool erased{false};
|
||||||
|
if (this->elements.find(index) != this->elements.end()) {
|
||||||
|
this->elements.erase(index);
|
||||||
|
erased = true;
|
||||||
|
} else if (this->sections.find(index) != this->sections.end()) {
|
||||||
|
this->sections.erase(index);
|
||||||
|
erased = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!erased) {
|
||||||
|
throw std::out_of_range("Index out of range");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::erase(const HTMLSection& section) {
|
||||||
|
for (int i{0}; i < this->size(); i++) {
|
||||||
|
const auto it = this->getHTMLSections().at(i);
|
||||||
|
|
||||||
|
if (it.get() == section.get()) {
|
||||||
|
this->erase(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::out_of_range("Section not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::erase(const HTMLElement& element) {
|
||||||
|
for (int i{0}; i < this->size(); i++) {
|
||||||
|
const auto it = this->getHTMLElements().at(i);
|
||||||
|
|
||||||
|
if (it.get() == element.get()) {
|
||||||
|
this->erase(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::out_of_range("Element not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::insert(const int index, const HTMLElement& element) {
|
||||||
|
if (this->sections.find(index) != this->sections.end()) {
|
||||||
|
throw std::invalid_argument("Index already occupied by a section");
|
||||||
|
} else {
|
||||||
|
this->elements[index] = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->index = std::max(this->index, index) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::insert(const int index, const HTMLSection& section) {
|
||||||
|
this->sections[index] = section;
|
||||||
|
this->index = std::max(this->index, index) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int docpp::HTML::HTMLSection::find(const HTMLElement& element) {
|
||||||
|
for (int i{0}; i < this->size(); i++) {
|
||||||
|
const auto it = this->getHTMLElements().at(i);
|
||||||
|
|
||||||
|
if (it.get() == element.get()) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::out_of_range("Element not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
int docpp::HTML::HTMLSection::find(const HTMLSection& section) {
|
||||||
|
for (int i{0}; i < this->size(); i++) {
|
||||||
|
const auto it = this->getHTMLSections().at(i);
|
||||||
|
|
||||||
|
if (it.get() == section.get()) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::out_of_range("Section not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
int docpp::HTML::HTMLSection::size() const {
|
||||||
|
return this->index;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<docpp::HTML::HTMLElement> docpp::HTML::HTMLSection::getHTMLElements() {
|
||||||
|
std::vector<docpp::HTML::HTMLElement> ret{};
|
||||||
|
ret.reserve(this->index);
|
||||||
|
for (int i{0}; i < this->index; i++) {
|
||||||
|
if (this->elements.find(i) != this->elements.end()) {
|
||||||
|
ret.push_back(this->elements.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::move(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<docpp::HTML::HTMLSection> docpp::HTML::HTMLSection::getHTMLSections() {
|
||||||
|
std::vector<docpp::HTML::HTMLSection> ret{};
|
||||||
|
ret.reserve(this->index);
|
||||||
|
|
||||||
|
for (int i{0}; i < this->index; i++) {
|
||||||
|
if (this->sections.find(i) != this->sections.end()) {
|
||||||
|
ret.push_back(this->sections.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string docpp::HTML::HTMLSection::get(const int formatting) const {
|
||||||
|
std::string ret{};
|
||||||
|
|
||||||
|
ret += "<" + this->tag;
|
||||||
|
|
||||||
|
for (const auto& it : this->properties.get()) {
|
||||||
|
if (!it.getKey().compare("")) continue;
|
||||||
|
if (!it.getValue().compare("")) continue;
|
||||||
|
|
||||||
|
ret += " " + it.getKey() + "=\"" + it.getValue() + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += ">";
|
||||||
|
|
||||||
|
if (formatting == docpp::HTML::FORMATTING_PRETTY) {
|
||||||
|
ret += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i{0}; i < this->index; i++) {
|
||||||
|
if (this->elements.find(i) != this->elements.end()) {
|
||||||
|
ret += this->elements.at(i).get(formatting);
|
||||||
|
} else if (this->sections.find(i) != this->sections.end()) {
|
||||||
|
ret += this->sections.at(i).get(formatting);
|
||||||
|
|
||||||
|
if (formatting == docpp::HTML::FORMATTING_PRETTY) {
|
||||||
|
ret += "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += "</" + this->tag + ">";
|
||||||
|
|
||||||
|
return std::move(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::swap(const int index1, const int index2) {
|
||||||
|
if (this->elements.find(index1) != this->elements.end() && this->elements.find(index2) != this->elements.end()) {
|
||||||
|
std::swap(this->elements[index1], this->elements[index2]);
|
||||||
|
} else if (this->sections.find(index1) != this->sections.end() && this->sections.find(index2) != this->sections.end()) {
|
||||||
|
std::swap(this->sections[index1], this->sections[index2]);
|
||||||
|
} else {
|
||||||
|
throw std::out_of_range("Index out of range");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::swap(const HTMLElement& element1, const HTMLElement& element2) {
|
||||||
|
this->swap(this->find(element1), this->find(element2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLSection::swap(const HTMLSection& section1, const HTMLSection& section2) {
|
||||||
|
this->swap(this->find(section1), this->find(section2));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string docpp::HTML::HTMLDocument::get(const int formatting) const {
|
||||||
|
return this->doctype + (formatting == FORMATTING_PRETTY ? "\n" : "") + this->document.get(formatting);
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLDocument::set(const docpp::HTML::HTMLSection& document) {
|
||||||
|
this->document = document;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::HTML::HTMLDocument::setDoctype(const std::string& doctype) {
|
||||||
|
this->doctype = doctype;
|
||||||
|
}
|
||||||
|
|
||||||
|
docpp::HTML::HTMLDocument::HTMLDocument(const docpp::HTML::HTMLSection& document, const std::string& doctype) {
|
||||||
|
this->set(document);
|
||||||
|
this->setDoctype(doctype);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string docpp::HTML::HTMLDocument::getDoctype() const {
|
||||||
|
return this->doctype;
|
||||||
|
}
|
||||||
|
|
||||||
|
docpp::CSS::CSSProperty::CSSProperty(const std::string& key, const std::string& value) {
|
||||||
|
this->set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
docpp::CSS::CSSProperty::CSSProperty(const std::pair<std::string, std::string>& property) {
|
||||||
|
this->set(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string docpp::CSS::CSSProperty::getKey() const {
|
||||||
|
return this->property.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string docpp::CSS::CSSProperty::getValue() const {
|
||||||
|
return this->property.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> docpp::CSS::CSSProperty::get() const {
|
||||||
|
return this->property;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSProperty::setKey(const std::string& key) {
|
||||||
|
this->property.first = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSProperty::setValue(const std::string& value) {
|
||||||
|
this->property.second = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSProperty::set(const std::pair<std::string, std::string>& property) {
|
||||||
|
this->property = property;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSProperty::set(const std::string& key, const std::string& value) {
|
||||||
|
this->property = std::make_pair(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
docpp::CSS::CSSElement::CSSElement(const std::string& tag, const std::vector<CSSProperty>& properties) {
|
||||||
|
this->set(tag, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
docpp::CSS::CSSElement::CSSElement(const std::pair<std::string, std::vector<CSSProperty>>& element) {
|
||||||
|
this->set(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSElement::set(const std::string& tag, const std::vector<CSSProperty>& properties) {
|
||||||
|
this->element.first = tag;
|
||||||
|
this->element.second = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSElement::set(const std::pair<std::string, std::vector<CSSProperty>>& element) {
|
||||||
|
this->element = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSElement::push_back(const CSSProperty& property) {
|
||||||
|
this->element.second.push_back(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string docpp::CSS::CSSElement::get(const int formatting) const {
|
||||||
|
std::string ret{};
|
||||||
|
|
||||||
|
if (this->element.first.compare("")) {
|
||||||
|
ret += this->element.first + " {";
|
||||||
|
|
||||||
|
if (formatting == docpp::CSS::FORMATTING_PRETTY) {
|
||||||
|
ret += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& it : this->element.second) {
|
||||||
|
if (!it.getKey().compare("")) continue;
|
||||||
|
if (!it.getValue().compare("")) continue;
|
||||||
|
|
||||||
|
ret += it.getKey() + ": " + it.getValue() + ";";
|
||||||
|
|
||||||
|
if (formatting == docpp::CSS::FORMATTING_PRETTY) {
|
||||||
|
ret += "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += "}";
|
||||||
|
|
||||||
|
if (formatting == docpp::CSS::FORMATTING_PRETTY) {
|
||||||
|
ret += "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string docpp::CSS::CSSElement::getTag() const {
|
||||||
|
return this->element.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<docpp::CSS::CSSProperty> docpp::CSS::CSSElement::getProperties() const {
|
||||||
|
return this->element.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
docpp::CSS::CSSStylesheet::CSSStylesheet(const std::vector<CSSElement>& elements) {
|
||||||
|
this->set(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSStylesheet::set(const std::vector<CSSElement>& elements) {
|
||||||
|
this->elements = elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSStylesheet::push_front(const CSSElement& element) {
|
||||||
|
this->elements.insert(this->elements.begin(), element);
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSStylesheet::push_back(const CSSElement& element) {
|
||||||
|
this->elements.push_back(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSStylesheet::insert(const int index, const CSSElement& element) {
|
||||||
|
if (index < 0 || index >= this->elements.size()) {
|
||||||
|
throw std::out_of_range("Index out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
this->elements.insert(this->elements.begin() + index, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSStylesheet::erase(const int index) {
|
||||||
|
if (index < 0 || index >= this->elements.size()) {
|
||||||
|
throw std::out_of_range("Index out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
this->elements.erase(this->elements.begin() + index);
|
||||||
|
}
|
||||||
|
|
||||||
|
int docpp::CSS::CSSStylesheet::find(const CSSElement& element) {
|
||||||
|
for (int i{0}; i < this->elements.size(); i++) {
|
||||||
|
if (this->elements.at(i).get() == element.get()) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::out_of_range("Element not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
int docpp::CSS::CSSStylesheet::size() const {
|
||||||
|
return this->elements.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSStylesheet::swap(const int index1, const int index2) {
|
||||||
|
if (index1 < 0 || index1 >= this->elements.size() || index2 < 0 || index2 >= this->elements.size()) {
|
||||||
|
throw std::out_of_range("Index out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::swap(this->elements[index1], this->elements[index2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void docpp::CSS::CSSStylesheet::swap(const CSSElement& element1, const CSSElement& element2) {
|
||||||
|
this->swap(this->find(element1), this->find(element2));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<docpp::CSS::CSSElement> docpp::CSS::CSSStylesheet::getElements() const {
|
||||||
|
return this->elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string docpp::CSS::CSSStylesheet::get(const int formatting) const {
|
||||||
|
std::string ret{};
|
||||||
|
|
||||||
|
for (const auto& it : this->elements) {
|
||||||
|
ret += it.get(formatting);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(ret);
|
||||||
|
}
|
200
tests/test.cpp
Normal file
200
tests/test.cpp
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
#include <string>
|
||||||
|
#include <include/docpp.hpp>
|
||||||
|
#include <src/docpp.cpp>
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test cases for the DuvaHTML namespace.
|
||||||
|
*/
|
||||||
|
SCENARIO("Test HTML", "[HTML]") {
|
||||||
|
auto test1 = []() {
|
||||||
|
docpp::HTML::HTMLDocument doc{};
|
||||||
|
docpp::HTML::HTMLSection html(docpp::HTML::SECTION_HTML, {});
|
||||||
|
|
||||||
|
docpp::HTML::HTMLSection head(docpp::HTML::SECTION_HEAD, {});
|
||||||
|
docpp::HTML::HTMLSection body(docpp::HTML::SECTION_BODY, {});
|
||||||
|
docpp::HTML::HTMLSection div(docpp::HTML::SECTION_DIV, {});
|
||||||
|
docpp::HTML::HTMLSection footer(docpp::HTML::SECTION_FOOTER, {});
|
||||||
|
|
||||||
|
head.push_back(docpp::HTML::HTMLElement("title", {}, "Test Title"));
|
||||||
|
body.push_back(docpp::HTML::HTMLElement("h1", {}, "Test Header"));
|
||||||
|
body.push_back(docpp::HTML::HTMLElement("p", {}, "Test Paragraph"));
|
||||||
|
|
||||||
|
docpp::HTML::HTMLElementProperties prop{};
|
||||||
|
prop.push_back(docpp::HTML::HTMLProperty(std::pair<std::string, std::string>("id", "test_id")));
|
||||||
|
|
||||||
|
body.push_back(docpp::HTML::HTMLElement("p", prop, "Test Paragraph With ID"));
|
||||||
|
|
||||||
|
div.push_back(docpp::HTML::HTMLElement("p", {}, "Test Paragraph In Div"));
|
||||||
|
body.push_back(div);
|
||||||
|
|
||||||
|
prop.push_back(docpp::HTML::HTMLProperty(std::pair<std::string, std::string>("class", "class1 class2 class3")));
|
||||||
|
|
||||||
|
body.push_back(docpp::HTML::HTMLElement("p", prop, "Test Paragraph With ID And Class"));
|
||||||
|
|
||||||
|
html.push_back(head);
|
||||||
|
html.push_back(body);
|
||||||
|
html.push_back(footer);
|
||||||
|
|
||||||
|
doc.set(html);
|
||||||
|
|
||||||
|
const std::string expected_html{"<!DOCTYPE html><html><head><title>Test Title</title></head><body><h1>Test Header</h1><p>Test Paragraph</p><p id=\"test_id\">Test Paragraph With ID</p><div><p>Test Paragraph In Div</p></div><p id=\"test_id\" class=\"class1 class2 class3\">Test Paragraph With ID And Class</p></body><footer></footer></html>"};
|
||||||
|
|
||||||
|
REQUIRE(doc.get() == expected_html);
|
||||||
|
REQUIRE(doc.get(docpp::HTML::FORMATTING_PRETTY) == "<!DOCTYPE html>\n<html>\n<head>\n<title>Test Title</title>\n</head>\n<body>\n<h1>Test Header</h1>\n<p>Test Paragraph</p>\n<p id=\"test_id\">Test Paragraph With ID</p>\n<div>\n<p>Test Paragraph In Div</p>\n</div>\n<p id=\"test_id\" class=\"class1 class2 class3\">Test Paragraph With ID And Class</p>\n</body>\n<footer>\n</footer>\n</html>");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test2 = []() {
|
||||||
|
docpp::HTML::HTMLSection section(docpp::HTML::SECTION_HTML, {});
|
||||||
|
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 1"));
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 2"));
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 3"));
|
||||||
|
|
||||||
|
section.erase(docpp::HTML::HTMLElement("p", {}, "Test 2"));
|
||||||
|
|
||||||
|
REQUIRE(section.get() == "<html><p>Test 1</p><p>Test 3</p></html>");
|
||||||
|
REQUIRE(section.get(docpp::HTML::FORMATTING_PRETTY) == "<html>\n<p>Test 1</p>\n<p>Test 3</p>\n</html>");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test3 = []() {
|
||||||
|
docpp::HTML::HTMLSection section = docpp::HTML::HTMLSection(docpp::HTML::SECTION_HTML, {});
|
||||||
|
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 1"));
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 2"));
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 3"));
|
||||||
|
|
||||||
|
int pos = section.find(docpp::HTML::HTMLElement("p", {}, "Test 2"));
|
||||||
|
section.insert(pos, docpp::HTML::HTMLElement("p", {}, "Test 2.5"));
|
||||||
|
|
||||||
|
REQUIRE(section.get() == "<html><p>Test 1</p><p>Test 2.5</p><p>Test 3</p></html>");
|
||||||
|
REQUIRE(section.get(docpp::HTML::FORMATTING_PRETTY) == "<html>\n<p>Test 1</p>\n<p>Test 2.5</p>\n<p>Test 3</p>\n</html>");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test4 = []() {
|
||||||
|
docpp::HTML::HTMLSection section = docpp::HTML::HTMLSection(docpp::HTML::SECTION_HTML, {});
|
||||||
|
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 1"));
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 2"));
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 3"));
|
||||||
|
|
||||||
|
int pos = section.find(docpp::HTML::HTMLElement("p", {}, "Test 2"));
|
||||||
|
|
||||||
|
section.erase(pos);
|
||||||
|
|
||||||
|
REQUIRE(section.get() == "<html><p>Test 1</p><p>Test 3</p></html>");
|
||||||
|
REQUIRE(section.get(docpp::HTML::FORMATTING_PRETTY) == "<html>\n<p>Test 1</p>\n<p>Test 3</p>\n</html>");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test5 = []() {
|
||||||
|
docpp::HTML::HTMLSection section = docpp::HTML::HTMLSection(docpp::HTML::SECTION_HTML, {});
|
||||||
|
docpp::HTML::HTMLSection subsection(docpp::HTML::SECTION_DIV, {});
|
||||||
|
|
||||||
|
subsection.push_back(docpp::HTML::HTMLElement("p", {}, "Test 1"));
|
||||||
|
|
||||||
|
docpp::HTML::HTMLSection subsection2(docpp::HTML::SECTION_DIV, {});
|
||||||
|
subsection2.push_back(docpp::HTML::HTMLElement("p", {}, "Test 2"));
|
||||||
|
|
||||||
|
subsection.push_back(subsection2);
|
||||||
|
|
||||||
|
section.push_back(subsection);
|
||||||
|
|
||||||
|
docpp::HTML::HTMLDocument doc{};
|
||||||
|
doc.set(section);
|
||||||
|
|
||||||
|
REQUIRE(doc.get() == "<!DOCTYPE html><html><div><p>Test 1</p><div><p>Test 2</p></div></div></html>");
|
||||||
|
REQUIRE(doc.get(docpp::HTML::FORMATTING_PRETTY) == "<!DOCTYPE html>\n<html>\n<div>\n<p>Test 1</p>\n<div>\n<p>Test 2</p>\n</div>\n</div>\n</html>");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test6 = []() {
|
||||||
|
docpp::CSS::CSSStylesheet css{};
|
||||||
|
docpp::CSS::CSSElement element{"p", {{"color", "red"}, {"font-size", "16px"}, {"font-family", "Arial"}}};
|
||||||
|
|
||||||
|
css.push_back(element);
|
||||||
|
|
||||||
|
REQUIRE(css.get() == "p {color: red;font-size: 16px;font-family: Arial;}");
|
||||||
|
REQUIRE(css.get(docpp::CSS::FORMATTING_PRETTY) == "p {\ncolor: red;\nfont-size: 16px;\nfont-family: Arial;\n}\n");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test7 = []() {
|
||||||
|
docpp::CSS::CSSStylesheet css = docpp::CSS::CSSStylesheet{};
|
||||||
|
docpp::CSS::CSSElement element = docpp::CSS::CSSElement{"p", {{"color", "red"}, {"font-size", "16px"}, {"font-family", "Arial"}}};
|
||||||
|
docpp::CSS::CSSElement element2{"div", {{"color", "blue"}, {"font-size", "12px"}, {"font-family", "Arial"}}};
|
||||||
|
|
||||||
|
css.push_back(element);
|
||||||
|
css.push_front(element2);
|
||||||
|
|
||||||
|
REQUIRE(css.get() == "div {color: blue;font-size: 12px;font-family: Arial;}p {color: red;font-size: 16px;font-family: Arial;}");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test8 = []() {
|
||||||
|
docpp::CSS::CSSStylesheet css = docpp::CSS::CSSStylesheet{};
|
||||||
|
docpp::CSS::CSSElement element = docpp::CSS::CSSElement{"p", {{"color", "red"}, {"font-size", "16px"}, {"font-family", "Arial"}}};
|
||||||
|
docpp::CSS::CSSElement element2{"div", {{"color", "blue"}, {"font-size", "12px"}, {"font-family", "Arial"}}};
|
||||||
|
|
||||||
|
css.push_back(element);
|
||||||
|
css.push_front(element2);
|
||||||
|
|
||||||
|
css.erase(css.find(element2));
|
||||||
|
|
||||||
|
REQUIRE(css.get() == "p {color: red;font-size: 16px;font-family: Arial;}");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test9 = []() {
|
||||||
|
docpp::CSS::CSSStylesheet css = docpp::CSS::CSSStylesheet{};
|
||||||
|
docpp::CSS::CSSElement element = docpp::CSS::CSSElement{"p", {{"color", "red"}, {"font-size", "16px"}, {"font-family", "Arial"}}};
|
||||||
|
docpp::CSS::CSSElement element2{"div", {{"color", "blue"}, {"font-size", "12px"}, {"font-family", "Arial"}}};
|
||||||
|
|
||||||
|
css.push_back(element);
|
||||||
|
css.push_front(element2);
|
||||||
|
|
||||||
|
css.erase(css.find(element2));
|
||||||
|
css.push_front(element2);
|
||||||
|
|
||||||
|
css.swap(css.find(element), css.find(element2));
|
||||||
|
|
||||||
|
REQUIRE(css.get() == "p {color: red;font-size: 16px;font-family: Arial;}div {color: blue;font-size: 12px;font-family: Arial;}");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test10 = []() {
|
||||||
|
docpp::HTML::HTMLSection section = docpp::HTML::HTMLSection(docpp::HTML::SECTION_HTML, {});
|
||||||
|
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 1"));
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 2"));
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 3"));
|
||||||
|
|
||||||
|
section.swap(section.find(docpp::HTML::HTMLElement("p", {}, "Test 2")), section.find(docpp::HTML::HTMLElement("p", {}, "Test 3")));
|
||||||
|
|
||||||
|
REQUIRE(section.get() == "<html><p>Test 1</p><p>Test 3</p><p>Test 2</p></html>");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test11 = []() {
|
||||||
|
docpp::HTML::HTMLSection section = docpp::HTML::HTMLSection(docpp::HTML::SECTION_HTML, {});
|
||||||
|
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 1"));
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 2"));
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 3"));
|
||||||
|
|
||||||
|
section.swap(docpp::HTML::HTMLElement("p", {}, "Test 2"), docpp::HTML::HTMLElement("p", {}, "Test 3"));
|
||||||
|
|
||||||
|
REQUIRE(section.get() == "<html><p>Test 1</p><p>Test 3</p><p>Test 2</p></html>");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test12 = []() {
|
||||||
|
docpp::HTML::HTMLSection section = docpp::HTML::HTMLSection(docpp::HTML::SECTION_HTML, {});
|
||||||
|
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 1"));
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 2"));
|
||||||
|
section.push_back(docpp::HTML::HTMLElement("p", {}, "Test 3"));
|
||||||
|
|
||||||
|
section.push_front(docpp::HTML::HTMLElement("p", {}, "Test 0"));
|
||||||
|
|
||||||
|
REQUIRE(section.get() == "<html><p>Test 0</p><p>Test 1</p><p>Test 2</p><p>Test 3</p></html>");
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<void (*)()> tests{test1, test2, test3, test4, test5, test6, test7, test8, test9, test10, test11, test12};
|
||||||
|
|
||||||
|
for (const auto& test : tests) {
|
||||||
|
test();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue