diff --git a/src/lay/lay/lay.pro b/src/lay/lay/lay.pro index 3500502b8..23295c1f8 100644 --- a/src/lay/lay/lay.pro +++ b/src/lay/lay/lay.pro @@ -33,6 +33,7 @@ HEADERS = \ layResourceHelpProvider.h \ layRuntimeErrorForm.h \ layReaderErrorForm.h \ + laySaltParsedURL.h \ laySearchReplaceConfigPage.h \ laySearchReplaceDialog.h \ laySearchReplacePropertiesWidgets.h \ @@ -144,6 +145,7 @@ SOURCES = \ layResourceHelpProvider.cc \ layRuntimeErrorForm.cc \ layReaderErrorForm.cc \ + laySaltParsedURL.cc \ laySearchReplaceConfigPage.cc \ laySearchReplaceDialog.cc \ laySearchReplacePlugin.cc \ diff --git a/src/lay/lay/laySaltParsedURL.cc b/src/lay/lay/laySaltParsedURL.cc new file mode 100644 index 000000000..8df57de5f --- /dev/null +++ b/src/lay/lay/laySaltParsedURL.cc @@ -0,0 +1,149 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "laySaltParsedURL.h" +#include "laySaltGrain.h" +#include "tlString.h" + +namespace lay +{ + +static void +parse_git_url (tl::Extractor &ex, std::string &url, std::string &branch, std::string &subfolder) +{ + const char *org_str = ex.get (); + + std::string w; + // protocol (http:) + ex.try_read_word (w, "") && ex.test (":"); + while (! ex.at_end () && ex.test ("/")) { + ; + } + // server ("www.klayout.de") + while (! ex.at_end () && (*ex != '/' && *ex != '+')) { + ++ex; + } + + while (! ex.at_end ()) { + + ++ex; + + const char *c1 = ex.get (); + while (! ex.at_end () && (*ex != '/' && *ex != '+' && *ex != '[')) { + ++ex; + } + const char *c2 = ex.get (); + + std::string comp (c1, c2 - c1); + + if ((! ex.at_end () && *ex == '+') || comp.find (".git") == comp.size () - 4) { + // subfolder starts here + break; + } + + } + + url = std::string (org_str, ex.get () - org_str); + + if (ex.at_end ()) { + return; + } + + if (*ex == '/') { + while (! ex.at_end () && *ex == '/') { + ++ex; + } + } else if (*ex == '+') { + ++ex; + } + + { + const char *c1 = ex.get (); + while (! ex.at_end () && *ex != '[') { + ++ex; + } + const char *c2 = ex.get (); + subfolder = std::string (c1, c2 - c1); + } + + if (! ex.at_end () && *ex == '[') { + + // explicit branch + ++ex; + const char *c1 = ex.get (); + while (! ex.at_end () && *ex != ']') { + ++ex; + } + const char *c2 = ex.get (); + branch = std::string (c1, c2 - c1); + + } else if (! subfolder.empty ()) { + + // SVN emulation + + auto parts = tl::split (subfolder, "/"); + if (parts.size () >= 1 && parts.back () == "trunk") { + + branch = "HEAD"; + parts.pop_back (); + subfolder = tl::join (parts, "/"); + + } else if (parts.size () >= 2 && parts[parts.size () - 2] == "tags") { + + branch = "refs/tags/" + parts.back (); + parts.pop_back (); + parts.pop_back (); + subfolder = tl::join (parts, "/"); + + } else if (parts.size () >= 2 && parts[parts.size () - 2] == "branches") { + + branch = "refs/heads/" + parts.back (); + parts.pop_back (); + parts.pop_back (); + subfolder = tl::join (parts, "/"); + + } + + } +} + +SaltParsedURL::SaltParsedURL (const std::string &url) + : m_protocol (lay::DefaultProtocol) +{ + tl::Extractor ex (url.c_str ()); + if (ex.test ("svn") && ex.test ("+")) { + m_protocol = lay::WebDAV; + m_url = ex.get (); + return; + } + + ex = tl::Extractor (url.c_str ()); + if (ex.test ("git") && ex.test ("+")) { + m_protocol = lay::Git; + parse_git_url (ex, m_url, m_branch, m_subfolder); + return; + } + + m_url = url; +} + +} diff --git a/src/lay/lay/laySaltParsedURL.h b/src/lay/lay/laySaltParsedURL.h new file mode 100644 index 000000000..bc076f76b --- /dev/null +++ b/src/lay/lay/laySaltParsedURL.h @@ -0,0 +1,106 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_laySaltParsedURL +#define HDR_laySaltParsedURL + +#include "layCommon.h" +#include "laySaltGrain.h" + +namespace lay +{ + +/** + * @brief A class representing a SaltGrain URL + * + * The URL is parsed into protocol, branch, URL and subfolder if applicable. + * Some heuristics is applied to decompose parts. + * + * SVN URLs: + * https://server.com/repo/trunk -> protocol=DefaultProtocol, url="https://server.com/repo/trunk", branch="", subfolder="" + * svn+https://server.com/repo/trunk -> protocol=WebDAV, url="https://server.com/repo/trunk", branch="", subfolder="" + * + * Git URL heuristics: + * git+https://server.com/repo.git -> protocol=Git, url="https://server.com/repo.git", branch="", subfolder="" + * git+https://server.com/repo.git/sub/folder -> protocol=Git, url="https://server.com/repo.git", branch="", subfolder="sub/folder" + * git+https://server.com/repo+sub/folder -> protocol=Git, url="https://server.com/repo", branch="", subfolder="sub/folder" + * git+https://server.com/repo.git[v1.0] -> protocol=Git, url="https://server.com/repo.git", branch="v1.0", subfolder="" + * git+https://server.com/repo.git/sub/folder[refs/tags/1.0] -> protocol=Git, url="https://server.com/repo.git", branch="refs/tags/1.0", subfolder="sub/folder" + * git+https://server.com/repo.git/trunk -> protocol=Git, url="https://server.com/repo.git", branch="HEAD", subfolder="" + * git+https://server.com/repo.git/sub/folder/trunk -> protocol=Git, url="https://server.com/repo.git", branch="HEAD", subfolder="sub/folder" + * git+https://server.com/repo.git/branches/release -> protocol=Git, url="https://server.com/repo.git", branch="refs/heads/release", subfolder="" + * git+https://server.com/repo.git/tags/1.9 -> protocol=Git, url="https://server.com/repo.git", branch="refs/tags/1.9", subfolder="" + * git+https://server.com/repo.git/sub/folder/tags/1.9 -> protocol=Git, url="https://server.com/repo.git", branch="refs/tags/1.9", subfolder="sub/folder" + */ + +class LAY_PUBLIC SaltParsedURL +{ +public: + /** + * @brief Constructor: creates an URL from the given generic URL string + * + * This will decompose the URL into the parts and fill protocol, branch and subfolder fields. + */ + SaltParsedURL (const std::string &url); + + /** + * @brief Gets the basic URL + */ + const std::string &url () const + { + return m_url; + } + + /** + * @brief Gets the subfolder string + */ + const std::string &subfolder () const + { + return m_subfolder; + } + + /** + * @brief Gets the branch string + */ + const std::string &branch () const + { + return m_branch; + } + + /** + * @brief Gets the protocol + */ + lay::Protocol protocol () const + { + return m_protocol; + } + +private: + std::string m_url; + std::string m_branch; + std::string m_subfolder; + lay::Protocol m_protocol; +}; + +} + +#endif diff --git a/src/lay/unit_tests/laySaltParsedURLTests.cc b/src/lay/unit_tests/laySaltParsedURLTests.cc new file mode 100644 index 000000000..3b4985d88 --- /dev/null +++ b/src/lay/unit_tests/laySaltParsedURLTests.cc @@ -0,0 +1,132 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "laySaltParsedURL.h" +#include "tlUnitTest.h" + +TEST (1_Basic) +{ + lay::SaltParsedURL purl ("https://server.com/repo/trunk"); + EXPECT_EQ (purl.protocol () == lay::DefaultProtocol, true); + EXPECT_EQ (purl.url (), "https://server.com/repo/trunk"); + EXPECT_EQ (purl.branch (), ""); + EXPECT_EQ (purl.subfolder (), ""); +} + +TEST (2_SVN) +{ + lay::SaltParsedURL purl ("svn+https://server.com/repo/trunk"); + EXPECT_EQ (purl.protocol () == lay::WebDAV, true); + EXPECT_EQ (purl.url (), "https://server.com/repo/trunk"); + EXPECT_EQ (purl.branch (), ""); + EXPECT_EQ (purl.subfolder (), ""); +} + +TEST (10_GitBasic) +{ + lay::SaltParsedURL purl ("git+https://server.com/repo.git"); + EXPECT_EQ (purl.protocol () == lay::Git, true); + EXPECT_EQ (purl.url (), "https://server.com/repo.git"); + EXPECT_EQ (purl.branch (), ""); + EXPECT_EQ (purl.subfolder (), ""); +} + +TEST (11_GitSubFolder) +{ + lay::SaltParsedURL purl ("git+https://server.com/repo.git/sub/folder"); + EXPECT_EQ (purl.protocol () == lay::Git, true); + EXPECT_EQ (purl.url (), "https://server.com/repo.git"); + EXPECT_EQ (purl.branch (), ""); + EXPECT_EQ (purl.subfolder (), "sub/folder"); +} + +TEST (12_GitExplicitBranch) +{ + lay::SaltParsedURL purl ("git+https://server.com/repo.git[v1.0]"); + EXPECT_EQ (purl.protocol () == lay::Git, true); + EXPECT_EQ (purl.url (), "https://server.com/repo.git"); + EXPECT_EQ (purl.branch (), "v1.0"); + EXPECT_EQ (purl.subfolder (), ""); +} + +TEST (13_GitExplicitBranchAndSubFolder) +{ + lay::SaltParsedURL purl ("git+https://server.com/repo.git/sub/folder[refs/tags/1.0]"); + EXPECT_EQ (purl.protocol () == lay::Git, true); + EXPECT_EQ (purl.url (), "https://server.com/repo.git"); + EXPECT_EQ (purl.branch (), "refs/tags/1.0"); + EXPECT_EQ (purl.subfolder (), "sub/folder"); +} + +TEST (14_GitExplicitBranchAndExplicitSubFolder) +{ + lay::SaltParsedURL purl ("git+https://server.com/repo+sub/folder[refs/tags/1.0]"); + EXPECT_EQ (purl.protocol () == lay::Git, true); + EXPECT_EQ (purl.url (), "https://server.com/repo"); + EXPECT_EQ (purl.branch (), "refs/tags/1.0"); + EXPECT_EQ (purl.subfolder (), "sub/folder"); +} + +TEST (15_GitSVNEmulationTrunk) +{ + lay::SaltParsedURL purl ("git+https://server.com/repo.git/trunk"); + EXPECT_EQ (purl.protocol () == lay::Git, true); + EXPECT_EQ (purl.url (), "https://server.com/repo.git"); + EXPECT_EQ (purl.branch (), "HEAD"); + EXPECT_EQ (purl.subfolder (), ""); +} + +TEST (16_GitSVNEmulationTrunkWithSubFolder) +{ + lay::SaltParsedURL purl ("git+https://server.com/repo.git/sub/folder/trunk"); + EXPECT_EQ (purl.protocol () == lay::Git, true); + EXPECT_EQ (purl.url (), "https://server.com/repo.git"); + EXPECT_EQ (purl.branch (), "HEAD"); + EXPECT_EQ (purl.subfolder (), "sub/folder"); +} + +TEST (17_GitSVNEmulationBranch) +{ + lay::SaltParsedURL purl ("git+https://server.com/repo.git/branches/xyz"); + EXPECT_EQ (purl.protocol () == lay::Git, true); + EXPECT_EQ (purl.url (), "https://server.com/repo.git"); + EXPECT_EQ (purl.branch (), "refs/heads/xyz"); + EXPECT_EQ (purl.subfolder (), ""); +} + +TEST (18_GitSVNEmulationTag) +{ + lay::SaltParsedURL purl ("git+https://server.com/repo.git/tags/1.9"); + EXPECT_EQ (purl.protocol () == lay::Git, true); + EXPECT_EQ (purl.url (), "https://server.com/repo.git"); + EXPECT_EQ (purl.branch (), "refs/tags/1.9"); + EXPECT_EQ (purl.subfolder (), ""); +} + +TEST (19_GitSVNEmulationTagWithSubFolder) +{ + lay::SaltParsedURL purl ("git+https://server.com/repo.git/sub/folder/tags/1.9"); + EXPECT_EQ (purl.protocol () == lay::Git, true); + EXPECT_EQ (purl.url (), "https://server.com/repo.git"); + EXPECT_EQ (purl.branch (), "refs/tags/1.9"); + EXPECT_EQ (purl.subfolder (), "sub/folder"); +} diff --git a/src/lay/unit_tests/unit_tests.pro b/src/lay/unit_tests/unit_tests.pro index bd437bdbb..adf955966 100644 --- a/src/lay/unit_tests/unit_tests.pro +++ b/src/lay/unit_tests/unit_tests.pro @@ -9,6 +9,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ laySalt.cc \ layHelpIndexTest.cc \ + laySaltParsedURLTests.cc \ laySessionTests.cc INCLUDEPATH += $$LAY_INC $$TL_INC $$LAYBASIC_INC $$LAYUI_INC $$LAYVIEW_INC $$DB_INC $$GSI_INC $$ANT_INC $$IMG_INC $$RDB_INC