LCOV - code coverage report
Current view: top level - src/esys/repo/sharing - gitmodules_sharing.cpp (source / functions) Hit Total Coverage
Test: esysrepo_coverage.info Lines: 77 78 98.7 %
Date: 2026-11-07 22:18:26 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /*!
       2             :  * \file esys/repo/sharing/gitmodules_sharing.cpp
       3             :  * \brief Parse ``.gitmodules`` (ADR-0042)
       4             :  *
       5             :  * \cond
       6             :  * __legal_b__
       7             :  *
       8             :  * Copyright (c) 2026 Michel Gillet
       9             :  * Distributed under the MIT License.
      10             :  * (See accompanying file LICENSE.txt or
      11             :  * copy at https://opensource.org/licenses/MIT)
      12             :  *
      13             :  * __legal_e__
      14             :  * \endcond
      15             :  *
      16             :  */
      17             : 
      18             : #include "esys/repo/esysrepo_prec.h"
      19             : #include "esys/repo/sharing/gitmodules.h"
      20             : 
      21             : #include <fstream>
      22             : #include <sstream>
      23             : 
      24             : namespace esys::repo::sharing
      25             : {
      26             : namespace
      27             : {
      28             : 
      29          76 : std::string trim_copy(const std::string &s)
      30             : {
      31          76 :     auto begin = s.find_first_not_of(" \t\r\n");
      32          76 :     if (begin == std::string::npos) return "";
      33          72 :     auto end = s.find_last_not_of(" \t\r\n");
      34          72 :     return s.substr(begin, end - begin + 1);
      35             : }
      36             : 
      37          18 : std::string unquote(std::string s)
      38             : {
      39          18 :     s = trim_copy(s);
      40          18 :     if (s.size() >= 2 && ((s.front() == '"' && s.back() == '"') || (s.front() == '\'' && s.back() == '\'')))
      41           0 :         return s.substr(1, s.size() - 2);
      42          18 :     return s;
      43             : }
      44             : 
      45             : } // namespace
      46             : 
      47           9 : void GitmodulesEntry::set_name(const std::string &name)
      48             : {
      49           9 :     m_name = name;
      50           9 : }
      51             : 
      52           8 : const std::string &GitmodulesEntry::get_name() const
      53             : {
      54           8 :     return m_name;
      55             : }
      56             : 
      57           9 : void GitmodulesEntry::set_path(const std::string &path)
      58             : {
      59           9 :     m_path = path;
      60           9 : }
      61             : 
      62          41 : const std::string &GitmodulesEntry::get_path() const
      63             : {
      64          41 :     return m_path;
      65             : }
      66             : 
      67           9 : void GitmodulesEntry::set_url(const std::string &url)
      68             : {
      69           9 :     m_url = url;
      70           9 : }
      71             : 
      72          22 : const std::string &GitmodulesEntry::get_url() const
      73             : {
      74          22 :     return m_url;
      75             : }
      76             : 
      77           6 : Result GitmodulesFile::parse_text(const std::string &text)
      78             : {
      79           6 :     m_entries.clear();
      80           6 :     GitmodulesEntry current;
      81           6 :     bool in_submodule = false;
      82             : 
      83           6 :     std::istringstream in(text);
      84           6 :     std::string line;
      85          37 :     while (std::getline(in, line))
      86             :     {
      87             :         // Strip comments
      88          31 :         auto hash = line.find('#');
      89          31 :         auto semi = line.find(';');
      90          31 :         auto cut = std::string::npos;
      91          31 :         if (hash != std::string::npos) cut = hash;
      92          31 :         if (semi != std::string::npos && (cut == std::string::npos || semi < cut)) cut = semi;
      93          31 :         if (cut != std::string::npos) line = line.substr(0, cut);
      94          31 :         line = trim_copy(line);
      95          40 :         if (line.empty()) continue;
      96             : 
      97          27 :         if (line.front() == '[' && line.back() == ']')
      98             :         {
      99           9 :             if (in_submodule && (!current.get_path().empty() || !current.get_url().empty()))
     100           3 :                 m_entries.push_back(current);
     101           9 :             current = GitmodulesEntry{};
     102           9 :             in_submodule = false;
     103             : 
     104           9 :             std::string section = trim_copy(line.substr(1, line.size() - 2));
     105             :             // [submodule "name"]
     106           9 :             const std::string prefix = "submodule";
     107          18 :             if (section.size() >= prefix.size() &&
     108           9 :                 section.compare(0, prefix.size(), prefix) == 0)
     109             :             {
     110           9 :                 auto q1 = section.find('"');
     111           9 :                 auto q2 = section.rfind('"');
     112           9 :                 if (q1 != std::string::npos && q2 != std::string::npos && q2 > q1)
     113             :                 {
     114           9 :                     current.set_name(section.substr(q1 + 1, q2 - q1 - 1));
     115           9 :                     in_submodule = true;
     116             :                 }
     117             :             }
     118           9 :             continue;
     119           9 :         }
     120             : 
     121          18 :         if (!in_submodule) continue;
     122             : 
     123          18 :         auto eq = line.find('=');
     124          18 :         if (eq == std::string::npos) continue;
     125          18 :         std::string key = trim_copy(line.substr(0, eq));
     126          18 :         std::string value = unquote(line.substr(eq + 1));
     127          18 :         if (key == "path")
     128           9 :             current.set_path(value);
     129           9 :         else if (key == "url")
     130           9 :             current.set_url(value);
     131          18 :     }
     132             : 
     133           6 :     if (in_submodule && (!current.get_path().empty() || !current.get_url().empty())) m_entries.push_back(current);
     134             : 
     135          12 :     return ESYSREPO_RESULT(ResultCode::OK);
     136           6 : }
     137             : 
     138           1 : Result GitmodulesFile::parse_file(const std::string &path)
     139             : {
     140           1 :     std::ifstream ifs(path);
     141           1 :     if (!ifs) return ESYSREPO_RESULT(ResultCode::FILE_NOT_EXISTING, path);
     142           1 :     std::ostringstream oss;
     143           1 :     oss << ifs.rdbuf();
     144           1 :     return parse_text(oss.str());
     145           1 : }
     146             : 
     147           6 : const std::vector<GitmodulesEntry> &GitmodulesFile::get_entries() const
     148             : {
     149           6 :     return m_entries;
     150             : }
     151             : 
     152             : } // namespace esys::repo::sharing

Generated by: LCOV version 1.14