diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | .gitmodules | 5 | ||||
-rw-r--r-- | CMakeLists.txt | 13 | ||||
-rw-r--r-- | Exception.cpp | 34 | ||||
-rw-r--r-- | Exception.h | 17 | ||||
-rw-r--r-- | File.cpp | 9 | ||||
-rw-r--r-- | File.h | 25 | ||||
-rw-r--r-- | FileReader.cpp | 32 | ||||
-rw-r--r-- | FileReader.h | 26 | ||||
-rw-r--r-- | HTTPFile.cpp | 28 | ||||
-rw-r--r-- | HTTPFile.h | 29 | ||||
-rw-r--r-- | LocalFile.cpp | 36 | ||||
-rw-r--r-- | LocalFile.h | 29 | ||||
-rw-r--r-- | docs/class-diag.puml | 26 | ||||
-rw-r--r-- | lazy.mk | 33 | ||||
m--------- | lib/cpr | 0 | ||||
-rw-r--r-- | main.cpp | 12 |
17 files changed, 321 insertions, 35 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7194ea7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.cache +build diff --git a/.gitmodules b/.gitmodules index 8c7dbb0..b260b07 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +1,8 @@ [submodule "lib/SDL"] path = lib/SDL url = https://github.com/libsdl-org/SDL - branch = release-2.30.x + shallow = true +[submodule "lib/cpr"] + path = lib/cpr + url = https://github.com/libcpr/cpr shallow = true diff --git a/CMakeLists.txt b/CMakeLists.txt index e4f76a5..67c5607 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,22 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_EXPORT_COMPILE_COMMANDS 1) set(CMAKE_BUILD_TYPE Debug) +find_package(SDL2 REQUIRED) +find_package(cpr REQUIRED) + project(main C CXX) add_executable(main main.cpp + File.cpp + HTTPFile.cpp + LocalFile.cpp + Exception.cpp + FileReader.cpp +) + +target_link_libraries(main + SDL2 + cpr ) diff --git a/Exception.cpp b/Exception.cpp new file mode 100644 index 0000000..d9765da --- /dev/null +++ b/Exception.cpp @@ -0,0 +1,34 @@ +#include <cstdarg> +#include <cstdio> +#include <cstdlib> + +#include "Exception.h" + +Exception::~Exception() { + if (error != NULL) + free(error); +} + +const char * Exception::what() { + return error; +} + +void Exception::va_format(va_list args, const char * fmt) { + va_list args_copy; + va_copy(args_copy, args); + + size_t sz = vsnprintf(NULL, 0, fmt, args_copy) + 1; + if (error != NULL) free(error); + error = (char *) malloc(sz); + va_end(args_copy); + + vsnprintf(error, sz, fmt, args); +} + +Exception::Exception(const char * fmt, ...) { + va_list args; + va_start(args, fmt); + va_format(args, fmt); + va_end(args); +} + diff --git a/Exception.h b/Exception.h new file mode 100644 index 0000000..31c517b --- /dev/null +++ b/Exception.h @@ -0,0 +1,17 @@ +#pragma once + +#include <cstdarg> +#include <exception> + +class Exception : public std::exception { +public: + Exception(const char * fmt, ...); + virtual ~Exception(); + virtual const char * what(); + +protected: + Exception() = default; + void va_format(va_list args, const char * fmt); + char * error = NULL; +}; + diff --git a/File.cpp b/File.cpp new file mode 100644 index 0000000..bc31b1c --- /dev/null +++ b/File.cpp @@ -0,0 +1,9 @@ +#include <string> + +#include "File.h" +#include "FileReader.h" + +File::File(const std::string protocol) { + FileReader::assign(protocol, this); +} + @@ -0,0 +1,25 @@ +#pragma once + +class FileReader; + +#include <string> + +class File { +protected: + virtual void open(const std::string url) = 0; +public: + virtual void close() = 0; + virtual const std::string read() = 0; + +public: + virtual ~File() = default; + +protected: + File() = default; + virtual File * clone() const = 0; + +protected: + File(const std::string protocol); + friend FileReader; +}; + diff --git a/FileReader.cpp b/FileReader.cpp new file mode 100644 index 0000000..503aee5 --- /dev/null +++ b/FileReader.cpp @@ -0,0 +1,32 @@ +#include "FileReader.h" +#include "File.h" + +File & FileReader::open(const std::string url) { + File * reader = find_reader(url)->clone(); + reader->open(url); + return *reader; +} + +void FileReader::assign(const std::string type, const File * node) { + static FactoryMap & map = get_map(); + map[type] = node; +} + +FactoryMap & FileReader::get_map() { + static FactoryMap map; + return map; +} + +const File * FileReader::find_reader(const std::string type) { + static FactoryMap & map = get_map(); + + // try to find protocol by prefix + for (auto item : map) { + if (!type.starts_with(item.first)) continue; + return item.second; + } + + // fallback is local file + return map.find("file://")->second; +} + diff --git a/FileReader.h b/FileReader.h new file mode 100644 index 0000000..b2d8b37 --- /dev/null +++ b/FileReader.h @@ -0,0 +1,26 @@ +#pragma once + +#include <string> +#include <map> + +#include "File.h" + +using FactoryMap = std::map<std::string, const File *>; + +class FileReader { +public: + static File & open(const std::string url); + +private: + FileReader() = default; + virtual ~FileReader() = default; + +private: + static void assign(const std::string type, const File * node); + static FactoryMap & get_map(); + static const File * find_reader(const std::string type); + +private: + friend File; +}; + diff --git a/HTTPFile.cpp b/HTTPFile.cpp new file mode 100644 index 0000000..a1d9185 --- /dev/null +++ b/HTTPFile.cpp @@ -0,0 +1,28 @@ +#include <cpr/cpr.h> + +#include "HTTPFile.h" +#include "Exception.h" + +HTTPFile HTTPFile::instance(protocol); + +void HTTPFile::open(const std::string url) { + _res = cpr::Get(cpr::Url{url}); + +} + +void HTTPFile::close() { } + +const std::string HTTPFile::read() { + return _res.text.data(); +} + +HTTPFile::~HTTPFile() { + close(); +} + +HTTPFile * HTTPFile::clone() const { + return new HTTPFile(this); +} + +HTTPFile::HTTPFile(const HTTPFile *) : File() { } + diff --git a/HTTPFile.h b/HTTPFile.h new file mode 100644 index 0000000..94207f2 --- /dev/null +++ b/HTTPFile.h @@ -0,0 +1,29 @@ +#pragma once + +#include <cpr/cpr.h> + +#include "File.h" + +class HTTPFile : File { +protected: + virtual void open(const std::string url); +public: + virtual void close(); + virtual const std::string read(); + +public: + virtual ~HTTPFile(); + +private: + HTTPFile(const HTTPFile *); + virtual HTTPFile * clone() const; + +private: + using File::File; + constexpr static const std::string protocol = "https://"; + static HTTPFile instance; + +private: + cpr::Response _res; +}; + diff --git a/LocalFile.cpp b/LocalFile.cpp new file mode 100644 index 0000000..2f4140d --- /dev/null +++ b/LocalFile.cpp @@ -0,0 +1,36 @@ +#include <cstdio> +#include <iterator> + +#include "LocalFile.h" +#include "Exception.h" + +LocalFile LocalFile::instance(protocol); + +void LocalFile::open(const std::string url) { + std::string path = url; + if (path.starts_with(protocol)) + path = path.substr(protocol.size()); + + std::ifstream _file(path); + if (!_file.is_open()) + throw Exception("Cannot open file://%s\n", path.c_str()); +} + +void LocalFile::close() { + if (_file.is_open()) _file.close(); +} + +const std::string LocalFile::read() { + return std::string(std::istreambuf_iterator<char>(_file), std::istreambuf_iterator<char>()); +} + +LocalFile::~LocalFile() { + close(); +} + +LocalFile * LocalFile::clone() const { + return new LocalFile(this); +} + +LocalFile::LocalFile(const LocalFile *) : File() { } + diff --git a/LocalFile.h b/LocalFile.h new file mode 100644 index 0000000..9529053 --- /dev/null +++ b/LocalFile.h @@ -0,0 +1,29 @@ +#pragma once + +#include <fstream> + +#include "File.h" + +class LocalFile : File { +protected: + virtual void open(const std::string url); +public: + virtual void close(); + virtual const std::string read(); + +public: + virtual ~LocalFile(); + +private: + LocalFile(const LocalFile *); + virtual LocalFile * clone() const; + +private: + using File::File; + constexpr static const std::string protocol = "file://"; + static LocalFile instance; + +private: + std::ifstream _file; +}; + diff --git a/docs/class-diag.puml b/docs/class-diag.puml index 17e2b59..6de5851 100644 --- a/docs/class-diag.puml +++ b/docs/class-diag.puml @@ -3,11 +3,17 @@ !theme plain skinparam linetype ortho skinparam classAttributeIconSize 0 +' skinparam packageStyle rectangle + +class main as "main()" +hide main circle +hide main members class FileReader { + open(path) + read() + close() + - protocol } class LocalFile @@ -31,6 +37,20 @@ class YellowTileBehavior class Artist +class ArtistDeserializer +class CanvasDeserializer + +package CPR { } +package SDL2 { } + +interface Parser { + + parse(f: FileReader) +} + +class CSVParser +class XMLParser +class TXTParser + FileReader <|-- LocalFile FileReader <|-- HTTPFile Canvas "1" -> "*" Tile @@ -39,6 +59,12 @@ TileBehavior <|-- RedTileBehavior TileBehavior <|-- BlueTileBehavior TileBehavior <|-- YellowTileBehavior +Parser <|-- CSVParser +Parser <|-- TXTParser +Parser <|-- XMLParser + +HTTPFile ..> CPR + TileFactory --> Tile : create diff --git a/lazy.mk b/lazy.mk deleted file mode 100644 index a591fd5..0000000 --- a/lazy.mk +++ /dev/null @@ -1,33 +0,0 @@ -# NOTE: CMAKE IS THE PRIMARY BUILD SYSTEM FOR THIS PROJECT. THIS FILE IS -# PROVIDED PURELY FOR CONVENIENCE, AND SHOULD NOT BECOME AN ESSENTIAL PART OF -# THE BUILD SYSTEM! - -BUILD_DIR ?= build -TARGET ?= $(BUILD_DIR)/main - -# always generate fresh build rules when cmake is re-run -CMFLAGS += --fresh -# make cmake shut up -CMFLAGS += --log-level WARNING -CMFLAGS += -Wno-deprecated - -.PHONY: FORCE - -all: FORCE $(TARGET) - -$(BUILD_DIR)/build.ninja: CMakeLists.txt - @mkdir -p $(BUILD_DIR) - @cmake -B $(BUILD_DIR) -G Ninja $(CMFLAGS) - -$(TARGET): $(BUILD_DIR)/build.ninja FORCE - @ninja -C $(BUILD_DIR) - -clean: FORCE - $(RM) -r $(BUILD_DIR) - -# Forward any unknown targets to Ninja -ifneq ($(MAKECMDGOALS),) -%:: - @ninja -C $(BUILD_DIR) $@ -endif - diff --git a/lib/cpr b/lib/cpr new file mode 160000 +Subproject 99f044e386115194485ce77e326c31e9bd80bb0 @@ -1,4 +1,14 @@ -int main() { +#include <cstdio> + +#include "FileReader.h" + +int main(int argc, char** argv) { + for (int i = 1; i < argc; i++) { + File & r = FileReader::open(argv[i]); + printf("-- %s --\n%s\n", argv[i], r.read().c_str()); + r.close(); + } + return 0; } |