VLink  2.0.0
A high-performance communication middleware
url_remap.h
浏览该文件的文档.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file url_remap.h
26  * @brief JSON-driven substring rewriter for VLink topic URLs.
27  *
28  * @details
29  * @c UrlRemap turns a flat JSON dictionary into a runtime topic-renaming layer. Each entry maps
30  * a fragment that should appear in an input URL to the replacement URL emitted to the transport,
31  * allowing operators to switch backends, redirect topics, or stage migrations without rebuilding
32  * application binaries.
33  *
34  * @par Rewrite rules
35  *
36  * | Source key (substring of input) | Target value (replacement URL) | Effect |
37  * | --------------------------------- | ------------------------------------- | --------------------------------- |
38  * | @c "intra://sensor/lidar" | @c "dds://vehicle/lidar" | promote local topic to DDS |
39  * | @c "shm://camera/front" | @c "zenoh://camera/front" | switch transport to Zenoh |
40  * | @c "dds://" | @c "ddst://" | force TCP DDS transport globally |
41  * | @c "fdbus://" | @c "intra://" | merge to intra-process bus |
42  *
43  * Lookup is linear in the configured order; the first key that occurs as a substring of the
44  * input URL wins. Results are cached so repeated lookups for the same input are O(1).
45  *
46  * @par Environment-driven remap
47  * @code
48  * +-------------------------+ load(path) +---------------------+ convert(url)
49  * | VLINK_URL_REMAP=path | ----------------> | UrlRemap (JSON map) | -----------------> rewritten URL
50  * | or load(path) at init | | + result cache | cache hit -> O(1)
51  * +-------------------------+ +---------------------+
52  * |
53  * v
54  * transports receive
55  * the rewritten URL
56  * @endcode
57  *
58  * @par Example
59  * @code
60  * vlink::UrlRemap remap;
61  *
62  * if (remap.load("/etc/vlink/remap.json")) {
63  * auto target = remap.convert("intra://sensor/lidar");
64  * auto pub = vlink::Publisher<MyMsg>::create_unique(target);
65  * } else {
66  * VLOG_W("remap unavailable: ", remap.get_error_string());
67  * }
68  *
69  * remap.reload("/etc/vlink/remap-v2.json");
70  * @endcode
71  *
72  * @note
73  * - @c UrlRemap is not thread-safe; serialise calls externally or confine to a single thread.
74  * - @c convert() returns its input unchanged when the table is empty or unloaded.
75  */
76 
77 #pragma once
78 
79 #include <string>
80 #include <unordered_map>
81 #include <vector>
82 
83 #include "../base/macros.h"
84 
85 namespace vlink {
86 
87 /**
88  * @class UrlRemap
89  * @brief Loads a JSON rewrite table and substitutes VLink URLs at runtime.
90  *
91  * @details
92  * Each instance holds an ordered rewrite list, a result cache, and a status string used for
93  * diagnostics. Copy and assignment are disabled.
94  */
96  public:
97  /**
98  * @brief Constructs an empty, unloaded instance.
99  */
100  UrlRemap() noexcept;
101 
102  /**
103  * @brief Destroys the instance and clears caches.
104  */
105  ~UrlRemap() noexcept;
106 
107  /**
108  * @brief Parses @p file_path and installs the rewrite table.
109  *
110  * @param file_path Absolute or relative path to a flat JSON object of string pairs.
111  * @return @c true on success; @c false when already loaded, the file is missing, unreadable,
112  * or contains invalid JSON. Errors are surfaced via @c get_error_string().
113  */
114  bool load(const std::string& file_path) noexcept;
115 
116  /**
117  * @brief Clears the rewrite table and the result cache.
118  *
119  * @return @c true when the table was cleared; @c false when no table was loaded.
120  */
121  bool unload() noexcept;
122 
123  /**
124  * @brief Atomically unloads the current table and loads @p file_path.
125  *
126  * @param file_path Path of the replacement JSON file.
127  * @return @c true when the new file loaded successfully.
128  */
129  bool reload(const std::string& file_path) noexcept;
130 
131  /**
132  * @brief Translates @p url using the loaded rules; returns @p url unchanged on miss.
133  *
134  * @param url Input URL to rewrite.
135  * @return Reference to the rewritten URL on a hit, or to @p url itself on a
136  * miss. The referent's lifetime depends on the path taken (the input
137  * @p url, the internal remap table, or the cache); copy it if you need
138  * to retain it.
139  */
140  const std::string& convert(const std::string& url) noexcept;
141 
142  /**
143  * @brief Toggles per-conversion INFO logging.
144  *
145  * @param enable_log @c true emits one log line per successful rewrite.
146  */
147  void set_enable_log(bool enable_log) noexcept;
148 
149  /**
150  * @brief Reports whether per-conversion logging is enabled.
151  *
152  * @return @c true when logging is currently enabled.
153  */
154  [[nodiscard]] bool is_enable_log() const noexcept;
155 
156  /**
157  * @brief Reports whether a rewrite table has been successfully loaded.
158  *
159  * @return @c true after a successful @c load() or @c reload() and before the next @c unload().
160  */
161  [[nodiscard]] bool is_valid() const noexcept;
162 
163  /**
164  * @brief Returns the diagnostic message produced by the last failure.
165  *
166  * @return Error string or empty when no failure has occurred since the last successful load.
167  */
168  [[nodiscard]] const std::string& get_error_string() const noexcept;
169 
170  private:
171  bool is_enable_log_{false};
172  bool is_valid_{false};
173 
174  std::string error_string_;
175 
176  std::vector<std::pair<std::string, std::string>> remap_list_;
177  std::unordered_map<std::string, std::string> cache_map_;
178 
180 };
181 
182 ////////////////////////////////////////////////////////////////
183 /// Details
184 ////////////////////////////////////////////////////////////////
185 
186 inline void UrlRemap::set_enable_log(bool enable_log) noexcept { is_enable_log_ = enable_log; }
187 
188 inline bool UrlRemap::is_enable_log() const noexcept { return is_enable_log_; }
189 
190 inline bool UrlRemap::is_valid() const noexcept { return is_valid_; }
191 
192 inline const std::string& UrlRemap::get_error_string() const noexcept { return error_string_; }
193 
194 } // namespace vlink
#define VLINK_EXPORT
Definition: macros.h:81
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition: macros.h:174