Line data Source code
1 : /*! 2 : * \file esys/repo/sharing/providerregistry_sharing.cpp 3 : * \brief Explicit shareable providers (ADR-0039/0040) 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/providerregistry.h" 20 : #include "esys/repo/sharing/identity.h" 21 : #include "esys/repo/errorinfo.h" 22 : #include "esys/repo/manifest.h" 23 : #include "esys/repo/manifest/location.h" 24 : #include "esys/repo/manifest/repository.h" 25 : 26 : namespace esys::repo::sharing 27 : { 28 : 29 4 : void Provider::set_repository(std::shared_ptr<manifest::Repository> repo) 30 : { 31 4 : m_repository = std::move(repo); 32 4 : } 33 : 34 4 : std::shared_ptr<manifest::Repository> Provider::get_repository() const 35 : { 36 4 : return m_repository; 37 : } 38 : 39 4 : void Provider::set_normalized_identity(const std::string &identity) 40 : { 41 4 : m_normalized_identity = identity; 42 4 : } 43 : 44 1 : const std::string &Provider::get_normalized_identity() const 45 : { 46 1 : return m_normalized_identity; 47 : } 48 : 49 4 : void Provider::set_config(const Config &config) 50 : { 51 4 : m_config = config; 52 4 : } 53 : 54 0 : const Config &Provider::get_config() const 55 : { 56 0 : return m_config; 57 : } 58 : 59 29 : Result ProviderRegistry::build(const Manifest &manifest) 60 : { 61 29 : clear(); 62 : 63 64 : for (const auto &loc : manifest.get_locations()) 64 : { 65 35 : if (!loc) continue; 66 216 : for (const auto &repo : loc->get_repos()) 67 : { 68 181 : if (!repo || !repo->get_sharing().has_value()) continue; 69 : 70 4 : if (repo->get_path() == "." || repo->get_path() == "./") 71 : { 72 0 : return ESYSREPO_RESULT(ResultCode::SHARING_ROOT_PROVIDER, 73 : "workspace root path=\".\" cannot be a sharing provider"); 74 : } 75 : 76 4 : const Config &cfg = *repo->get_sharing(); 77 : 78 : // Prefer primary addr; also register alt_addr as same provider identity set 79 4 : std::string url = Identity::compose_location_url(loc->get_address(), repo->get_name()); 80 4 : auto id = Identity::normalize(url); 81 4 : if (id.error()) 82 : { 83 0 : std::string msg = "provider identity normalize failed for path=" + repo->get_path(); 84 0 : if (id.get_error_info() && !id.get_error_info()->get_text().empty()) 85 0 : msg += ": " + id.get_error_info()->get_text(); 86 0 : return ESYSREPO_RESULT(id.get_result_code(), msg); 87 0 : } 88 : 89 4 : Provider provider; 90 8 : provider.set_repository(repo); 91 4 : provider.set_normalized_identity(id.get()); 92 4 : provider.set_config(cfg); 93 4 : const std::size_t index = m_providers.size(); 94 4 : m_providers.push_back(provider); 95 4 : m_by_identity[id.get()].push_back(index); 96 : 97 4 : if (!loc->get_alt_address().empty()) 98 : { 99 1 : std::string alt_url = Identity::compose_location_url(loc->get_alt_address(), repo->get_name()); 100 1 : auto alt_id = Identity::normalize(alt_url); 101 5 : if (!alt_id.error() && alt_id.get() != id.get()) 102 : { 103 : // Same provider reachable under alt spelling — index alias to same Provider 104 0 : m_by_identity[alt_id.get()].push_back(index); 105 : } 106 1 : } 107 4 : } 108 : } 109 : 110 29 : return ESYSREPO_RESULT(ResultCode::OK); 111 : } 112 : 113 29 : void ProviderRegistry::clear() 114 : { 115 29 : m_providers.clear(); 116 29 : m_by_identity.clear(); 117 29 : } 118 : 119 57 : const std::vector<Provider> &ProviderRegistry::get_providers() const 120 : { 121 57 : return m_providers; 122 : } 123 : 124 1 : std::set<std::string> ProviderRegistry::registered_identities() const 125 : { 126 1 : std::set<std::string> out; 127 2 : for (const auto &p : m_providers) out.insert(p.get_normalized_identity()); 128 : // Include alt aliases that map to providers 129 2 : for (const auto &pair : m_by_identity) out.insert(pair.first); 130 1 : return out; 131 0 : } 132 : 133 3 : Result_t<std::shared_ptr<Provider>> ProviderRegistry::match(const std::string &normalized_identity) const 134 : { 135 3 : auto it = m_by_identity.find(normalized_identity); 136 3 : if (it == m_by_identity.end() || it->second.empty()) 137 0 : return Result_t<std::shared_ptr<Provider>>(std::shared_ptr<Provider>{}); 138 : 139 : // Deduplicate indices (addr + alt_addr can both point at same provider) 140 3 : std::set<std::size_t> unique(it->second.begin(), it->second.end()); 141 3 : if (unique.size() > 1) 142 : { 143 1 : std::string msg = "ambiguous sharing providers for identity '" + normalized_identity + "':"; 144 3 : for (std::size_t idx : unique) 145 : { 146 2 : const auto &repo = m_providers[idx].get_repository(); 147 2 : msg += " "; 148 4 : msg += repo ? repo->get_path() : "?"; 149 2 : } 150 2 : return Result_t<std::shared_ptr<Provider>>(ESYSREPO_RESULT(ResultCode::SHARING_AMBIGUOUS_PROVIDER, msg)); 151 1 : } 152 : 153 2 : auto provider = std::make_shared<Provider>(m_providers[*unique.begin()]); 154 6 : return Result_t<std::shared_ptr<Provider>>(provider); 155 6 : } 156 : 157 : } // namespace esys::repo::sharing