diff --git a/OdbDesignLib/App/BasicRequestAuthentication.cpp b/OdbDesignLib/App/BasicRequestAuthentication.cpp index aa1ff4cc..c5668687 100644 --- a/OdbDesignLib/App/BasicRequestAuthentication.cpp +++ b/OdbDesignLib/App/BasicRequestAuthentication.cpp @@ -1,6 +1,7 @@ #include "BasicRequestAuthentication.h" #include #include "macros.h" +#include using namespace Utils; @@ -17,21 +18,20 @@ namespace Odb::Lib::App if (resp.code != crow::status::OK) { const auto& authHeader = req.get_header_value(AUTHORIZATION_HEADER_NAME); - if (authHeader.empty()) return crow::response(401, "Unauthorized"); + if (authHeader.empty()) return crow::response(crow::status::UNAUTHORIZED, "Unauthorized"); auto authValue = authHeader.substr(6); - if (authValue.empty()) return crow::response(401, "Unauthorized"); + if (authValue.empty()) return crow::response(crow::status::UNAUTHORIZED, "Unauthorized"); auto authValueDecoded = crow::utility::base64decode(authValue, authValue.size()); - if (authValueDecoded.empty()) return crow::response(401, "Unauthorized"); + if (authValueDecoded.empty()) return crow::response(crow::status::UNAUTHORIZED, "Unauthorized"); auto seperatorPos = authValueDecoded.find(':'); - if (seperatorPos == std::string::npos) return crow::response(401, "Unauthorized"); + if (seperatorPos == std::string::npos) return crow::response(crow::status::UNAUTHORIZED, "Unauthorized"); auto username = authValueDecoded.substr(0, seperatorPos); auto password = authValueDecoded.substr(seperatorPos + 1); - //if (! VerifyCredentials(username, password)) return crow::response(403, "Invalid username or password"); resp = VerifyCredentials(username, password); } return resp; @@ -58,10 +58,10 @@ namespace Odb::Lib::App if (username != validUsername || password != validPassword) { - return crow::response(403, "Invalid username or password"); + return crow::response(crow::status::FORBIDDEN, "Invalid username or password"); } // 200 Authorized! - return crow::response(200, "Authorized"); + return crow::response(crow::status::OK, "Authorized"); } } \ No newline at end of file diff --git a/OdbDesignLib/App/DesignCache.cpp b/OdbDesignLib/App/DesignCache.cpp index cdcfc992..343825a4 100644 --- a/OdbDesignLib/App/DesignCache.cpp +++ b/OdbDesignLib/App/DesignCache.cpp @@ -4,6 +4,7 @@ #include #include #include +#include using namespace Utils; using namespace std::filesystem; diff --git a/OdbDesignLib/App/OdbAppBase.cpp b/OdbDesignLib/App/OdbAppBase.cpp index 98fd9ec5..bae7f6f8 100644 --- a/OdbDesignLib/App/OdbAppBase.cpp +++ b/OdbDesignLib/App/OdbAppBase.cpp @@ -1,7 +1,9 @@ #include "OdbServerAppBase.h" #include "Logger.h" +#include using namespace Utils; +using namespace std::filesystem; namespace Odb::Lib::App { @@ -46,17 +48,27 @@ namespace Odb::Lib::App try { auto pFileArchive = - designs().GetDesign(args().loadDesign()); - //designs().GetFileArchive(args().loadDesign()); + //designs().GetDesign(args().loadDesign()); + designs().GetFileArchive(args().loadDesign()); if (pFileArchive == nullptr) { logerror("Failed to load design specified in arguments \"" + args().loadDesign() + "\""); return Utils::ExitCode::FailedInitLoadDesign; } + else + { + //std::string filename = + pFileArchive->SaveFileModel(".", "notused"); + } + } + catch (filesystem_error& fe) + { + logexception(fe); + logerror("filesystem_error: \"" + args().loadDesign() + "\" " + fe.what()); } - catch (std::exception&) + catch (std::exception& e) { - //logexception(e); + logexception(e); logerror("Failed to load design specified in arguments \"" + args().loadDesign() + "\""); return Utils::ExitCode::FailedInitLoadDesign; } diff --git a/OdbDesignLib/App/RequestAuthenticationBase.cpp b/OdbDesignLib/App/RequestAuthenticationBase.cpp index 6fbcd5c3..bf7eb242 100644 --- a/OdbDesignLib/App/RequestAuthenticationBase.cpp +++ b/OdbDesignLib/App/RequestAuthenticationBase.cpp @@ -16,16 +16,16 @@ namespace Odb::Lib::App if (IsDebug() && IsLocal()) { // 200 Authorized! - return crow::response(200, "Authorized"); + return crow::response(crow::status::OK, "Authorized"); } else if (m_disableAuthentication) { // 200 Authorized! - return crow::response(200, "Authorized"); + return crow::response(crow::status::OK, "Authorized"); } else { - return crow::response(401, "Unauthorized"); + return crow::response(crow::status::UNAUTHORIZED, "Unauthorized"); } } } \ No newline at end of file diff --git a/OdbDesignLib/CMakeLists.txt b/OdbDesignLib/CMakeLists.txt index 4e5968dc..fdc539eb 100644 --- a/OdbDesignLib/CMakeLists.txt +++ b/OdbDesignLib/CMakeLists.txt @@ -37,7 +37,7 @@ add_library(OdbDesign SHARED "ProtoBuf/via.pb.h" "ProtoBuf/via.pb.cc" "ProtoBuf/package.pb.h" "ProtoBuf/package.pb.cc" "FileModel/parse_error.h" "FileModel/parse_info.h" "FileModel/parse_info.cpp" "FileModel/parse_error.cpp" "FileModel/invalid_odb_error.h" "FileModel/invalid_odb_error.cpp" "ProtoBuf/common.pb.h" "ProtoBuf/common.pb.cc" "ProtoBuf/componentsfile.pb.h" "ProtoBuf/componentsfile.pb.cc" "FileModel/Design/PropertyRecord.h" "FileModel/Design/PropertyRecord.cpp" "FileModel/Design/FeaturesFile.h" "FileModel/Design/FeaturesFile.cpp" "FileModel/Design/ContourPolygon.h" "FileModel/Design/ContourPolygon.cpp" "FileModel/Design/SymbolName.h" "FileModel/Design/SymbolName.cpp" "FileModel/Design/SymbolsDirectory.h" "FileModel/Design/SymbolsDirectory.cpp" "FileModel/Design/StepHdrFile.h" "FileModel/Design/StepHdrFile.cpp" "FileModel/Design/AttributeLookupTable.h" "FileModel/Design/AttributeLookupTable.cpp" - "App/RequestAuthenticationBase.h" "App/RequestAuthenticationBase.cpp" "App/BasicRequestAuthentication.h" "App/BasicRequestAuthentication.cpp") + "App/RequestAuthenticationBase.h" "App/RequestAuthenticationBase.cpp" "App/BasicRequestAuthentication.h" "App/BasicRequestAuthentication.cpp" "FileModel/IStreamSaveable.h") # disable warning C4250: inheritance by dominance target_compile_options(OdbDesign PUBLIC diff --git a/OdbDesignLib/FileModel/Design/AttrListFile.cpp b/OdbDesignLib/FileModel/Design/AttrListFile.cpp index 4b29ffbf..eb5ab4ee 100644 --- a/OdbDesignLib/FileModel/Design/AttrListFile.cpp +++ b/OdbDesignLib/FileModel/Design/AttrListFile.cpp @@ -43,20 +43,20 @@ namespace Odb::Lib::FileModel::Design loginfo("checking for extraction..."); - std::filesystem::path featuresFilePath; - for (const std::string featuresFilename : ATTRLIST_FILENAMES) + std::filesystem::path attrListFilePath; + for (const std::string attrListFilename : ATTRLIST_FILENAMES) { - loginfo("trying attrlist file: [" + featuresFilename + "]..."); + loginfo("trying attrlist file: [" + attrListFilename + "]..."); - featuresFilePath = Utils::ArchiveExtractor::getUncompressedFilePath(m_directory, featuresFilename); - if (exists(featuresFilePath) && is_regular_file(featuresFilePath)) + attrListFilePath = Utils::ArchiveExtractor::getUncompressedFilePath(m_directory, attrListFilename); + if (exists(attrListFilePath) && is_regular_file(attrListFilePath)) { - loginfo("found attrlist file: [" + featuresFilePath.string() + "]"); + loginfo("found attrlist file: [" + attrListFilePath.string() + "]"); break; } } - m_path = featuresFilePath; + m_path = attrListFilePath; loginfo("any extraction complete, parsing data..."); @@ -203,6 +203,14 @@ namespace Odb::Lib::FileModel::Design return true; } - + bool AttrListFile::Save(std::ostream& os) + { + os << Constants::UNITS_TOKEN << " = " << m_units << std::endl; + for (const auto& kvAttribute : m_attributesByName) + { + os << kvAttribute.first << " = " << kvAttribute.second << std::endl; + } + return true; + } } // namespace Odb::Lib::FileModel::Design \ No newline at end of file diff --git a/OdbDesignLib/FileModel/Design/AttrListFile.h b/OdbDesignLib/FileModel/Design/AttrListFile.h index cbf01825..74831169 100644 --- a/OdbDesignLib/FileModel/Design/AttrListFile.h +++ b/OdbDesignLib/FileModel/Design/AttrListFile.h @@ -10,10 +10,11 @@ #include "../../IProtoBuffable.h" #include "../../ProtoBuf/attrlistfile.pb.h" #include "../../odbdesign_export.h" +#include "../IStreamSaveable.h" namespace Odb::Lib::FileModel::Design { - class ODBDESIGN_EXPORT AttrListFile : public IProtoBuffable + class ODBDESIGN_EXPORT AttrListFile : public IProtoBuffable, public IStreamSaveable { public: AttrListFile(); @@ -24,6 +25,8 @@ namespace Odb::Lib::FileModel::Design const AttributeMap& GetAttributes() const; bool Parse(std::filesystem::path directory); + // Inherited via IStreamSaveable + bool Save(std::ostream& os) override; // Inherited via IProtoBuffable std::unique_ptr to_protobuf() const override; @@ -40,7 +43,6 @@ namespace Odb::Lib::FileModel::Design inline static const auto ATTRLIST_FILENAMES = { "attrlist" }; inline static const char* OPTIONAL_ATTRIBUTES[] = { "" }; }; - } #endif //ODBDESIGN_ATTRLISTFILE_H diff --git a/OdbDesignLib/FileModel/Design/ComponentsFile.cpp b/OdbDesignLib/FileModel/Design/ComponentsFile.cpp index e22a4114..f5e91040 100644 --- a/OdbDesignLib/FileModel/Design/ComponentsFile.cpp +++ b/OdbDesignLib/FileModel/Design/ComponentsFile.cpp @@ -1,10 +1,4 @@ #include "ComponentsFile.h" -#include "ComponentsFile.h" -#include "ComponentsFile.h" -#include "ComponentsFile.h" -#include "ComponentsFile.h" -#include "ComponentsFile.h" -#include "ComponentsFile.h" #include #include #include @@ -155,6 +149,11 @@ namespace Odb::Lib::FileModel::Design } } + bool ComponentsFile::Save(std::ostream& os) + { + return true; + } + std::unique_ptr ComponentsFile::BomDescriptionRecord::to_protobuf() const { std::unique_ptr pBomDescriptionRecordMessage(new Odb::Lib::Protobuf::ComponentsFile::BomDescriptionRecord); diff --git a/OdbDesignLib/FileModel/Design/ComponentsFile.h b/OdbDesignLib/FileModel/Design/ComponentsFile.h index 100dd8a3..4b60de3c 100644 --- a/OdbDesignLib/FileModel/Design/ComponentsFile.h +++ b/OdbDesignLib/FileModel/Design/ComponentsFile.h @@ -11,17 +11,20 @@ #include "PropertyRecord.h" #include "../../ProtoBuf/componentsfile.pb.h" #include "AttributeLookupTable.h" +#include "../IStreamSaveable.h" namespace Odb::Lib::FileModel::Design { - class ODBDESIGN_EXPORT ComponentsFile : public IProtoBuffable + class ODBDESIGN_EXPORT ComponentsFile : public IProtoBuffable, public IStreamSaveable { public: ComponentsFile(); ~ComponentsFile(); bool Parse(std::filesystem::path directory); + // Inherited via IStreamSaveable + bool Save(std::ostream& os) override; std::string GetUnits() const; BoardSide GetSide() const; @@ -123,7 +126,7 @@ namespace Odb::Lib::FileModel::Design constexpr inline static const char* TOP_COMPONENTS_LAYER_NAME = "comp_+_top"; constexpr inline static const char* BOTTOM_COMPONENTS_LAYER_NAME = "comp_+_bot"; - + // Inherited via IProtoBuffable std::unique_ptr to_protobuf() const override; void from_protobuf(const Odb::Lib::Protobuf::ComponentsFile& message) override; @@ -148,9 +151,9 @@ namespace Odb::Lib::FileModel::Design const bool m_allowToepintNetNumbersOfNegative1 = true; constexpr inline static const char* COMPONENTS_FILENAMES[] = - { - "components", - "components2", + { + "components", + "components2", "components3" }; @@ -168,6 +171,7 @@ namespace Odb::Lib::FileModel::Design constexpr inline static const char* BOM_DESCR_RECORD_TOKEN_VPL_VND = "VPL_VND"; constexpr inline static const char* BOM_DESCR_RECORD_TOKEN_VPL_MPN = "VPL_MPN"; constexpr inline static const char* BOM_DESCR_RECORD_TOKEN_VND = "VND"; - constexpr inline static const char* BOM_DESCR_RECORD_TOKEN_MPN = "MPN"; -}; + constexpr inline static const char* BOM_DESCR_RECORD_TOKEN_MPN = "MPN"; + + }; } \ No newline at end of file diff --git a/OdbDesignLib/FileModel/Design/EdaDataFile.cpp b/OdbDesignLib/FileModel/Design/EdaDataFile.cpp index 4830ad6a..4be2e5c2 100644 --- a/OdbDesignLib/FileModel/Design/EdaDataFile.cpp +++ b/OdbDesignLib/FileModel/Design/EdaDataFile.cpp @@ -366,7 +366,12 @@ namespace Odb::Lib::FileModel::Design pFeatureGroupRecord->from_protobuf(featureGroupRecordMessage); m_featureGroupRecords.push_back(pFeatureGroupRecord); } - } + } + + bool EdaDataFile::Save(std::ostream& os) + { + return true; + } bool EdaDataFile::Parse(std::filesystem::path path) { diff --git a/OdbDesignLib/FileModel/Design/EdaDataFile.h b/OdbDesignLib/FileModel/Design/EdaDataFile.h index 5b8338b5..265443ad 100644 --- a/OdbDesignLib/FileModel/Design/EdaDataFile.h +++ b/OdbDesignLib/FileModel/Design/EdaDataFile.h @@ -12,11 +12,12 @@ #include "PropertyRecord.h" #include "ContourPolygon.h" #include "AttributeLookupTable.h" +#include "../IStreamSaveable.h" namespace Odb::Lib::FileModel::Design { - class ODBDESIGN_EXPORT EdaDataFile : public IProtoBuffable + class ODBDESIGN_EXPORT EdaDataFile : public IProtoBuffable, public IStreamSaveable { public: EdaDataFile(bool logAllLineParsing = false); @@ -27,7 +28,9 @@ namespace Odb::Lib::FileModel::Design const std::string& GetUnits() const; const std::string& GetSource() const; - bool Parse(std::filesystem::path path); + bool Parse(std::filesystem::path path); + // Inherited via IStreamSaveable + bool Save(std::ostream& os) override; struct ODBDESIGN_EXPORT FeatureIdRecord : public IProtoBuffable { diff --git a/OdbDesignLib/FileModel/Design/FeaturesFile.cpp b/OdbDesignLib/FileModel/Design/FeaturesFile.cpp index 67f81ca9..66b53071 100644 --- a/OdbDesignLib/FileModel/Design/FeaturesFile.cpp +++ b/OdbDesignLib/FileModel/Design/FeaturesFile.cpp @@ -1,5 +1,4 @@ #include "FeaturesFile.h" -#include "FeaturesFile.h" #include "ArchiveExtractor.h" #include #include "Logger.h" @@ -858,6 +857,11 @@ namespace Odb::Lib::FileModel::Design } } + bool FeaturesFile::Save(std::ostream& os) + { + return true; + } + FeaturesFile::FeatureRecord::~FeatureRecord() { m_contourPolygons.clear(); diff --git a/OdbDesignLib/FileModel/Design/FeaturesFile.h b/OdbDesignLib/FileModel/Design/FeaturesFile.h index 5f24cb9f..526491fb 100644 --- a/OdbDesignLib/FileModel/Design/FeaturesFile.h +++ b/OdbDesignLib/FileModel/Design/FeaturesFile.h @@ -9,11 +9,12 @@ #include "../../ProtoBuf/featuresfile.pb.h" #include "SymbolName.h" #include "AttributeLookupTable.h" +#include "../IStreamSaveable.h" namespace Odb::Lib::FileModel::Design { - class FeaturesFile : public IProtoBuffable + class FeaturesFile : public IProtoBuffable, public IStreamSaveable { public: FeaturesFile(); @@ -86,6 +87,8 @@ namespace Odb::Lib::FileModel::Design }; bool Parse(std::filesystem::path directory, const std::string& alternateFilename = ""); + // Inherited via IStreamSaveable + bool Save(std::ostream& os) override; std::string GetUnits() const; std::filesystem::path GetPath(); diff --git a/OdbDesignLib/FileModel/Design/FileArchive.cpp b/OdbDesignLib/FileModel/Design/FileArchive.cpp index 4bafa633..19297900 100644 --- a/OdbDesignLib/FileModel/Design/FileArchive.cpp +++ b/OdbDesignLib/FileModel/Design/FileArchive.cpp @@ -2,9 +2,11 @@ #include #include "ArchiveExtractor.h" #include "MiscInfoFile.h" -#include #include "Logger.h" #include "StopWatch.h" +#include "fastmove.h" +#include +#include using namespace Utils; using namespace std::filesystem; @@ -64,7 +66,7 @@ namespace Odb::Lib::FileModel::Design if (is_regular_file(m_filePath)) { - std::filesystem::path extractedPath; + path extractedPath; if (!ExtractDesignArchive(m_filePath, extractedPath)) { logerror("failed to extract archive: (" + m_filePath + ")"); @@ -106,20 +108,100 @@ namespace Odb::Lib::FileModel::Design return false; } - bool FileArchive::ExtractDesignArchive(const std::filesystem::path& path, std::filesystem::path& extractedPath) + bool FileArchive::SaveFileModel(const path& directory, const std::string& archiveName) + { + // create directory in /tmp + // write dir structure and files to it + // gzip with archiveName + // move archive to directory + + char szTmpNameBuff[L_tmpnam] = { 0 }; + std::tmpnam(szTmpNameBuff); + + auto tempPath = temp_directory_path() / szTmpNameBuff; + if (!create_directory(tempPath)) return false; + + auto rootPath = tempPath / m_productName; + if (!create_directory(rootPath)) return false; + if (!Save(rootPath)) return false; + + // compress the written file structure + std::string createdArchivePath; + if (! Utils::ArchiveExtractor::CompressDir(rootPath.string(), tempPath.string(), m_productName, createdArchivePath)) return false; + if (createdArchivePath.empty()) return false; + + // move the compressed file to the requested save directory + path archiveFilename = path(createdArchivePath).filename(); + path destPath = directory / archiveFilename; + std::error_code ec; + Utils::fastmove_file(createdArchivePath, destPath, true, ec); + if (ec.value() != 0) return false; + + return true; + } + + bool FileArchive::Save(const path& directory) + { + // MiscInfoFile + auto miscPath = directory / "misc"; + if (!create_directory(miscPath)) return false; + + std::ofstream ofs1(miscPath / "info", std::ios::out); + if (!m_miscInfoFile.Save(ofs1)) return false; + ofs1.close(); + + // MiscAttrFile + std::ofstream ofs2(miscPath / "sysattr"); + if (!m_miscAttrListFile.Save(ofs2)) return false; + ofs2.close(); + + // StandardFontsFile + auto fontsPath = directory / "fonts"; + if (!create_directory(fontsPath)) return false; + std::ofstream ofs3(fontsPath / "standard"); + if (!m_standardFontsFile.Save(ofs3)) return false; + ofs3.close(); + + // MatrixFile + auto matrixPath = directory / "matrix"; + if (!create_directory(matrixPath)) return false; + std::ofstream ofs4(matrixPath / "matrix"); + if (!m_matrixFile.Save(ofs4)) return false; + ofs4.close(); + + // Steps + const auto stepsDirectory = directory / "steps"; + if (!create_directory(stepsDirectory)) return false; + for (auto& kvStepDirectory : m_stepsByName) + { + if (!kvStepDirectory.second->Save(stepsDirectory)) return false; + } + + // Symbols + const auto symbolsDirectory = directory / "symbols"; + if (!create_directory(symbolsDirectory)) return false; + for (auto& kvSymbolsDirectory : m_symbolsDirectoriesByName) + { + if (!kvSymbolsDirectory.second->Save(symbolsDirectory)) return false; + } + + return true; + } + + bool FileArchive::ExtractDesignArchive(const path& archivePath, path& extractedPath) { loginfo("Extracting... "); - if (!Utils::ArchiveExtractor::IsArchiveTypeSupported(path)) + if (!Utils::ArchiveExtractor::IsArchiveTypeSupported(archivePath)) { - logerror("Unsupported archive type: (" + path.string() + ")"); + logerror("Unsupported archive type: (" + archivePath.string() + ")"); return false; } - Utils::ArchiveExtractor extractor(path.string()); + Utils::ArchiveExtractor extractor(archivePath.string()); if (!extractor.Extract()) return false; - auto extracted = std::filesystem::path(extractor.GetExtractionDirectory()); + auto extracted = path(extractor.GetExtractionDirectory()); if (!exists(extracted)) return false; extractedPath = extracted; @@ -153,7 +235,7 @@ namespace Odb::Lib::FileModel::Design } } - /*static*/ bool FileArchive::pathContainsTopLevelDesignDirs(const std::filesystem::path& path) + /*static*/ bool FileArchive::pathContainsTopLevelDesignDirs(const path& path) { for (const auto& topLevelRootDirName : TOPLEVEL_DESIGN_DIR_NAMES) { @@ -210,7 +292,7 @@ namespace Odb::Lib::FileModel::Design } } - bool FileArchive::ParseDesignDirectory(const std::filesystem::path& path) + bool FileArchive::ParseDesignDirectory(const path& path) { if (!exists(path)) return false; else if (!is_directory(path)) return false; @@ -227,7 +309,7 @@ namespace Odb::Lib::FileModel::Design return true; } - bool FileArchive::ParseStepDirectories(const std::filesystem::path& path) + bool FileArchive::ParseStepDirectories(const path& path) { loginfo("Parsing steps..."); @@ -269,7 +351,7 @@ namespace Odb::Lib::FileModel::Design return true; } - bool FileArchive::ParseMiscAttrListFile(const std::filesystem::path& path) + bool FileArchive::ParseMiscAttrListFile(const path& path) { loginfo("Parsing misc/attrlist file..."); @@ -284,7 +366,7 @@ namespace Odb::Lib::FileModel::Design return true; } - bool FileArchive::ParseMatrixFile(const std::filesystem::path& path) + bool FileArchive::ParseMatrixFile(const path& path) { loginfo("Parsing matrix/matrix file..."); @@ -298,7 +380,7 @@ namespace Odb::Lib::FileModel::Design return true; } - bool FileArchive::ParseStandardFontsFile(const std::filesystem::path& path) + bool FileArchive::ParseStandardFontsFile(const path& path) { loginfo("Parsing fonts/standard file..."); @@ -313,26 +395,26 @@ namespace Odb::Lib::FileModel::Design return true; } - bool FileArchive::ParseSymbolsDirectories(const std::filesystem::path& path) + bool FileArchive::ParseSymbolsDirectories(const path& path) { loginfo("Parsing symbols root directory..."); auto symbolsDirectory = path / "symbols"; - if (!std::filesystem::exists(symbolsDirectory)) + if (!exists(symbolsDirectory)) { logwarn("symbols root directory does not exist (" + symbolsDirectory.string() + ")"); return true; } - else if (!std::filesystem::is_directory(symbolsDirectory)) + else if (!is_directory(symbolsDirectory)) { logerror("symbols root directory exists but is a regular file/not a directory (" + symbolsDirectory.string() + ")"); return false; } - for (auto& d : std::filesystem::directory_iterator(symbolsDirectory)) + for (auto& d : directory_iterator(symbolsDirectory)) { - if (std::filesystem::is_directory(d)) + if (is_directory(d)) { auto pSymbolsDirectory = std::make_shared(d.path()); if (pSymbolsDirectory->Parse()) diff --git a/OdbDesignLib/FileModel/Design/FileArchive.h b/OdbDesignLib/FileModel/Design/FileArchive.h index f221f2f2..288e59cf 100644 --- a/OdbDesignLib/FileModel/Design/FileArchive.h +++ b/OdbDesignLib/FileModel/Design/FileArchive.h @@ -14,11 +14,12 @@ #include "../../ProtoBuf/filearchive.pb.h" #include "SymbolsDirectory.h" #include "AttrListFile.h" +#include "../ISaveable.h" namespace Odb::Lib::FileModel::Design { - class ODBDESIGN_EXPORT FileArchive : public IProtoBuffable + class ODBDESIGN_EXPORT FileArchive : public IProtoBuffable, public ISaveable { public: FileArchive(std::string path); @@ -41,8 +42,10 @@ namespace Odb::Lib::FileModel::Design // TODO: fix these to use pointer return types //const EdaDataFile& GetStepEdaDataFile(std::string stepName) const; //const EdaDataFile& GetFirstStepEdaDataFile() const; - + bool ParseFileModel(); + bool SaveFileModel(const std::filesystem::path& directory, const std::string& archiveName); + //bool SaveFileModel(const std::string& directory, const std::string& archiveName); // Inherited via IProtoBuffable std::unique_ptr to_protobuf() const override; @@ -57,13 +60,14 @@ namespace Odb::Lib::FileModel::Design std::string m_filename; std::string m_filePath; - StepDirectory::StringMap m_stepsByName; - MiscInfoFile m_miscInfoFile; + MiscInfoFile m_miscInfoFile; MatrixFile m_matrixFile; StandardFontsFile m_standardFontsFile; - SymbolsDirectory::StringMap m_symbolsDirectoriesByName; AttrListFile m_miscAttrListFile; + StepDirectory::StringMap m_stepsByName; + SymbolsDirectory::StringMap m_symbolsDirectoriesByName; + bool ParseDesignDirectory(const std::filesystem::path& path); bool ParseStepDirectories(const std::filesystem::path& path); bool ParseMiscInfoFile(const std::filesystem::path& path); @@ -72,6 +76,8 @@ namespace Odb::Lib::FileModel::Design bool ParseSymbolsDirectories(const std::filesystem::path& path); bool ParseMiscAttrListFile(const std::filesystem::path& path); + bool Save(const std::filesystem::path& directory); + bool ExtractDesignArchive(const std::filesystem::path& path, std::filesystem::path& extractedPath); static std::string findRootDir(const std::filesystem::path& extractedPath); diff --git a/OdbDesignLib/FileModel/Design/LayerDirectory.cpp b/OdbDesignLib/FileModel/Design/LayerDirectory.cpp index c8f999a2..4717c285 100644 --- a/OdbDesignLib/FileModel/Design/LayerDirectory.cpp +++ b/OdbDesignLib/FileModel/Design/LayerDirectory.cpp @@ -1,10 +1,4 @@ #include "LayerDirectory.h" -#include "LayerDirectory.h" -#include "LayerDirectory.h" -#include "LayerDirectory.h" -#include "LayerDirectory.h" -#include "LayerDirectory.h" -#include "LayerDirectory.h" #include "Logger.h" namespace Odb::Lib::FileModel::Design @@ -95,4 +89,30 @@ namespace Odb::Lib::FileModel::Design m_featuresFile.from_protobuf(message.featurefile()); m_attrListFile.from_protobuf(message.attrlistfile()); } + + bool LayerDirectory::Save(const std::filesystem::path& directory) + { + auto layerDir = directory / m_name; + if (!create_directory(layerDir)) return false; + + //ComponentsFile m_componentsFile; + std::ofstream componentsFile(layerDir / "components"); + if (!componentsFile.is_open()) return false; + if (!m_componentsFile.Save(componentsFile)) return false; + componentsFile.close(); + + //FeaturesFile m_featuresFile; + std::ofstream featuresFile(layerDir / "features"); + if (!featuresFile.is_open()) return false; + if (!m_featuresFile.Save(featuresFile)) return false; + featuresFile.close(); + + //AttrListFile m_attrListFile; + std::ofstream attrlistFile(layerDir / "attrlist"); + if (!attrlistFile.is_open()) return false; + if (!m_attrListFile.Save(attrlistFile)) return false; + attrlistFile.close(); + + return true; + } } \ No newline at end of file diff --git a/OdbDesignLib/FileModel/Design/LayerDirectory.h b/OdbDesignLib/FileModel/Design/LayerDirectory.h index 2f930cbd..34a51665 100644 --- a/OdbDesignLib/FileModel/Design/LayerDirectory.h +++ b/OdbDesignLib/FileModel/Design/LayerDirectory.h @@ -10,11 +10,12 @@ #include "../../ProtoBuf/layerdirectory.pb.h" #include "FeaturesFile.h" #include "AttrListFile.h" +#include "../ISaveable.h" namespace Odb::Lib::FileModel::Design { - class ODBDESIGN_EXPORT LayerDirectory : public IProtoBuffable + class ODBDESIGN_EXPORT LayerDirectory : public IProtoBuffable, public ISaveable { public: LayerDirectory(std::filesystem::path path); @@ -24,6 +25,8 @@ namespace Odb::Lib::FileModel::Design std::filesystem::path GetPath() const; bool Parse(); + // Inherited via ISaveable + bool Save(const std::filesystem::path& directory) override; bool ParseComponentsFile(std::filesystem::path directory); bool ParseFeaturesFile(std::filesystem::path directory); diff --git a/OdbDesignLib/FileModel/Design/MatrixFile.cpp b/OdbDesignLib/FileModel/Design/MatrixFile.cpp index 33c5586e..05f33b10 100644 --- a/OdbDesignLib/FileModel/Design/MatrixFile.cpp +++ b/OdbDesignLib/FileModel/Design/MatrixFile.cpp @@ -1,4 +1,3 @@ -#include "MatrixFile.h" // // Created by nmill on 10/13/2023. // @@ -13,6 +12,10 @@ #include #include "../invalid_odb_error.h" #include "../../ProtoBuf/enums.pb.h" +#include "../../enums.h" +#include "../OdbFile.h" +#include +#include namespace Odb::Lib::FileModel::Design { @@ -132,6 +135,7 @@ namespace Odb::Lib::FileModel::Design { // TODO: how to determine if you are opening a step or layer array record? // (maybe a boolean flag? stepArrayOpen = true/false) + // no current opening of a layer or step array record found yet if (pCurrentStepRecord == nullptr && pCurrentLayerRecord == nullptr) { @@ -190,17 +194,17 @@ namespace Odb::Lib::FileModel::Design if (pCurrentStepRecord != nullptr && openBraceFound) { - if (attribute == "COL" || attribute == "col") + if (attribute == StepRecord::COLUMN_KEY || attribute == "col") { pCurrentStepRecord->column = std::stoi(value); } - else if (attribute == "NAME" || attribute == "name") + else if (attribute == StepRecord::NAME_KEY || attribute == "name") { pCurrentStepRecord->name = value; } - else if (attribute == "ID" || attribute == "id") + else if (attribute == StepRecord::ID_KEY || attribute == "id") { - pCurrentStepRecord->id = (unsigned int)std::stoul(value); + pCurrentStepRecord->id = static_cast(std::stoul(value)); } else { @@ -222,135 +226,67 @@ namespace Odb::Lib::FileModel::Design pCurrentLayerRecord->id = (unsigned int)std::stoul(value); } else if (attribute == "TYPE" || attribute == "type") - { - if (value == "SIGNAL") - { - pCurrentLayerRecord->type = LayerRecord::Type::Signal; - } - else if (value == "POWER_GROUND") - { - pCurrentLayerRecord->type = LayerRecord::Type::PowerGround; - } - else if (value == "DIELECTRIC") - { - pCurrentLayerRecord->type = LayerRecord::Type::Dielectric; - } - else if (value == "MIXED") - { - pCurrentLayerRecord->type = LayerRecord::Type::Mixed; - } - else if (value == "SOLDER_MASK") - { - pCurrentLayerRecord->type = LayerRecord::Type::SolderMask; - } - else if (value == "SOLDER_PASTE") - { - pCurrentLayerRecord->type = LayerRecord::Type::SolderPaste; - } - else if (value == "SILK_SCREEN") - { - pCurrentLayerRecord->type = LayerRecord::Type::SilkScreen; - } - else if (value == "DRILL") - { - pCurrentLayerRecord->type = LayerRecord::Type::Drill; - } - else if (value == "ROUT") - { - pCurrentLayerRecord->type = LayerRecord::Type::Rout; - } - else if (value == "DOCUMENT") - { - pCurrentLayerRecord->type = LayerRecord::Type::Document; - } - else if (value == "COMPONENT") - { - pCurrentLayerRecord->type = LayerRecord::Type::Component; - } - else if (value == "MASK") + { + if (LayerRecord::typeMap.contains(value)) { - pCurrentLayerRecord->type = LayerRecord::Type::Mask; - } - else if (value == "CONDUCTIVE_PASTE") - { - pCurrentLayerRecord->type = LayerRecord::Type::ConductivePaste; - } + pCurrentLayerRecord->type = LayerRecord::typeMap.getValue(value); + } else { throw_parse_error(m_path, line, attribute, lineNumber); } } - else if (attribute == "CONTEXT" || attribute == "context") + else if (attribute == LayerRecord::CONTEXT_KEY || attribute == "context") { - if (value == "BOARD") - { - pCurrentLayerRecord->context = LayerRecord::Context::Board; - } - else if (value == "MISC") + if (LayerRecord::contextMap.contains(value)) { - pCurrentLayerRecord->context = LayerRecord::Context::Misc; - } + pCurrentLayerRecord->context = LayerRecord::contextMap.getValue(value); + } else { - throw_parse_error(m_path, line, attribute, lineNumber); - } + throw_parse_error(m_path, line, attribute, lineNumber); + } } else if (attribute == "OLD_NAME" || attribute == "old_name") { pCurrentLayerRecord->oldName = value; } - else if (attribute == "POLARITY" || attribute == "polarity") + else if (attribute == LayerRecord::POLARITY_KEY || attribute == "polarity") { - if (value == "POSITIVE") + if (LayerRecord::polarityMap.contains(value)) { - pCurrentLayerRecord->polarity = Polarity::Positive; - } - else if (value == "NEGATIVE") - { - pCurrentLayerRecord->polarity = Polarity::Negative; - } + pCurrentLayerRecord->polarity = LayerRecord::polarityMap.getValue(value); + } else { throw_parse_error(m_path, line, attribute, lineNumber); - } + } } - else if (attribute == "DIELECTRIC_TYPE" || attribute == "dielectric_type") + else if (attribute == LayerRecord::DIELECTRIC_TYPE_KEY || attribute == "dielectric_type") { - if (value == "NONE") + if (LayerRecord::dielectricTypeMap.contains(value)) { - pCurrentLayerRecord->dielectricType = LayerRecord::DielectricType::None; - } - else if (value == "PREPREG") - { - pCurrentLayerRecord->dielectricType = LayerRecord::DielectricType::Prepreg; - } - else if (value == "CORE") - { - pCurrentLayerRecord->dielectricType = LayerRecord::DielectricType::Core; - } + pCurrentLayerRecord->dielectricType = LayerRecord::dielectricTypeMap.getValue(value); + } else { - throw_parse_error(m_path, line, attribute, lineNumber); - } + throw_parse_error(m_path, line, attribute, lineNumber); + } } else if (attribute == "DIELECTRIC_NAME" || attribute == "dielectric_name") { pCurrentLayerRecord->dielectricName = value; } - else if (attribute == "FORM" || attribute == "form") + else if (attribute == LayerRecord::FORM_KEY || attribute == "form") { - if (value == "RIGID") - { - pCurrentLayerRecord->form = LayerRecord::Form::Rigid; - } - else if (value == "FLEX") + if (LayerRecord::formMap.contains(value)) { - pCurrentLayerRecord->form = LayerRecord::Form::Flex; - } + pCurrentLayerRecord->form = LayerRecord::formMap.getValue(value); + } else { - throw_parse_error(m_path, line, attribute, lineNumber); - } + throw_parse_error(m_path, line, attribute, lineNumber); + } } else if (attribute == "CU_TOP" || attribute == "cu_top") { @@ -471,6 +407,64 @@ namespace Odb::Lib::FileModel::Design m_layerRecords.push_back(pLayerRecord); } } + + bool MatrixFile::Save(std::ostream& os) + { + for (const auto& stepRecord : m_stepRecords) + { + os << StepRecord::RECORD_TOKEN << " " << Constants::ARRAY_RECORD_OPEN_TOKEN << std::endl; + + os << '\t' << StepRecord::COLUMN_KEY << "=" << stepRecord->column << std::endl; + os << '\t' << StepRecord::NAME_KEY << "=" << stepRecord->name << std::endl; + os << '\t' << StepRecord::ID_KEY << "=" << stepRecord->id << std::endl; + + os << Constants::ARRAY_RECORD_CLOSE_TOKEN << std::endl; + os << std::endl; + } + + for (const auto& layerRecord : m_layerRecords) + { + os << LayerRecord::RECORD_TOKEN << " " << Constants::ARRAY_RECORD_OPEN_TOKEN << std::endl; + + os << '\t' << LayerRecord::ROW_KEY << "=" << layerRecord->row << std::endl; + os << '\t' << LayerRecord::CONTEXT_KEY << "=" << LayerRecord::contextMap.getValue(layerRecord->context) << std::endl; + os << '\t' << LayerRecord::TYPE_KEY << "=" << LayerRecord::typeMap.getValue(layerRecord->type) << std::endl; + os << '\t' << LayerRecord::NAME_KEY << "=" << layerRecord->name << std::endl; + os << '\t' << LayerRecord::POLARITY_KEY << "=" << LayerRecord::polarityMap.getValue(layerRecord->polarity) << std::endl; + os << '\t' << LayerRecord::FORM_KEY << "=" << LayerRecord::formMap.getValue(layerRecord->form) << std::endl; + os << '\t' << LayerRecord::DIELECTRIC_TYPE_KEY << "=" << LayerRecord::dielectricTypeMap.getValue(layerRecord->dielectricType) << std::endl; + os << '\t' << LayerRecord::DIELECTRIC_NAME_KEY << "=" << layerRecord->dielectricName << std::endl; + os << '\t' << LayerRecord::CU_TOP_KEY << "="; + if (layerRecord->cuTop != (unsigned int)-1) + { + os << layerRecord->cuTop; + } + os << std::endl; + os << '\t' << LayerRecord::CU_BOTTOM_KEY << "="; + if (layerRecord->cuBottom != (unsigned int)-1) + { + os << layerRecord->cuBottom; + } + os << std::endl; + os << '\t' << LayerRecord::REF_KEY << "="; + if (layerRecord->ref != (unsigned int)-1) + { + os << layerRecord->ref; + } + os << std::endl; + os << '\t' << LayerRecord::START_NAME_KEY << "=" << layerRecord->startName << std::endl; + os << '\t' << LayerRecord::END_NAME_KEY << "=" << layerRecord->endName << std::endl; + os << '\t' << LayerRecord::OLD_NAME_KEY << "=" << layerRecord->oldName << std::endl; + os << '\t' << LayerRecord::ADD_TYPE_KEY << "=" << layerRecord->addType << std::endl; + os << '\t' << LayerRecord::COLOR_KEY << "=" << layerRecord->color.to_string() << std::endl; + os << '\t' << LayerRecord::ID_KEY << "=" << layerRecord->id << std::endl; + + os << Constants::ARRAY_RECORD_CLOSE_TOKEN << std::endl; + os << std::endl; + } + + return true; + } std::unique_ptr Odb::Lib::FileModel::Design::MatrixFile::StepRecord::to_protobuf() const { @@ -536,5 +530,5 @@ namespace Odb::Lib::FileModel::Design //color.r = message.color().r(); //color.g = message.color().g(); //color.b = message.color().b(); - } + } } \ No newline at end of file diff --git a/OdbDesignLib/FileModel/Design/MatrixFile.h b/OdbDesignLib/FileModel/Design/MatrixFile.h index 3343d181..6afc825f 100644 --- a/OdbDesignLib/FileModel/Design/MatrixFile.h +++ b/OdbDesignLib/FileModel/Design/MatrixFile.h @@ -12,24 +12,31 @@ #include "../../enums.h" #include "../../IProtoBuffable.h" #include "../../ProtoBuf/matrixfile.pb.h" +#include "../IStreamSaveable.h" +#include "EnumMap.h" +#include +#include namespace Odb::Lib::FileModel::Design { - class MatrixFile : public OdbFile, public IProtoBuffable + class MatrixFile : public OdbFile, public IProtoBuffable, public IStreamSaveable { - public: + public: ~MatrixFile(); struct StepRecord : public IProtoBuffable { unsigned int column; - unsigned int id = (unsigned int) -1; + unsigned int id = (unsigned int)-1; std::string name; - typedef std::vector> Vector; + typedef std::vector> Vector; inline static const char* RECORD_TOKEN = "STEP"; + inline static const char* COLUMN_KEY = "COL"; + inline static const char* NAME_KEY = "NAME"; + inline static const char* ID_KEY = "ID"; // Inherited via IProtoBuffable std::unique_ptr to_protobuf() const override; @@ -37,7 +44,7 @@ namespace Odb::Lib::FileModel::Design }; struct LayerRecord : public IProtoBuffable - { + { enum class Type { Signal, @@ -53,16 +60,17 @@ namespace Odb::Lib::FileModel::Design Component, Mask, ConductivePaste, - }; + }; enum class Context { Board, Misc - }; + }; enum class DielectricType { + NotSet, None, Prepreg, Core @@ -70,9 +78,10 @@ namespace Odb::Lib::FileModel::Design enum class Form { + NotSet, Rigid, Flex - }; + }; typedef std::vector> Vector; @@ -81,24 +90,91 @@ namespace Odb::Lib::FileModel::Design Type type; std::string name; Polarity polarity; - DielectricType dielectricType = DielectricType::None; + DielectricType dielectricType = DielectricType::NotSet; std::string dielectricName; - Form form = Form::Rigid; - unsigned int cuTop = (unsigned int) -1; - unsigned int cuBottom = (unsigned int) -1; - unsigned int ref = (unsigned int) -1; + Form form = Form::NotSet; + unsigned int cuTop = (unsigned int)-1; + unsigned int cuBottom = (unsigned int)-1; + unsigned int ref = (unsigned int)-1; std::string startName; std::string endName; std::string oldName; std::string addType; - RgbColor color{"0"}; - unsigned int id = (unsigned int) -1; + RgbColor color{ "0" }; + unsigned int id = (unsigned int)-1; inline static const char* RECORD_TOKEN = "LAYER"; + inline static const char* ROW_KEY = "ROW"; + inline static const char* CONTEXT_KEY = "CONTEXT"; + inline static const char* TYPE_KEY = "TYPE"; + inline static const char* NAME_KEY = "NAME"; + inline static const char* POLARITY_KEY = "POLARITY"; + inline static const char* DIELECTRIC_TYPE_KEY = "DIELECTRIC_TYPE"; + inline static const char* DIELECTRIC_NAME_KEY = "DIELECTRIC_NAME"; + inline static const char* FORM_KEY = "FORM"; + inline static const char* CU_TOP_KEY = "CU_TOP"; + inline static const char* CU_BOTTOM_KEY = "CU_BOTTOM"; + inline static const char* REF_KEY = "REF"; + inline static const char* START_NAME_KEY = "START_NAME"; + inline static const char* END_NAME_KEY = "END_NAME"; + inline static const char* OLD_NAME_KEY = "OLD_NAME"; + inline static const char* ADD_TYPE_KEY = "ADD_TYPE"; + inline static const char* COLOR_KEY = "COLOR"; + inline static const char* ID_KEY = "ID"; + // Inherited via IProtoBuffable std::unique_ptr to_protobuf() const override; void from_protobuf(const Odb::Lib::Protobuf::MatrixFile::LayerRecord& message) override; + + inline static const Utils::EnumMap typeMap { + { + "SIGNAL", + "POWER_GROUND", + "DIELECTRIC", + "MIXED", + "SOLDER_MASK", + "SOLDER_PASTE", + "SILK_SCREEN", + "DRILL", + "ROUT", + "DOCUMENT", + "COMPONENT", + "MASK", + "CONDUCTIVE_PASTE" + } + }; + + inline static const Utils::EnumMap contextMap{ + { + "BOARD", + "MISC" + } + }; + + inline static const Utils::EnumMap dielectricTypeMap{ + { + "", + "NONE", + "PREPREG", + "CORE" + } + }; + + inline static const Utils::EnumMap
formMap{ + { + "", + "RIGID", + "FLEX" + } + }; + + inline static const Utils::EnumMap polarityMap{ + { + "POSITIVE", + "NEGATIVE" + } + }; }; const LayerRecord::Vector& GetLayerRecords() const; @@ -106,6 +182,8 @@ namespace Odb::Lib::FileModel::Design // Inherited via OdbFile bool Parse(std::filesystem::path path) override; + // Inherited via IStreamSaveable + bool Save(std::ostream& os) override; static inline bool attributeValueIsOptional(const std::string& attribute); @@ -134,6 +212,6 @@ namespace Odb::Lib::FileModel::Design "CU_BOTTOM", "REF", "COLOR", - }; + }; }; } diff --git a/OdbDesignLib/FileModel/Design/MiscInfoFile.cpp b/OdbDesignLib/FileModel/Design/MiscInfoFile.cpp index b0158861..2265ae60 100644 --- a/OdbDesignLib/FileModel/Design/MiscInfoFile.cpp +++ b/OdbDesignLib/FileModel/Design/MiscInfoFile.cpp @@ -1,4 +1,5 @@ #include "MiscInfoFile.h" +#include "MiscInfoFile.h" // // Created by nmill on 10/13/2023. // @@ -192,6 +193,23 @@ namespace Odb::Lib::FileModel::Design return true; } + bool MiscInfoFile::Save(std::ostream& os) + { + os << PRODUCT_MODEL_NAME_KEY << "=" << m_productModelName << std::endl; + os << JOB_NAME_KEY << "=" << m_jobName << std::endl; + os << ODB_VERSION_MAJOR_KEY << "=" << m_odbVersionMajor << std::endl; + os << ODB_VERSION_MINOR_KEY << "=" << m_odbVersionMinor << std::endl; + os << ODB_SOURCE_KEY << "=" << m_odbSource << std::endl; + os << CREATION_DATE_KEY << "=" << Utils::make_timestamp(m_creationDateDate) << std::endl; + os << SAVE_DATE_KEY << "=" << Utils::make_timestamp(m_saveDate) << std::endl; + os << SAVE_APP_KEY << "=" << m_saveApp << std::endl; + os << SAVE_USER_KEY << "=" << m_saveUser << std::endl; + os << UNITS_KEY << "=" << m_units << std::endl; + os << MAX_UID_KEY << "=" << m_maxUniqueId << std::endl; + + return true; + } + /*static*/ inline bool MiscInfoFile::attributeValueIsOptional(const std::string& attribute) { for (const auto& optionalAttribute : OPTIONAL_ATTRIBUTES) diff --git a/OdbDesignLib/FileModel/Design/MiscInfoFile.h b/OdbDesignLib/FileModel/Design/MiscInfoFile.h index fac73b6a..3afc478e 100644 --- a/OdbDesignLib/FileModel/Design/MiscInfoFile.h +++ b/OdbDesignLib/FileModel/Design/MiscInfoFile.h @@ -6,13 +6,15 @@ #include #include "../../IProtoBuffable.h" #include "../../ProtoBuf/miscinfofile.pb.h" +#include "../IStreamSaveable.h" +#include #pragma once namespace Odb::Lib::FileModel::Design { - class MiscInfoFile : public OdbFile, public IProtoBuffable + class MiscInfoFile : public OdbFile, public IProtoBuffable, public IStreamSaveable { public: MiscInfoFile(); @@ -31,6 +33,7 @@ namespace Odb::Lib::FileModel::Design unsigned int GetMaxUniqueId() const; bool Parse(std::filesystem::path path) override; + bool Save(std::ostream& os) override; // Inherited via IProtoBuffable std::unique_ptr to_protobuf() const override; @@ -55,6 +58,18 @@ namespace Odb::Lib::FileModel::Design { //"ODB_SOURCE", // not optional per spec pg. 80 "MAX_UID", - }; + }; + + constexpr inline static const char* PRODUCT_MODEL_NAME_KEY = "PRODUCT_MODEL_NAME"; + constexpr inline static const char* JOB_NAME_KEY = "JOB_NAME"; + constexpr inline static const char* ODB_VERSION_MAJOR_KEY = "ODB_VERSION_MAJOR"; + constexpr inline static const char* ODB_VERSION_MINOR_KEY = "ODB_VERSION_MINOR"; + constexpr inline static const char* ODB_SOURCE_KEY = "ODB_SOURCE"; + constexpr inline static const char* CREATION_DATE_KEY = "CREATION_DATE"; + constexpr inline static const char* SAVE_DATE_KEY = "SAVE_DATE"; + constexpr inline static const char* SAVE_APP_KEY = "SAVE_APP"; + constexpr inline static const char* SAVE_USER_KEY = "SAVE_USER"; + constexpr inline static const char* UNITS_KEY = "UNITS"; + constexpr inline static const char* MAX_UID_KEY = "MAX_UID"; }; } diff --git a/OdbDesignLib/FileModel/Design/NetlistFile.cpp b/OdbDesignLib/FileModel/Design/NetlistFile.cpp index e30d2ee3..0a5f480f 100644 --- a/OdbDesignLib/FileModel/Design/NetlistFile.cpp +++ b/OdbDesignLib/FileModel/Design/NetlistFile.cpp @@ -1,17 +1,21 @@ #include "NetlistFile.h" #include "NetlistFile.h" -#include "NetlistFile.h" -#include "NetlistFile.h" -#include "NetlistFile.h" -#include "NetlistFile.h" -#include "NetlistFile.h" -#include "NetlistFile.h" -#include -#include #include #include "../parse_error.h" #include "../invalid_odb_error.h" #include +#include +#include +#include "../../Constants.h" +#include +#include +#include +#include +#include +#include "../parse_info.h" +#include "../../ProtoBuf/netlistfile.pb.h" + +using namespace std::filesystem; namespace Odb::Lib::FileModel::Design { @@ -109,11 +113,11 @@ namespace Odb::Lib::FileModel::Design { std::stringstream lineStream(line); - if (line.find(COMMENT_TOKEN) == 0) + if (line.find(Constants::COMMENT_TOKEN) == 0) { // comment line } - else if (line.find(UNITS_TOKEN) == 0) + else if (line.find(Constants::UNITS_TOKEN) == 0) { // units line std::string token; @@ -162,7 +166,7 @@ namespace Odb::Lib::FileModel::Design } // staggered (optional) - if (lineStream >> token && token == "staggered") + if (lineStream >> token && token == STAGGERED_KEY) { char staggered; if (!(lineStream >> staggered)) @@ -289,14 +293,14 @@ namespace Odb::Lib::FileModel::Design return true; } - std::unique_ptr NetlistFile::to_protobuf() const + std::unique_ptr NetlistFile::to_protobuf() const { - std::unique_ptr pNetlistFileMessage(new Odb::Lib::Protobuf::NetlistFile); + std::unique_ptr pNetlistFileMessage(new Protobuf::NetlistFile); pNetlistFileMessage->set_units(m_units); pNetlistFileMessage->set_path(m_path.string()); pNetlistFileMessage->set_name(m_name); pNetlistFileMessage->set_optimized(m_optimized); - pNetlistFileMessage->set_staggered(static_cast(m_staggered)); + pNetlistFileMessage->set_staggered(static_cast(m_staggered)); for (const auto& pNetRecord : m_netRecords) { @@ -318,7 +322,7 @@ namespace Odb::Lib::FileModel::Design return pNetlistFileMessage; } - void NetlistFile::from_protobuf(const Odb::Lib::Protobuf::NetlistFile& message) + void NetlistFile::from_protobuf(const Protobuf::NetlistFile& message) { m_name = message.name(); m_path = message.path(); @@ -348,23 +352,55 @@ namespace Odb::Lib::FileModel::Design } } - std::unique_ptr NetlistFile::NetRecord::to_protobuf() const + bool NetlistFile::Save(std::ostream& os) { - std::unique_ptr pNetRecordMessage(new Odb::Lib::Protobuf::NetlistFile::NetRecord); + os << 'H' << " optimize " << (m_optimized ? 'Y' : 'N') << "staggered " << (m_staggered == Staggered::Yes ? 'Y' : 'N') << std::endl; + os << Constants::UNITS_TOKEN << " = " << m_units << std::endl; + + for (const auto& netRecord : m_netRecords) + { + os << NetRecord::FIELD_TOKEN << netRecord->serialNumber << " " << netRecord->netName << std::endl; + } + + for (const auto& netPointRecord : m_netPointRecords) + { + netPointRecord->Save(os); + os << std::endl; + } + + return true; + } + + bool NetlistFile::Save(const std::filesystem::path& directory) + { + auto netlistDir = directory / m_name; + if (!create_directory(netlistDir)) return false; + + std::ofstream netlistFile(netlistDir / "netlist"); + if (!netlistFile.is_open()) return false; + if (! Save(netlistFile)) return false; + netlistFile.close(); + + return true; + } + + std::unique_ptr NetlistFile::NetRecord::to_protobuf() const + { + std::unique_ptr pNetRecordMessage(new Protobuf::NetlistFile::NetRecord); pNetRecordMessage->set_serialnumber(serialNumber); pNetRecordMessage->set_netname(netName); return pNetRecordMessage; } - void NetlistFile::NetRecord::from_protobuf(const Odb::Lib::Protobuf::NetlistFile::NetRecord& message) + void NetlistFile::NetRecord::from_protobuf(const Protobuf::NetlistFile::NetRecord& message) { serialNumber = message.serialnumber(); netName = message.netname(); } - std::unique_ptr NetlistFile::NetPointRecord::to_protobuf() const + std::unique_ptr NetlistFile::NetPointRecord::to_protobuf() const { - std::unique_ptr pNetPointRecordMessage(new Odb::Lib::Protobuf::NetlistFile::NetPointRecord); + std::unique_ptr pNetPointRecordMessage(new Protobuf::NetlistFile::NetPointRecord); pNetPointRecordMessage->set_netnumber(netNumber); pNetPointRecordMessage->set_radius(radius); pNetPointRecordMessage->set_x(x); @@ -376,7 +412,7 @@ namespace Odb::Lib::FileModel::Design pNetPointRecordMessage->set_height(height); pNetPointRecordMessage->set_commentpoint(commentPoint); pNetPointRecordMessage->set_netnumber(netNumber); - pNetPointRecordMessage->set_side(static_cast(side)); + pNetPointRecordMessage->set_side(static_cast(side)); pNetPointRecordMessage->set_staggeredradius(staggeredRadius); pNetPointRecordMessage->set_staggeredx(staggeredX); pNetPointRecordMessage->set_staggeredy(staggeredY); @@ -387,7 +423,7 @@ namespace Odb::Lib::FileModel::Design return pNetPointRecordMessage; } - void NetlistFile::NetPointRecord::from_protobuf(const Odb::Lib::Protobuf::NetlistFile::NetPointRecord& message) + void NetlistFile::NetPointRecord::from_protobuf(const Protobuf::NetlistFile::NetPointRecord& message) { netNumber = message.netnumber(); radius = message.radius(); @@ -419,5 +455,9 @@ namespace Odb::Lib::FileModel::Design fiducialPoint = message.fiducialpoint(); } + bool NetlistFile::NetPointRecord::Save(std::ostream& os) + { + return false; + } } // namespace Odb::Lib::FileModel::Design \ No newline at end of file diff --git a/OdbDesignLib/FileModel/Design/NetlistFile.h b/OdbDesignLib/FileModel/Design/NetlistFile.h index 91bf8d95..03b79aef 100644 --- a/OdbDesignLib/FileModel/Design/NetlistFile.h +++ b/OdbDesignLib/FileModel/Design/NetlistFile.h @@ -8,11 +8,14 @@ #include "../../odbdesign_export.h" #include "../../IProtoBuffable.h" #include "../../ProtoBuf/netlistfile.pb.h" +#include "../ISaveable.h" +#include "../IStreamSaveable.h" +#include "EnumMap.h" namespace Odb::Lib::FileModel::Design { - class ODBDESIGN_EXPORT NetlistFile : public IProtoBuffable + class ODBDESIGN_EXPORT NetlistFile : public IProtoBuffable, public ISaveable, public IStreamSaveable { public: struct ODBDESIGN_EXPORT NetRecord : public IProtoBuffable @@ -30,7 +33,7 @@ namespace Odb::Lib::FileModel::Design inline constexpr static const char* FIELD_TOKEN = "$"; }; - struct ODBDESIGN_EXPORT NetPointRecord : public IProtoBuffable + struct ODBDESIGN_EXPORT NetPointRecord : public IProtoBuffable, public IStreamSaveable { enum AccessSide { @@ -64,6 +67,19 @@ namespace Odb::Lib::FileModel::Design void from_protobuf(const Odb::Lib::Protobuf::NetlistFile::NetPointRecord& message) override; typedef std::vector> Vector; + + inline static const Utils::EnumMap accessSideMap{ + { + "Top", + "Down", + "Both", + "Inner" + } + }; + + // Inherited via IStreamSaveable + bool Save(std::ostream& os) override; + }; // NetPointRecord enum class Staggered @@ -73,6 +89,14 @@ namespace Odb::Lib::FileModel::Design Unknown }; + inline static const Utils::EnumMap staggeredMap{ + { + "Y", + "N", + "Unknown" + } + }; + NetlistFile(std::filesystem::path path); ~NetlistFile(); @@ -88,6 +112,8 @@ namespace Odb::Lib::FileModel::Design const NetPointRecord::Vector& GetNetPointRecords() const; bool Parse(); + // Inherited via ISaveable + bool Save(const std::filesystem::path& directory) override; // Inherited via IProtoBuffable std::unique_ptr to_protobuf() const override; @@ -105,11 +131,13 @@ namespace Odb::Lib::FileModel::Design NetRecord::Vector m_netRecords; NetRecord::StringMap m_netRecordsByName; - NetPointRecord::Vector m_netPointRecords; + NetPointRecord::Vector m_netPointRecords; + + inline constexpr static const char HEADER_TOKEN[] = "H"; + inline constexpr static const char STAGGERED_KEY[] = "staggered"; - inline constexpr static const char* UNITS_TOKEN = "UNITS"; - inline constexpr static const char* COMMENT_TOKEN = "#"; - inline constexpr static const char* HEADER_TOKEN = "H"; + // Inherited via IStreamSaveable + bool Save(std::ostream& os) override; }; // NetlistFile } \ No newline at end of file diff --git a/OdbDesignLib/FileModel/Design/RgbColor.cpp b/OdbDesignLib/FileModel/Design/RgbColor.cpp index d87f9c97..322272f7 100644 --- a/OdbDesignLib/FileModel/Design/RgbColor.cpp +++ b/OdbDesignLib/FileModel/Design/RgbColor.cpp @@ -1,4 +1,5 @@ #include "RgbColor.h" +#include namespace Odb::Lib::FileModel::Design { @@ -44,7 +45,7 @@ namespace Odb::Lib::FileModel::Design return false; } - inline std::string RgbColor::to_string() + std::string RgbColor::to_string() const { if (noPreference) { diff --git a/OdbDesignLib/FileModel/Design/RgbColor.h b/OdbDesignLib/FileModel/Design/RgbColor.h index 98539c20..f18aad51 100644 --- a/OdbDesignLib/FileModel/Design/RgbColor.h +++ b/OdbDesignLib/FileModel/Design/RgbColor.h @@ -1,13 +1,15 @@ #pragma once #include +#include "../../odbdesign_export.h" +#include namespace Odb::Lib::FileModel::Design { /// @class struct for representing preferred layer color /// @brief each color value ranges from 0% - 100% /// noPreference is true if the Odb++ file does not specify a color - struct RgbColor + struct ODBDESIGN_EXPORT RgbColor { uint8_t red; uint8_t green; @@ -19,6 +21,6 @@ namespace Odb::Lib::FileModel::Design explicit RgbColor(const std::string& str); bool from_string(const std::string& str); - std::string to_string(); + std::string to_string() const; }; } diff --git a/OdbDesignLib/FileModel/Design/StandardFontsFile.cpp b/OdbDesignLib/FileModel/Design/StandardFontsFile.cpp index 4caf77ad..040a0758 100644 --- a/OdbDesignLib/FileModel/Design/StandardFontsFile.cpp +++ b/OdbDesignLib/FileModel/Design/StandardFontsFile.cpp @@ -1,5 +1,4 @@ #include "StandardFontsFile.h" - #include "Logger.h" #include #include "str_utils.h" @@ -307,6 +306,11 @@ namespace Odb::Lib::FileModel::Design } } + bool StandardFontsFile::Save(std::ostream& os) + { + return true; + } + StandardFontsFile::CharacterBlock::~CharacterBlock() { m_lineRecords.clear(); diff --git a/OdbDesignLib/FileModel/Design/StandardFontsFile.h b/OdbDesignLib/FileModel/Design/StandardFontsFile.h index 4ba65921..9c0c840c 100644 --- a/OdbDesignLib/FileModel/Design/StandardFontsFile.h +++ b/OdbDesignLib/FileModel/Design/StandardFontsFile.h @@ -6,16 +6,19 @@ #include #include "../../IProtoBuffable.h" #include "../../ProtoBuf/standardfontsfile.pb.h" +#include "../IStreamSaveable.h" namespace Odb::Lib::FileModel::Design { - class StandardFontsFile : public OdbFile, public IProtoBuffable + class StandardFontsFile : public OdbFile, public IProtoBuffable, public IStreamSaveable { public: StandardFontsFile() = default; ~StandardFontsFile(); bool Parse(std::filesystem::path path) override; + // Inherited via IStreamSaveable + bool Save(std::ostream& os) override; // Inherited via IProtoBuffable std::unique_ptr to_protobuf() const override; @@ -61,6 +64,6 @@ namespace Odb::Lib::FileModel::Design float m_ySize; float m_offset; - CharacterBlock::Vector m_characterBlocks; + CharacterBlock::Vector m_characterBlocks; }; } diff --git a/OdbDesignLib/FileModel/Design/StepDirectory.cpp b/OdbDesignLib/FileModel/Design/StepDirectory.cpp index 3b9ec457..33f0f286 100644 --- a/OdbDesignLib/FileModel/Design/StepDirectory.cpp +++ b/OdbDesignLib/FileModel/Design/StepDirectory.cpp @@ -1,6 +1,4 @@ #include "StepDirectory.h" -#include "StepDirectory.h" -#include "StepDirectory.h" #include #include "LayerDirectory.h" #include @@ -172,6 +170,52 @@ namespace Odb::Lib::FileModel::Design return success; } + bool StepDirectory::Save(const std::filesystem::path& directory) + { + auto stepDir = directory / m_name; + if (!create_directory(stepDir)) return false; + + // eda/data + auto edaPath = stepDir / "eda"; + if (!create_directory(edaPath)) return false; + std::ofstream edaDataFile(edaPath / "data"); + if (!m_edaData.Save(edaDataFile)) return false; + edaDataFile.close(); + + // attrlist + std::ofstream attrlistFile(stepDir / "attrlist"); + if (!m_attrListFile.Save(attrlistFile)) return false; + attrlistFile.close(); + + // profile + std::ofstream profileFile(stepDir / "profile"); + if (!m_profileFile.Save(profileFile)) return false; + profileFile.close(); + + // StepHdrFile + std::ofstream stephdrFile(stepDir / "stephdr"); + if (!m_stepHdrFile.Save(stephdrFile)) return false; + stephdrFile.close(); + + // layers + auto layersPath = stepDir / "layers"; + if (!create_directory(layersPath)) return false; + for (auto& kvLayer : m_layersByName) + { + if (!kvLayer.second->Save(layersPath)) return false; + } + + // m_netlistsByName; + auto netlistsPath = stepDir / "netlists"; + if (!create_directory(netlistsPath)) return false; + for (auto& kvNetlist : m_netlistsByName) + { + if (!kvNetlist.second->Save(netlistsPath)) return false; + } + + return true; + } + std::unique_ptr StepDirectory::to_protobuf() const { std::unique_ptr pStepDirectoryMessage(new Odb::Lib::Protobuf::StepDirectory); diff --git a/OdbDesignLib/FileModel/Design/StepDirectory.h b/OdbDesignLib/FileModel/Design/StepDirectory.h index 38533da2..7bb3a4ac 100644 --- a/OdbDesignLib/FileModel/Design/StepDirectory.h +++ b/OdbDesignLib/FileModel/Design/StepDirectory.h @@ -14,11 +14,13 @@ #include "ComponentsFile.h" #include "AttrListFile.h" #include "StepHdrFile.h" +#include "../ISaveable.h" +#include "FeaturesFile.h" namespace Odb::Lib::FileModel::Design { - class ODBDESIGN_EXPORT StepDirectory : public IProtoBuffable + class ODBDESIGN_EXPORT StepDirectory : public IProtoBuffable, public ISaveable { public: StepDirectory(std::filesystem::path path); @@ -38,6 +40,8 @@ namespace Odb::Lib::FileModel::Design const ComponentsFile* GetBottomComponentsFile() const; bool Parse(); + // Inherited via ISaveable + bool Save(const std::filesystem::path& directory) override; typedef std::map> StringMap; diff --git a/OdbDesignLib/FileModel/Design/StepHdrFile.cpp b/OdbDesignLib/FileModel/Design/StepHdrFile.cpp index c0fe49fb..9f7e49ab 100644 --- a/OdbDesignLib/FileModel/Design/StepHdrFile.cpp +++ b/OdbDesignLib/FileModel/Design/StepHdrFile.cpp @@ -351,6 +351,11 @@ namespace Odb::Lib::FileModel::Design } } + bool StepHdrFile::Save(std::ostream& os) + { + return true; + } + std::unique_ptr StepHdrFile::StepRepeatRecord::to_protobuf() const { auto message = std::make_unique(); diff --git a/OdbDesignLib/FileModel/Design/StepHdrFile.h b/OdbDesignLib/FileModel/Design/StepHdrFile.h index 8a5a0875..9a3acfae 100644 --- a/OdbDesignLib/FileModel/Design/StepHdrFile.h +++ b/OdbDesignLib/FileModel/Design/StepHdrFile.h @@ -8,10 +8,11 @@ #include "../../IProtoBuffable.h" #include "../../ProtoBuf/stephdrfile.pb.h" #include "../OdbFile.h" +#include "../IStreamSaveable.h" namespace Odb::Lib::FileModel::Design { - class ODBDESIGN_EXPORT StepHdrFile : public OdbFile, public IProtoBuffable + class ODBDESIGN_EXPORT StepHdrFile : public OdbFile, public IProtoBuffable, public IStreamSaveable { public: virtual ~StepHdrFile(); @@ -39,6 +40,8 @@ namespace Odb::Lib::FileModel::Design }; bool Parse(std::filesystem::path path) override; + // Inherited via IStreamSaveable + bool Save(std::ostream& os) override; // Inherited via IProtoBuffable std::unique_ptr to_protobuf() const override; diff --git a/OdbDesignLib/FileModel/Design/SymbolsDirectory.cpp b/OdbDesignLib/FileModel/Design/SymbolsDirectory.cpp index 44c2eeba..e8436fc4 100644 --- a/OdbDesignLib/FileModel/Design/SymbolsDirectory.cpp +++ b/OdbDesignLib/FileModel/Design/SymbolsDirectory.cpp @@ -1,6 +1,10 @@ #include "SymbolsDirectory.h" #include "Logger.h" #include +#include +#include "FeaturesFile.h" +#include "AttrListFile.h" +#include namespace Odb::Lib::FileModel::Design { @@ -61,4 +65,24 @@ namespace Odb::Lib::FileModel::Design { return m_attrListFile.Parse(directory); } + + bool SymbolsDirectory::Save(const std::filesystem::path& directory) + { + auto symbolDir = directory / m_name; + if (!create_directory(symbolDir)) return false; + + //FeaturesFile m_featuresFile; + std::ofstream featuresFile(symbolDir / "features"); + if (!featuresFile.is_open()) return false; + if (!m_featuresFile.Save(featuresFile)) return false; + featuresFile.close(); + + //AttrListFile m_attrListFile; + std::ofstream attrlistFile(symbolDir / "attrlist"); + if (!attrlistFile.is_open()) return false; + if (!m_attrListFile.Save(attrlistFile)) return false; + attrlistFile.close(); + + return true; + } } \ No newline at end of file diff --git a/OdbDesignLib/FileModel/Design/SymbolsDirectory.h b/OdbDesignLib/FileModel/Design/SymbolsDirectory.h index 1632733b..f2c5e2b8 100644 --- a/OdbDesignLib/FileModel/Design/SymbolsDirectory.h +++ b/OdbDesignLib/FileModel/Design/SymbolsDirectory.h @@ -10,10 +10,11 @@ #include #include "../../IProtoBuffable.h" #include "../../ProtoBuf/symbolsdirectory.pb.h" +#include "../ISaveable.h" namespace Odb::Lib::FileModel::Design { - class ODBDESIGN_EXPORT SymbolsDirectory : public IProtoBuffable + class ODBDESIGN_EXPORT SymbolsDirectory : public IProtoBuffable, public ISaveable { public: SymbolsDirectory(const std::filesystem::path& path); @@ -22,6 +23,8 @@ namespace Odb::Lib::FileModel::Design typedef std::map> StringMap; bool Parse(); + // Inherited via ISaveable + bool Save(const std::filesystem::path& directory) override; std::string GetName() const; std::filesystem::path GetPath() const; diff --git a/OdbDesignLib/FileModel/ISaveable.h b/OdbDesignLib/FileModel/ISaveable.h new file mode 100644 index 00000000..e1436579 --- /dev/null +++ b/OdbDesignLib/FileModel/ISaveable.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include "../odbdesign_export.h" + +namespace Odb::Lib::FileModel +{ + class ISaveable + { + public: + virtual bool Save(const std::filesystem::path& directory) = 0; + + protected: + ISaveable() = default; + }; +} \ No newline at end of file diff --git a/OdbDesignLib/FileModel/IStreamSaveable.h b/OdbDesignLib/FileModel/IStreamSaveable.h new file mode 100644 index 00000000..6ffd2696 --- /dev/null +++ b/OdbDesignLib/FileModel/IStreamSaveable.h @@ -0,0 +1,17 @@ +#pragma once + +//#include +#include +#include "../odbdesign_export.h" + +namespace Odb::Lib::FileModel +{ + class IStreamSaveable + { + public: + virtual bool Save(std::ostream& os) = 0; + + protected: + IStreamSaveable() = default; + }; +} \ No newline at end of file diff --git a/OdbDesignLib/ProductModel/Design.cpp b/OdbDesignLib/ProductModel/Design.cpp index ff32ca8c..b896626a 100644 --- a/OdbDesignLib/ProductModel/Design.cpp +++ b/OdbDesignLib/ProductModel/Design.cpp @@ -417,7 +417,7 @@ namespace Odb::Lib::ProductModel //auto subnetNumber = pToeprintRecord->subnetNumber; // -1 means no connection for the component pin - if (netNumber != (unsigned int)-1) + if (netNumber != static_cast(-1)) { if (!CreatePinConnection(refDes, netNumber, pinNumber, toeprintName)) return false; } diff --git a/OdbDesignServer/Controllers/FileUploadController.cpp b/OdbDesignServer/Controllers/FileUploadController.cpp index ad9b2d81..863dcd6c 100644 --- a/OdbDesignServer/Controllers/FileUploadController.cpp +++ b/OdbDesignServer/Controllers/FileUploadController.cpp @@ -1,5 +1,9 @@ #include "FileUploadController.h" -#include "fastcopy.h" +#include "fastmove.h" +#include +#include +#include +#include using namespace std::filesystem; using namespace Odb::Lib::App; @@ -101,13 +105,16 @@ namespace Odb::App::Server outfile << req.body; outfile.close(); - // TODO: sanitize provided filename auto safeName = sanitizeFilename(filename); - path finalPath(m_serverApp.args().designsDir()); finalPath /= safeName; - //rename(tempPath, finalPath); - auto ec = fastcopy(tempPath, finalPath, false); + + std::error_code ec; + fastmove_file(tempPath, finalPath, false, ec); + if (ec.value() != 0) + { + return crow::response(crow::status::INTERNAL_SERVER_ERROR, "failed handling new file"); + } std::string responseBody = "{ \"filename\": \"" + safeName + "\" }"; @@ -139,13 +146,13 @@ namespace Odb::App::Server if (headers_it == part_value.headers.end()) { CROW_LOG_ERROR << "No Content-Disposition found"; - return crow::response(400); + return crow::response(crow::status::BAD_REQUEST); } auto params_it = headers_it->second.params.find("filename"); if (params_it == headers_it->second.params.end()) { CROW_LOG_ERROR << "Part with name \"InputFile\" should have a file"; - return crow::response(400); + return crow::response(crow::status::BAD_REQUEST); } const std::string outfile_name = params_it->second; @@ -176,8 +183,13 @@ namespace Odb::App::Server auto safeName = sanitizeFilename(outfile_name); path finalPath(m_serverApp.args().designsDir()); finalPath /= safeName; - //rename(tempPath, finalPath); - auto ec = fastcopy(tempPath, finalPath, false); + + std::error_code ec; + fastmove_file(tempPath, finalPath, false, ec); + if (ec.value() != 0) + { + return crow::response(crow::status::INTERNAL_SERVER_ERROR, "failed handling new file"); + } CROW_LOG_INFO << " Contents written to " << outfile_name << '\n'; } @@ -217,6 +229,7 @@ namespace Odb::App::Server std::string FileUploadController::sanitizeFilename(const std::string& filename) const { + // TODO: implement sanitize filename return filename; } } \ No newline at end of file diff --git a/OdbDesignServer/main.cpp b/OdbDesignServer/main.cpp index a6458526..99f80327 100644 --- a/OdbDesignServer/main.cpp +++ b/OdbDesignServer/main.cpp @@ -1,7 +1,6 @@ // main.cpp : Source file for your target. // -#include "OdbDesignServer.h" #include "OdbDesignServerApp.h" using namespace Odb::App::Server; diff --git a/OdbDesignTests/ArchiveTests.cpp b/OdbDesignTests/ArchiveTests.cpp new file mode 100644 index 00000000..2018c988 --- /dev/null +++ b/OdbDesignTests/ArchiveTests.cpp @@ -0,0 +1,31 @@ +#include +#include +#include "Fixtures/TestDataFixture.h" +#include "libarchive_extract.h" +#include "ArchiveExtractor.h" + +using namespace std::filesystem; +using namespace Odb::Test::Fixtures; +using namespace testing; +using namespace Utils; + +namespace Odb::Test +{ + static inline constexpr char FILE_CONTENTS[] = "Hello, World!\n\n"; + + TEST_F(TestDataFixture, Test_LibArchive_CompressDir) + { + std::string fileArchiveOut; + compress_dir(getTestDataFilesDir().string().c_str(), getTestDataFilesDir().string().c_str(), "files_libarchive", fileArchiveOut); + + ASSERT_TRUE(exists(fileArchiveOut)); + } + + TEST_F(TestDataFixture, Test_ArchiveExtractor_CompressDir) + { + std::string fileArchiveOut; + ArchiveExtractor::CompressDir(getTestDataFilesDir().string(), getTestDataFilesDir().string(), "files_archiveextractor", fileArchiveOut); + + ASSERT_TRUE(exists(fileArchiveOut)); + } +} \ No newline at end of file diff --git a/OdbDesignTests/CMakeLists.txt b/OdbDesignTests/CMakeLists.txt index b68dc9e7..f7c9f901 100644 --- a/OdbDesignTests/CMakeLists.txt +++ b/OdbDesignTests/CMakeLists.txt @@ -10,7 +10,7 @@ add_executable(OdbDesignTests "DesignCacheLoadTests.cpp" "Fixtures/DesignNameValueParamTest.h" "FileArchiveTests.cpp" - "DesignTests.cpp" "ProtobufSerializationTests.cpp") + "DesignTests.cpp" "ProtobufSerializationTests.cpp" "FileReaderTests.cpp" "ArchiveTests.cpp" "CrossPlatformTests.cpp" "Fixtures/TestDataFixture.h" "Fixtures/TestDataFixture.cpp") target_link_libraries(OdbDesignTests PRIVATE GTest::gtest_main GTest::gmock_main) diff --git a/OdbDesignTests/CrossPlatformTests.cpp b/OdbDesignTests/CrossPlatformTests.cpp new file mode 100644 index 00000000..71efc72c --- /dev/null +++ b/OdbDesignTests/CrossPlatformTests.cpp @@ -0,0 +1,69 @@ +#include +#include "CrossPlatform.h" +#include + +using namespace std::filesystem; +using namespace Odb::Test::Fixtures; +using namespace testing; +using namespace Utils; + +namespace Odb::Test +{ + TEST_F(TestDataFixture, Test_CrossPlatform_GetEnvSafe_VariableExists) + { + std::string value; + ASSERT_EQ(value.size(), 0U); + ASSERT_TRUE(CrossPlatform::getenv_safe(ODB_TEST_DATA_DIR_ENV_NAME, value)); + ASSERT_STRNE(value.c_str(), ""); + ASSERT_STREQ(value.c_str(), m_testDataDir.string().c_str()); + } + + TEST_F(TestDataFixture, Test_CrossPlatform_GetEnvSafe_VariableDoesntExist) + { + std::string value; + ASSERT_EQ(value.size(), 0U); + ASSERT_FALSE(CrossPlatform::getenv_safe("FEARISTHEMINDKILLER", value)); + ASSERT_STREQ(value.c_str(), ""); + } + + TEST_F(TestDataFixture, Test_CrossPlatform_TmpNameSafe_ReturnsRandomFilename) + { + std::string value1; + ASSERT_EQ(value1.size(), 0U); + ASSERT_TRUE(CrossPlatform::tmpnam_safe(value1)); + ASSERT_STRNE(value1.c_str(), ""); + + std::string value2; + ASSERT_EQ(value2.size(), 0U); + ASSERT_TRUE(CrossPlatform::tmpnam_safe(value2)); + ASSERT_STRNE(value2.c_str(), ""); + + ASSERT_STRNE(value1.c_str(), value2.c_str()); + } + + TEST_F(TestDataFixture, Test_CrossPlatform_LocalTimeSafe_ReturnsSomeTime) + { + time_t tt{ 0 }; + ASSERT_EQ(tt, 0LL); + std::time(&tt); + ASSERT_NE(tt, 0LL); + + struct tm tm{ 0 }; + + ASSERT_EQ(tm.tm_year, 0); + ASSERT_EQ(tm.tm_mon, 0); + ASSERT_EQ(tm.tm_mday, 0); + ASSERT_EQ(tm.tm_hour, 0); + ASSERT_EQ(tm.tm_min, 0); + ASSERT_EQ(tm.tm_sec, 0); + + ASSERT_TRUE(CrossPlatform::localtime_safe(&tt, tm)); + + ASSERT_NE(tm.tm_year, 0); + ASSERT_NE(tm.tm_mon, 0); + ASSERT_NE(tm.tm_mday, 0); + ASSERT_NE(tm.tm_hour, 0); + ASSERT_NE(tm.tm_min, 0); + ASSERT_NE(tm.tm_sec, 0); + } +} \ No newline at end of file diff --git a/OdbDesignTests/FileReaderTests.cpp b/OdbDesignTests/FileReaderTests.cpp new file mode 100644 index 00000000..a1ae6d23 --- /dev/null +++ b/OdbDesignTests/FileReaderTests.cpp @@ -0,0 +1,48 @@ +#include +#include +#include "FileReader.h" +#include "Fixtures/TestDataFixture.h" + +using namespace std::filesystem; +using namespace Odb::Test::Fixtures; +using namespace testing; +using namespace Utils; + +namespace Odb::Test +{ + static inline constexpr char FILE_CONTENTS[] = "Hello, World!\r\n"; + + TEST_F(TestDataFixture, Test_FileReaderRead_Buffered) + { + auto filePath = getTestDataFilePath("filereader_test1.txt"); + + ASSERT_TRUE(exists(filePath)); + + FileReader fr(filePath); + + auto fileSize = file_size(filePath); + auto read = fr.Read(FileReader::BufferStrategy::Buffered); + ASSERT_EQ(read, fileSize); + + auto& buffer = fr.GetBuffer(); + ASSERT_EQ(buffer.size(), fileSize); + ASSERT_STREQ(buffer.data(), FILE_CONTENTS); + } + + TEST_F(TestDataFixture, Test_FileReaderRead_Unbuffered) + { + auto filePath = getTestDataFilePath("filereader_test1.txt"); + + ASSERT_TRUE(exists(filePath)); + + FileReader fr(filePath); + + auto fileSize = file_size(filePath); + auto read = fr.Read(FileReader::BufferStrategy::Unbuffered); + ASSERT_EQ(read, fileSize); + + auto& buffer = fr.GetBuffer(); + ASSERT_EQ(buffer.size(), fileSize); + ASSERT_STREQ(buffer.data(), FILE_CONTENTS); + } +} \ No newline at end of file diff --git a/OdbDesignTests/Fixtures/FileArchiveLoadFixture.cpp b/OdbDesignTests/Fixtures/FileArchiveLoadFixture.cpp index 4562438e..bfc9df0b 100644 --- a/OdbDesignTests/Fixtures/FileArchiveLoadFixture.cpp +++ b/OdbDesignTests/Fixtures/FileArchiveLoadFixture.cpp @@ -1,6 +1,11 @@ #include "FileArchiveLoadFixture.h" #include #include "Logger.h" +#include +#include +#include "App/DesignCache.h" +#include "TestDataFixture.h" +#include using namespace std::filesystem; //using namespace Odb::Lib; @@ -10,38 +15,22 @@ using namespace Utils; namespace Odb::Test::Fixtures { FileArchiveLoadFixture::FileArchiveLoadFixture() - : m_testDataDir() - , m_pDesignCache(nullptr) + : m_pDesignCache(nullptr) { } void FileArchiveLoadFixture::SetUp() { - //Logger::instance()->start(); - - ASSERT_FALSE(getTestDataDir().empty()); - m_testDataDir = getTestDataDir(); - m_testDataDir = m_testDataDir.make_preferred(); - - ASSERT_TRUE(exists(m_testDataDir)); + TestDataFixture::SetUp(); m_pDesignCache = std::unique_ptr(new DesignCache(m_testDataDir.string())); ASSERT_NE(m_pDesignCache, nullptr); } - /*static*/ std::string FileArchiveLoadFixture::getTestDataDir() - { - auto szTestDataDir = std::getenv(ODB_TEST_DATA_DIR_ENV_NAME); - if (szTestDataDir == nullptr) return ""; - //if (szTestDataDir == nullptr) throw std::runtime_error("ODB_TEST_DATA_DIR environment variable is not set"); - //if (!exists(szTestDataDir)) throw std::runtime_error("ODB_TEST_DATA_DIR environment variable is set to a non-existent directory"); - return szTestDataDir; - } - void FileArchiveLoadFixture::TearDown() - { + { if (m_removeDecompressedDirectories) - { + { if (exists(m_testDataDir)) { // delete uncompressed directories @@ -49,17 +38,20 @@ namespace Odb::Test::Fixtures { if (is_directory(entry)) { - remove_all(entry.path()); + if (std::find(KEEP_DIRECTORIES.begin(), KEEP_DIRECTORIES.end(), entry.path().filename()) == KEEP_DIRECTORIES.end()) + { + remove_all(entry.path()); + } } } } } - //Logger::instance()->stop(); - } + TestDataFixture::TearDown(); + } path FileArchiveLoadFixture::getDesignPath(const std::string& filename) const { - return m_testDataDir / filename; + return getTestDataDir() / filename; } } \ No newline at end of file diff --git a/OdbDesignTests/Fixtures/FileArchiveLoadFixture.h b/OdbDesignTests/Fixtures/FileArchiveLoadFixture.h index 44780329..15cbb492 100644 --- a/OdbDesignTests/Fixtures/FileArchiveLoadFixture.h +++ b/OdbDesignTests/Fixtures/FileArchiveLoadFixture.h @@ -2,19 +2,18 @@ #include "gtest/gtest.h" #include -#include "OdbDesign.h" -#include #include +#include +#include "TestDataFixture.h" namespace Odb::Test::Fixtures { - class FileArchiveLoadFixture : public testing::Test + class FileArchiveLoadFixture : public TestDataFixture { public: FileArchiveLoadFixture(); protected: - std::filesystem::path m_testDataDir; std::unique_ptr m_pDesignCache; const bool m_removeDecompressedDirectories = true; @@ -22,10 +21,9 @@ namespace Odb::Test::Fixtures void SetUp() override; void TearDown() override; - static std::string getTestDataDir(); std::filesystem::path getDesignPath(const std::string& filename) const; - - constexpr const static inline char ODB_TEST_DATA_DIR_ENV_NAME[] = "ODB_TEST_DATA_DIR"; + + static inline const std::vector KEEP_DIRECTORIES = { TESTDATA_FILES_DIR }; }; } diff --git a/OdbDesignTests/Fixtures/TestDataFixture.cpp b/OdbDesignTests/Fixtures/TestDataFixture.cpp new file mode 100644 index 00000000..1c6206b2 --- /dev/null +++ b/OdbDesignTests/Fixtures/TestDataFixture.cpp @@ -0,0 +1,56 @@ +#include "TestDataFixture.h" +#include +#include +#include +#include + +using namespace std::filesystem; +using namespace Utils; + +namespace Odb::Test::Fixtures +{ + TestDataFixture::TestDataFixture() + : m_testDataDir() + { + } + + void TestDataFixture::SetUp() + { + if (ENABLE_TEST_LOGGING) + { + Logger::instance()->start(); + } + + ASSERT_FALSE(getTestDataDir().empty()); + m_testDataDir = getTestDataDir(); + m_testDataDir = m_testDataDir.make_preferred(); + ASSERT_TRUE(exists(m_testDataDir)); + } + + void TestDataFixture::TearDown() + { + if (ENABLE_TEST_LOGGING) + { + Logger::instance()->stop(); + } + } + + path TestDataFixture::getTestDataDir() + { + auto szTestDataDir = std::getenv(ODB_TEST_DATA_DIR_ENV_NAME); + if (szTestDataDir == nullptr) return ""; + //if (szTestDataDir == nullptr) throw std::runtime_error("ODB_TEST_DATA_DIR environment variable is not set"); + //if (!exists(szTestDataDir)) throw std::runtime_error("ODB_TEST_DATA_DIR environment variable is set to a non-existent directory"); + return szTestDataDir; + } + + path TestDataFixture::getTestDataFilesDir() const + { + return m_testDataDir / TESTDATA_FILES_DIR; + } + + path TestDataFixture::getTestDataFilePath(const std::string& filename) const + { + return getTestDataFilesDir() / filename; + } +} \ No newline at end of file diff --git a/OdbDesignTests/Fixtures/TestDataFixture.h b/OdbDesignTests/Fixtures/TestDataFixture.h new file mode 100644 index 00000000..74457b52 --- /dev/null +++ b/OdbDesignTests/Fixtures/TestDataFixture.h @@ -0,0 +1,32 @@ +#pragma once + +#include "gtest/gtest.h" +#include +#include + +namespace Odb::Test::Fixtures +{ + class TestDataFixture : public testing::Test + { + public: + TestDataFixture(); + + protected: + std::filesystem::path m_testDataDir; + + const bool m_removeDecompressedDirectories = true; + + virtual void SetUp() override; + virtual void TearDown() override; + + static std::filesystem::path getTestDataDir(); + std::filesystem::path getTestDataFilesDir() const; + std::filesystem::path getTestDataFilePath(const std::string& filename) const; + + static inline constexpr const char ODB_TEST_DATA_DIR_ENV_NAME[] = "ODB_TEST_DATA_DIR"; + static inline constexpr const char TESTDATA_FILES_DIR[] = "FILES"; + + static inline constexpr bool ENABLE_TEST_LOGGING = false; + + }; +} diff --git a/OdbDesignTests/TestTests.cpp b/OdbDesignTests/TestTests.cpp index 2a85f707..df63ac06 100644 --- a/OdbDesignTests/TestTests.cpp +++ b/OdbDesignTests/TestTests.cpp @@ -1,6 +1,6 @@ #include #include -#include "Fixtures/FileArchiveLoadFixture.h" +#include "Fixtures/TestDataFixture.h" #include using namespace std::filesystem; @@ -26,13 +26,13 @@ namespace Odb::Test SUCCEED(); } - TEST_F(FileArchiveLoadFixture, TestDataDirEnvironmentVariablesExists) + TEST_F(TestDataFixture, TestDataDirEnvironmentVariablesExists) { //ASSERT_FALSE(getTestDataDir().empty()); - EXPECT_THAT(getTestDataDir(), Not(IsEmpty())); + EXPECT_THAT(getTestDataDir().string(), Not(IsEmpty())); } - TEST_F(FileArchiveLoadFixture, TestDataDirDirectoryExists) + TEST_F(TestDataFixture, TestDataDirDirectoryExists) { ASSERT_FALSE(getTestDataDir().empty()); EXPECT_TRUE(exists(getTestDataDir())); diff --git a/Utils/ArchiveExtractor.cpp b/Utils/ArchiveExtractor.cpp index 9112c457..a1f995b1 100644 --- a/Utils/ArchiveExtractor.cpp +++ b/Utils/ArchiveExtractor.cpp @@ -1,4 +1,5 @@ #include "ArchiveExtractor.h" +#include "ArchiveExtractor.h" #include #include "libarchive_extract.h" #include "Logger.h" @@ -155,4 +156,11 @@ namespace Utils return uncompressedPath; } + + /*static*/ bool ArchiveExtractor::CompressDir(const std::string& srcDir, const std::string& destDir, + const std::string& archiveName, std::string& fileOut, + CompressionType type) + { + return compress_dir(srcDir.c_str(), destDir.c_str(), archiveName.c_str(), fileOut, type); + } } \ No newline at end of file diff --git a/Utils/ArchiveExtractor.h b/Utils/ArchiveExtractor.h index 16a53ee1..183c0341 100644 --- a/Utils/ArchiveExtractor.h +++ b/Utils/ArchiveExtractor.h @@ -3,6 +3,7 @@ #include #include #include "utils_export.h" +#include "libarchive_extract.h" namespace Utils @@ -35,6 +36,10 @@ namespace Utils static std::filesystem::path getUncompressedFilePath(const std::filesystem::path& directory, const std::string& filename); + static bool CompressDir(const std::string& srcDir, const std::string& destDir, + const std::string& archiveName, std::string& fileOut, + CompressionType type = CompressionType::TarGzip); + inline static bool ALLOW_ALL_ARCHIVE_EXTENSION_TYPES = false; constexpr static inline const char* SupportedExtensions[] = { "tgz", "tar.gz", "gz", "zip", "Z", "gzip", "tar" }; diff --git a/Utils/CMakeLists.txt b/Utils/CMakeLists.txt index 04432a2d..452121ea 100644 --- a/Utils/CMakeLists.txt +++ b/Utils/CMakeLists.txt @@ -1,7 +1,7 @@ # CMakeList.txt : CMake project for OdbDesignServer # -add_library(Utils SHARED "utils_export.h" "ExitCode.h" "ThreadSafeQueue.h" "WorkQueueLoopThread.h" "Logger.h" "Logger.cpp" "CommandLineArgs.h" "CommandLineArgs.cpp" "bin2ascii.h" "ArchiveExtractor.cpp" "ArchiveExtractor.h" "libarchive_extract.cpp" "libarchive_extract.h" "str_utils.cpp" "str_utils.h" "IJsonable.h" "IJsonable.cpp" "CrowReturnable.h" "JsonCrowReturnable.h" "timestamp.h" "timestamp.cpp" "StopWatch.h" "StopWatch.cpp" "UrlEncoding.h" "UrlEncoding.cpp" "StringVector.h" "equals_within.h" "equals_within.cpp" "crow_win.h" "fastcopy.h" "fastcopy.cpp") +add_library(Utils SHARED "utils_export.h" "ExitCode.h" "ThreadSafeQueue.h" "WorkQueueLoopThread.h" "Logger.h" "Logger.cpp" "CommandLineArgs.h" "CommandLineArgs.cpp" "bin2ascii.h" "ArchiveExtractor.cpp" "ArchiveExtractor.h" "libarchive_extract.cpp" "libarchive_extract.h" "str_utils.cpp" "str_utils.h" "IJsonable.h" "IJsonable.cpp" "CrowReturnable.h" "JsonCrowReturnable.h" "timestamp.h" "timestamp.cpp" "StopWatch.h" "StopWatch.cpp" "UrlEncoding.h" "UrlEncoding.cpp" "StringVector.h" "equals_within.h" "equals_within.cpp" "crow_win.h" "fastmove.h" "fastmove.cpp" "FileReader.h" "FileReader.cpp" "EnumMap.h" "EnumMap.cpp" "CrossPlatform.h" "CrossPlatform.cpp") # state that anybody linking to us needs to include the current source dir, # while we don't. diff --git a/Utils/CrossPlatform.cpp b/Utils/CrossPlatform.cpp new file mode 100644 index 00000000..d9164c87 --- /dev/null +++ b/Utils/CrossPlatform.cpp @@ -0,0 +1,84 @@ +#include "CrossPlatform.h" +#include "macros.h" +#include +#include +#include +#include +#include + +namespace Utils +{ + bool CrossPlatform::localtime_safe(const std::time_t* time, struct std::tm& tmOut) + { + #if (IS_WINDOWS) + { + return (0 == localtime_s(&tmOut, time)); + } + #elif (IS_LINUX || IS_APPLE) + { + if (nullptr != localtime_r(time, &tmOut)) + { + return true; + } + } + #endif + return false; + } + + bool CrossPlatform::getenv_safe(const char* env_var, std::string& envValueOut) + { + #if (IS_WINDOWS) + { + char* envValue = nullptr; + size_t len = 0; + if (0 == _dupenv_s(&envValue, &len, env_var)) + { + if (envValue != nullptr && len > 0) + { + envValueOut = envValue; + free(envValue); + return true; + } + } + } + #elif (IS_LINUX || IS_APPLE) + { + auto val = std::getenv(env_var); + if (val != nullptr) + { + envValueOut = val; + return true; + } + + } + #endif + + return false; + } + + bool CrossPlatform::tmpnam_safe(std::string& tempNameOut) + { + #if (IS_WINDOWS) + { + char szTempName[L_tmpnam_s]; + if (0 == tmpnam_s(szTempName, L_tmpnam_s)) + { + tempNameOut = szTempName; + return true; + } + } + #elif (IS_LINUX || IS_APPLE) + { + // mkstemp + char szTempName[L_tmpnam_s]{ 0 }; + if (nullptr != std::tmpnam(szTempName)) + { + tempNameOut = szTempName; + return true; + } + } + #endif + + return false; + } +} \ No newline at end of file diff --git a/Utils/CrossPlatform.h b/Utils/CrossPlatform.h new file mode 100644 index 00000000..2bc94147 --- /dev/null +++ b/Utils/CrossPlatform.h @@ -0,0 +1,17 @@ +#pragma once + +//#include +#include "utils_export.h" +#include + +namespace Utils +{ + class UTILS_EXPORT CrossPlatform + { + public: + static bool localtime_safe(const std::time_t* time, struct std::tm& tmOut); + static bool getenv_safe(const char* env_var, std::string& envValueOut); + static bool tmpnam_safe(std::string& tempNameOut); + + }; +} diff --git a/Utils/EnumMap.cpp b/Utils/EnumMap.cpp new file mode 100644 index 00000000..805dabd4 --- /dev/null +++ b/Utils/EnumMap.cpp @@ -0,0 +1 @@ +#include "EnumMap.h" \ No newline at end of file diff --git a/Utils/EnumMap.h b/Utils/EnumMap.h new file mode 100644 index 00000000..3f0a8ffb --- /dev/null +++ b/Utils/EnumMap.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include "str_utils.h" + +namespace Utils +{ + template + class EnumMap + { + public: + EnumMap(const std::vector& names, bool caseInsensitive = false); + + const std::string& getValue(const E e) const; + E getValue(const std::string& name) const; + + bool contains(const std::string& name) const; + bool contains(const E e) const; + + //typedef std::exception Exception; + + private: + const std::vector m_names; + const bool m_bCaseInsensitive; + + }; + + template + inline EnumMap::EnumMap(const std::vector& names, bool caseInsensitive) + : m_names(names) + , m_bCaseInsensitive(caseInsensitive) + {} + + template + inline const std::string& EnumMap::getValue(const E e) const + { + auto index = static_cast(e); + if (index < 0 || index >= m_names.size()) + { + std::string msg = "no name found for value: " + std::to_string(index); + throw std::invalid_argument(msg.c_str()); + } + return m_names[index]; + } + + template + inline E EnumMap::getValue(const std::string& name) const + { + std::vector::const_iterator findIt; + if (m_bCaseInsensitive) + { + findIt = Utils::find_str_icmp(m_names.begin(), m_names.end(), name); + } + else + { + findIt = std::find(m_names.begin(), m_names.end(), name); + } + + if (findIt == m_names.end()) + { + std::string msg = "no value found for name: (" + name + ")"; + throw std::invalid_argument(msg.c_str()); + } + return static_cast(std::distance(m_names.begin(), findIt)); + } + + template + inline bool EnumMap::contains(const std::string& name) const + { + return std::find(m_names.begin(), m_names.end(), name) != m_names.end(); + } + + template + inline bool EnumMap::contains(const E e) const + { + auto index = static_cast(e); + return index >= 0 && index < m_names.size(); + } +} diff --git a/Utils/FileReader.cpp b/Utils/FileReader.cpp new file mode 100644 index 00000000..47833149 --- /dev/null +++ b/Utils/FileReader.cpp @@ -0,0 +1,78 @@ +#include "FileReader.h" +#include "FileReader.h" +#include +#include +#include +#include + +using namespace std::filesystem; + +namespace Utils +{ + FileReader::FileReader(const std::filesystem::path& filePath) + : m_filePath(filePath) + { + } + + //FileReader::FileReader(const std::filesystem::path& filePath, std::ios::openmode mode) + // : FileReader(filePath, mode, false) + //{ + //} + + //FileReader::FileReader(const std::filesystem::path& filePath, std::ios::openmode mode, bool unbuffered) + // : m_filePath(filePath) + // , m_mode(mode) + // , m_unbuffered(unbuffered) + //{ + //} + + long long FileReader::Read(BufferStrategy bufferStrategy /*= BufferStrategy::Buffered*/, std::ios::openmode mode /*= DEFAULT_OPENMODE*/) + { + auto read = 0LL; + + std::ifstream file(m_filePath, mode); + if (file.is_open()) + { + if (bufferStrategy == BufferStrategy::Unbuffered) + { + long long size = file_size(m_filePath); + m_buffer.resize(size); + file.read(m_buffer.data(), size); + if (file.gcount() == size) + { + read = size; + } + } + else + { + char szBuffer[BUFFER_SIZE]{ 0 }; + while (true) + { + file.read(szBuffer, sizeof(szBuffer)); + auto readin = file.gcount(); + if (readin < 1) + { + break; + } + m_buffer.insert(m_buffer.end(), szBuffer, szBuffer + readin); + read += readin; + } + } + m_buffer.push_back(0); + m_buffer.resize(read); + file.close(); + } + + return read; + } + + void FileReader::Clear() + { + m_buffer.clear(); + } + + const std::vector& Utils::FileReader::GetBuffer() const + { + return m_buffer; + } +} \ No newline at end of file diff --git a/Utils/FileReader.h b/Utils/FileReader.h new file mode 100644 index 00000000..d7b1029b --- /dev/null +++ b/Utils/FileReader.h @@ -0,0 +1,32 @@ +#pragma once + +#include "utils_export.h" +#include +#include + +namespace Utils +{ + class UTILS_EXPORT FileReader + { + public: + enum class BufferStrategy + { + Unbuffered, + Buffered + }; + + FileReader(const std::filesystem::path& filePath); + + long long Read(BufferStrategy bufferStrategy = BufferStrategy::Buffered, std::ios::openmode mode = DEFAULT_OPENMODE); + void Clear(); + const std::vector& GetBuffer() const; + + private: + const std::filesystem::path m_filePath; + std::vector m_buffer; + + static inline constexpr std::ios::openmode DEFAULT_OPENMODE = std::ios::in|std::ios::binary; + static inline constexpr size_t BUFFER_SIZE = 1024; + + }; +} diff --git a/Utils/fastcopy.cpp b/Utils/fastcopy.cpp deleted file mode 100644 index f6fab078..00000000 --- a/Utils/fastcopy.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "fastcopy.h" - -using namespace std; -using namespace std::filesystem; - -namespace Utils -{ - error_code copy(const path& source, const path& dest, bool overwriteExisting) - { - error_code ec; - - auto options = copy_options::none; - if (overwriteExisting) - { - options = copy_options::overwrite_existing; - } - - if (copy_file(source, dest, options, ec)) - { - remove(source, ec); - } - - return ec; - } - - error_code copy(const string& source, const string& dest, bool overwriteExisting) - { - return copy(path(source), path(dest), overwriteExisting); - } - - error_code fastcopy(const string& source, const string& dest, bool overwriteExisting) - { - return fastcopy(path(source), path(dest), overwriteExisting); - } - - error_code fastcopy(const path& source, const path& dest, bool overwriteExisting) - { - error_code ec; - - try - { - rename(source, dest); - } - catch (filesystem_error& fe) - { - // can't rename across devices- try standard copy and remove - if (fe.code() == std::errc::cross_device_link) - { - ec = copy(source, dest, overwriteExisting); - } - else - { - throw fe; - } - } - - return ec; - } -} \ No newline at end of file diff --git a/Utils/fastcopy.h b/Utils/fastcopy.h deleted file mode 100644 index 05d2a373..00000000 --- a/Utils/fastcopy.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include -#include "utils_export.h" - -namespace Utils -{ - UTILS_EXPORT std::error_code copy(const std::filesystem::path& source, const std::filesystem::path& dest, bool overwriteExisting); - UTILS_EXPORT std::error_code copy(const std::string& source, const std::string& dest, bool overwriteExisting); - - UTILS_EXPORT std::error_code fastcopy(const std::string& source, const std::string& dest, bool overwriteExisting); - UTILS_EXPORT std::error_code fastcopy(const std::filesystem::path& source, const std::filesystem::path& dest, bool overwriteExisting); -} diff --git a/Utils/fastmove.cpp b/Utils/fastmove.cpp new file mode 100644 index 00000000..d1dabb3b --- /dev/null +++ b/Utils/fastmove.cpp @@ -0,0 +1,49 @@ +#include "fastmove.h" +#include + +using namespace std; +using namespace std::filesystem; + +namespace Utils +{ + void move_file(const path& source, const path& dest, bool overwriteExisting, std::error_code& ec) + { + auto options = copy_options::none; + if (overwriteExisting) + { + options |= copy_options::overwrite_existing; + } + + if (copy_file(source, dest, options, ec) && + ec.value() == 0) + { + remove(source, ec); + } + } + + void fastmove_file(const path& source, const path& dest, bool overwriteExisting, std::error_code& ec) + { + //try + { + rename(source, dest, ec); + if (ec.value() == static_cast(std::errc::cross_device_link)) + { + move_file(source, dest, overwriteExisting, ec); + } + } + //catch (filesystem_error& fe) + //{ + // // can't rename across devices- try standard copy and remove + // if (fe.code() == std::errc::cross_device_link) + // { + // ec = move_file(source, dest, overwriteExisting); + // } + // else + // { + // throw fe; + // } + //} + + //return ec; + } +} \ No newline at end of file diff --git a/Utils/fastmove.h b/Utils/fastmove.h new file mode 100644 index 00000000..75c1812f --- /dev/null +++ b/Utils/fastmove.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include "utils_export.h" +#include + +namespace Utils +{ + UTILS_EXPORT void move_file(const std::filesystem::path& source, const std::filesystem::path& dest, bool overwriteExisting, std::error_code& ec); + UTILS_EXPORT void fastmove_file(const std::filesystem::path& source, const std::filesystem::path& dest, bool overwriteExisting, std::error_code& ec); +} diff --git a/Utils/libarchive_extract.cpp b/Utils/libarchive_extract.cpp index 2985709c..c0c7ee0e 100644 --- a/Utils/libarchive_extract.cpp +++ b/Utils/libarchive_extract.cpp @@ -4,6 +4,10 @@ #include #include #include "Logger.h" +#include +#include "FileReader.h" +#include +#include // from: https://github.com/libarchive/libarchive/wiki/Examples#user-content-A_Complete_Extractor @@ -82,7 +86,8 @@ namespace Utils r = archive_write_header(ext, entry); if (r < ARCHIVE_OK) fprintf(stderr, "archive_write_header failed: %s\n", archive_error_string(ext)); - else if (archive_entry_size(entry) > 0) { + else if (archive_entry_size(entry) > 0) + { r = copy_data(a, ext); if (r < ARCHIVE_OK) fprintf(stderr, "%s\n", archive_error_string(ext)); @@ -105,7 +110,7 @@ namespace Utils archive_write_free(ext); return true; - } + } int copy_data(struct archive* ar, struct archive* aw) { @@ -115,17 +120,121 @@ namespace Utils la_int64_t offset; la_ssize_t written; - for (;;) { + for (;;) + { r = archive_read_data_block(ar, &buff, &size, &offset); if (r == ARCHIVE_EOF) + { return (ARCHIVE_OK); - if (r < ARCHIVE_OK) + } + else if (r < ARCHIVE_OK) + { return (r); + } + written = archive_write_data_block(aw, buff, size, offset); - if (written < ARCHIVE_OK) { + if (written < ARCHIVE_OK) + { fprintf(stderr, "%s\n", archive_error_string(aw)); return (r); } } } + + constexpr inline static const char* TGZ_EXTENSION = ".tgz"; + constexpr static const int READ_BUFF_SIZE = 1024; + + bool compress_dir(const char* srcDir, const char* destDir, const char* archiveName, std::string& fileOut, CompressionType type /* = CompressionType::TarGzip*/) + { + path destArchivePath = path(destDir) / archiveName; + switch (type) + { + case CompressionType::TarGzip: + destArchivePath.replace_extension(TGZ_EXTENSION); + break; + } + + struct archive* a = archive_write_new(); + //if (archive_write_set_format_v7tar(a) != ARCHIVE_OK) return false; + if (archive_write_set_format_ustar(a) != ARCHIVE_OK) return false; + //if (archive_write_set_format_gnutar(a) != ARCHIVE_OK) return false; + if (archive_write_add_filter_gzip(a) != ARCHIVE_OK) return false; + + if (archive_write_open_filename(a, destArchivePath.string().c_str()) != ARCHIVE_OK) return false; + + //// add top-level directory to the archive + auto root_entry = archive_entry_new(); + //auto rootDir = path(archiveName); + auto rootDir = path(srcDir).filename(); + archive_entry_set_pathname(root_entry, rootDir.string().c_str()); + archive_entry_set_filetype(root_entry, AE_IFDIR); + if (archive_write_header(a, root_entry) != ARCHIVE_OK) return false; + archive_entry_free(root_entry); + + struct stat st; + + // add files to the archive + for (const auto& it : recursive_directory_iterator(srcDir)) + { + if (it.is_directory()) + { + auto relativePath = relative(it.path(), srcDir); + auto dir_entry = archive_entry_new(); + archive_entry_set_pathname(dir_entry, (rootDir / relativePath).string().c_str()); + archive_entry_set_filetype(dir_entry, AE_IFDIR); + stat(it.path().string().c_str(), &st); + archive_entry_copy_stat(dir_entry, &st); + if (archive_write_header(a, dir_entry) != ARCHIVE_OK) return false; + archive_write_finish_entry(a); + archive_entry_free(dir_entry); + } + else if (it.is_regular_file()) + { + // write header + auto relativePath = relative(it.path(), srcDir); + auto file_entry = archive_entry_new(); + archive_entry_set_pathname(file_entry, (rootDir / relativePath).string().c_str()); + stat(it.path().string().c_str(), &st); + archive_entry_set_filetype(file_entry, AE_IFREG); + archive_entry_set_size(file_entry, st.st_size); // Note 2 + //archive_entry_set_mtime(file_entry, st.st_mtime, 0); + //archive_entry_set_perm(file_entry, st.st_mode?); + archive_entry_copy_stat(file_entry, &st); + if (archive_write_header(a, file_entry) != ARCHIVE_OK) return false; + + std::ifstream ifs(it.path(), std::ios::in | std::ios::binary); + if (!ifs.is_open()) return false; + char szBuffer[READ_BUFF_SIZE]{ 0 }; + while (true) + { + ifs.read(szBuffer, sizeof(szBuffer)); + auto readin = ifs.gcount(); + if (readin < 1) + { + break; + } + if (archive_write_data(a, szBuffer, readin) != readin) return false; + } + ifs.close(); + + // read file and write to archive + //FileReader fr(it.path()); + //auto read = fr.Read(); + //if (read > 0) + //{ + // auto& buffer = fr.GetBuffer(); + // if (archive_write_data(a, buffer.data(), read) != read) return false; + //} + archive_write_finish_entry(a); + archive_entry_free(file_entry); + } + } + + // clean up + archive_write_close(a); + archive_write_free(a); + + fileOut = destArchivePath.string(); + return true; + } } \ No newline at end of file diff --git a/Utils/libarchive_extract.h b/Utils/libarchive_extract.h index f5f8b2f8..a1badeea 100644 --- a/Utils/libarchive_extract.h +++ b/Utils/libarchive_extract.h @@ -1,6 +1,18 @@ #pragma once +#include "utils_export.h" + namespace Utils { + enum class CompressionType + { + TarGzip + }; + bool extract(const char* filename, const char* destDir); + UTILS_EXPORT bool compress_dir(const char* srcDir, + const char* destDir, + const char* archiveName, + std::string& fileOut, + CompressionType type = CompressionType::TarGzip); } diff --git a/Utils/macros.h b/Utils/macros.h index fd1b580d..4cd57e1e 100644 --- a/Utils/macros.h +++ b/Utils/macros.h @@ -6,9 +6,61 @@ namespace Utils { - constexpr static inline bool IsMsvc() + +#if defined(_WIN32) // or _MSC_VER +# define IS_WINDOWS (1) +#else +# define IS_WINDOWS (0) +#endif + +#if defined(__linux__) +# define IS_LINUX (1) +#else +# define IS_LINUX (0) +#endif + +#if defined(__APPLE__) +# define IS_APPLE (1) +#else +# define IS_APPLE (0) +#endif + +#if defined(__unix__) +# define IS_UNIX (1) +#else +# define IS_UNIX (0) +#endif + + constexpr static inline bool IsWindows() + { + #if IS_WINDOWS + return true; + #else + return false; + #endif + } + + constexpr static inline bool IsApple() + { + #if IS_APPLE + return true; + #else + return false; + #endif + } + + constexpr static inline bool IsLinux() + { + #if IS_LINUX + return true; + #else + return false; + #endif + } + + constexpr static inline bool IsUnix() { - #if defined(_MSC_VER) + #if IS_UNIX return true; #else return false; diff --git a/Utils/str_utils.cpp b/Utils/str_utils.cpp index 08fc797f..9a329be6 100644 --- a/Utils/str_utils.cpp +++ b/Utils/str_utils.cpp @@ -1,14 +1,21 @@ #include "str_utils.h" +#include "str_utils.h" +#include "str_utils.h" +#include "str_utils.h" +#include "str_utils.h" #include #include #include +#include namespace Utils { + + static bool case_insensitive_compare(const std::string& s1, const std::string& s2); + //! std::isspace(char) can only handle chars in the range [0,255] //! so we need to cast the it's argument to unsigned char - // trim from start (in place) std::string& str_ltrim(std::string& s) { @@ -127,5 +134,52 @@ namespace Utils std::string copy; std::transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c) { return std::toupper(c); }); return copy; + } + + bool str_iequals(const std::string& s1, const std::string& s2) + { + return case_insensitive_compare(s1, s2); + } + + std::vector::iterator find_str_icmp(std::vector::iterator first, std::vector::iterator last, const std::string& val) + { + return std::find_if(first, last, [&val](const std::string& s) { return case_insensitive_compare(s, val); }); } + + std::vector::const_iterator find_str_icmp(const std::vector::const_iterator first, const std::vector::const_iterator last, const std::string& val) + { + return std::find_if(first, last, [&val](const std::string& s) { return case_insensitive_compare(s, val); }); + } + + static bool case_insensitive_compare(const std::string& s1, const std::string& s2) + { + return std::equal( + s1.begin(), s1.end(), + s2.begin(), s2.end(), + [](char c1, char c2) + { + return std::tolower(c1) == std::tolower(c2); + }); + } + + // UTILS_EXPORT std::string Utils::str_replace(const std::string& s, const std::string& from, const std::string& to) + // { + // std::string result; + //result.reserve(s.length()); // avoids a few memory allocations + + //std::string::size_type lastPos = 0; + //std::string::size_type findPos; + + // while (std::string::npos != (findPos = s.find(from, lastPos))) + // { + // result.append(s, lastPos, findPos - lastPos); + // result += to; + // lastPos = findPos + from.length(); + //} + + //// Care for the rest after last occurrence + //result += s.substr(lastPos); + + //return result; + // } } diff --git a/Utils/str_utils.h b/Utils/str_utils.h index cb948619..8dad67ca 100644 --- a/Utils/str_utils.h +++ b/Utils/str_utils.h @@ -2,6 +2,7 @@ #include #include "utils_export.h" +#include namespace Utils @@ -37,4 +38,17 @@ namespace Utils UTILS_EXPORT std::string str_to_lower_copy(const std::string& s); UTILS_EXPORT std::string str_to_upper_copy(const std::string& s); + + //UTILS_EXPORT std::string str_replace(const std::string& s, const std::string& from, const std::string& to); + + UTILS_EXPORT bool str_iequals(const std::string& s1, const std::string& s2); + + UTILS_EXPORT std::vector::iterator find_str_icmp(std::vector::iterator first, + std::vector::iterator last, + const std::string& val); + + UTILS_EXPORT std::vector::const_iterator find_str_icmp(const std::vector::const_iterator first, + const std::vector::const_iterator last, + const std::string& val); + } \ No newline at end of file diff --git a/docs/BUILD.md b/docs/BUILD.md index 2aa42192..d920d1aa 100644 --- a/docs/BUILD.md +++ b/docs/BUILD.md @@ -72,14 +72,14 @@ Then run the commands from one or more of the sections below, based on what you ###### Windows ```Bash -$ cmake --preset x64-debug +$ cmake --preset x64-release $ cmake --build --preset x64-release ``` ###### Linux ```Bash -$ cmake --preset linux-debug +$ cmake --preset linux-release $ cmake --build --preset linux-release ``` @@ -99,4 +99,4 @@ From the root of the source directory... ```Bash $ docker compose up -``` \ No newline at end of file +``` diff --git a/docs/README.md b/docs/README.md index 0f7626d7..f9a3e1f2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -225,14 +225,14 @@ Then run the commands from one or more of the sections below, based on what you ###### Windows ```Bash -$ cmake --preset x64-debug +$ cmake --preset x64-release $ cmake --build --preset x64-release ``` ###### Linux ```Bash -$ cmake --preset linux-debug +$ cmake --preset linux-release $ cmake --build --preset linux-release ```