Skip to content

Commit 3feca5c

Browse files
committed
Create canonical_path class
Using std::string everywhere is a bit sloppy. Eventually, I want to use a std::wstring on Windows instead of a std::string. Create a canonical_path class to encapsulate the std::string. This commit should not change behavior.
1 parent 8fb4422 commit 3feca5c

File tree

5 files changed

+138
-12
lines changed

5 files changed

+138
-12
lines changed

src/configuration-loader.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ configuration* configuration_loader::load_config_file(const char* config_path) {
3232
}
3333

3434
if (configuration* config =
35-
this->get_loaded_config(canonical_config_path.c_str())) {
35+
this->get_loaded_config(canonical_config_path.canonical())) {
3636
return config;
3737
}
3838
read_file_result config_json = read_file(canonical_config_path.c_str());
@@ -42,7 +42,7 @@ configuration* configuration_loader::load_config_file(const char* config_path) {
4242
}
4343
auto [config_it, inserted] = this->loaded_config_files_.emplace(
4444
std::piecewise_construct,
45-
std::forward_as_tuple(canonical_config_path.path()),
45+
std::forward_as_tuple(canonical_config_path.canonical()),
4646
std::forward_as_tuple());
4747
QLJS_ASSERT(inserted);
4848
configuration* config = &config_it->second;
@@ -83,14 +83,18 @@ configuration* configuration_loader::find_and_load_config_file(
8383
QLJS_ASSERT(config_path == parent_directory);
8484
config_path.append(suffix);
8585

86-
if (configuration* config =
87-
this->get_loaded_config(config_path.c_str())) {
86+
if (configuration* config = this->get_loaded_config(config_path)) {
8887
return config;
8988
}
9089

9190
read_file_result config_json = read_file(config_path.c_str());
9291
if (config_json.ok()) {
93-
configuration* config = &this->loaded_config_files_[config_path];
92+
auto [config_it, inserted] = this->loaded_config_files_.emplace(
93+
std::piecewise_construct,
94+
std::forward_as_tuple(canonical_path(std::string(config_path))),
95+
std::forward_as_tuple());
96+
QLJS_ASSERT(inserted);
97+
configuration* config = &config_it->second;
9498
config->set_config_file_path(std::move(config_path));
9599
config->load_from_json(&config_json.content);
96100
return config;
@@ -119,7 +123,20 @@ configuration* configuration_loader::find_and_load_config_file(
119123
QLJS_WARNING_POP
120124

121125
configuration* configuration_loader::get_loaded_config(
122-
const char* path) noexcept {
126+
const std::string& path) noexcept {
127+
#if QLJS_HAVE_STD_TRANSPARENT_KEYS
128+
auto existing_config_it = this->loaded_config_files_.find(path);
129+
#else
130+
auto existing_config_it =
131+
this->loaded_config_files_.find(canonical_path(std::string(path)));
132+
#endif
133+
return existing_config_it == this->loaded_config_files_.end()
134+
? nullptr
135+
: &existing_config_it->second;
136+
}
137+
138+
configuration* configuration_loader::get_loaded_config(
139+
const canonical_path& path) noexcept {
123140
auto existing_config_it = this->loaded_config_files_.find(path);
124141
return existing_config_it == this->loaded_config_files_.end()
125142
? nullptr

src/file-canonical.cpp

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,42 @@ std::string string_for_error_message(std::wstring_view);
5858
#endif
5959
}
6060

61+
canonical_path::canonical_path(std::string &&path) : path_(std::move(path)) {}
62+
63+
std::string_view canonical_path::path() const &noexcept { return this->path_; }
64+
65+
std::string &&canonical_path::path() && noexcept {
66+
return std::move(this->path_);
67+
}
68+
69+
const char *canonical_path::c_str() const noexcept {
70+
return this->path_.c_str();
71+
}
72+
73+
bool operator==(const canonical_path &lhs, const canonical_path &rhs) noexcept {
74+
return lhs.path() == rhs.path();
75+
}
76+
77+
bool operator!=(const canonical_path &lhs, const canonical_path &rhs) noexcept {
78+
return !(lhs == rhs);
79+
}
80+
81+
bool operator==(std::string_view lhs, const canonical_path &rhs) noexcept {
82+
return lhs == rhs.path();
83+
}
84+
85+
bool operator!=(std::string_view lhs, const canonical_path &rhs) noexcept {
86+
return !(lhs == rhs);
87+
}
88+
89+
bool operator==(const canonical_path &lhs, std::string_view rhs) noexcept {
90+
return lhs.path() == rhs;
91+
}
92+
93+
bool operator!=(const canonical_path &lhs, std::string_view rhs) noexcept {
94+
return !(lhs == rhs);
95+
}
96+
6197
canonical_path_result::canonical_path_result() {}
6298

6399
canonical_path_result::canonical_path_result(std::string &&path)
@@ -67,17 +103,27 @@ canonical_path_result::canonical_path_result(const char *path) : path_(path) {}
67103

68104
std::string_view canonical_path_result::path() const &noexcept {
69105
QLJS_ASSERT(this->ok());
70-
return this->path_;
106+
return this->path_->path();
71107
}
72108

73109
std::string &&canonical_path_result::path() && noexcept {
74110
QLJS_ASSERT(this->ok());
75-
return std::move(this->path_);
111+
return std::move(*this->path_).path();
76112
}
77113

78114
const char *canonical_path_result::c_str() const noexcept {
79115
QLJS_ASSERT(this->ok());
80-
return this->path_.c_str();
116+
return this->path_->c_str();
117+
}
118+
119+
const canonical_path &canonical_path_result::canonical() const &noexcept {
120+
QLJS_ASSERT(this->ok());
121+
return *this->path_;
122+
}
123+
124+
canonical_path &&canonical_path_result::canonical() && noexcept {
125+
QLJS_ASSERT(this->ok());
126+
return std::move(*this->path_);
81127
}
82128

83129
std::string &&canonical_path_result::error() && noexcept {

src/quick-lint-js/configuration-loader.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#define QUICK_LINT_JS_CONFIGURATION_LOADER_H
66

77
#include <quick-lint-js/configuration.h>
8+
#include <quick-lint-js/file-canonical.h>
89
#include <string>
910
#include <unordered_map>
1011

@@ -21,10 +22,11 @@ class configuration_loader {
2122
configuration* load_config_file(const char* config_path);
2223
configuration* find_and_load_config_file(const char* input_path);
2324

24-
configuration* get_loaded_config(const char* path) noexcept;
25+
configuration* get_loaded_config(const std::string& path) noexcept;
26+
configuration* get_loaded_config(const canonical_path& path) noexcept;
2527

2628
configuration default_config_;
27-
std::unordered_map<std::string, configuration> loaded_config_files_;
29+
std::unordered_map<canonical_path, configuration> loaded_config_files_;
2830
std::string last_error_;
2931
};
3032
}

src/quick-lint-js/file-canonical.h

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,45 @@
44
#ifndef QUICK_LINT_JS_FILE_CANONICAL_H
55
#define QUICK_LINT_JS_FILE_CANONICAL_H
66

7+
#include <cstddef>
8+
#include <functional>
9+
#include <optional>
710
#include <string>
11+
#include <string_view>
812

913
namespace quick_lint_js {
14+
// A filesystem path.
15+
//
16+
// * The path is absolute (i.e. not relative to the current working directory or
17+
// current drive). (The path is relative to the current chroot/jail/namespace,
18+
// though.)
19+
// * The path has no '.' or '..' components.
20+
// * The path has no redundant component separators (\ or /, depending on the
21+
// operating system).
22+
// * No subpath refers to a symlink, assuming no changes to the filesystem since
23+
// creation of the canonical_path.
24+
class canonical_path {
25+
public:
26+
// Does not check the validity of the path.
27+
explicit canonical_path(std::string &&path);
28+
29+
std::string_view path() const &noexcept;
30+
std::string &&path() && noexcept;
31+
const char *c_str() const noexcept;
32+
33+
friend bool operator==(const canonical_path &,
34+
const canonical_path &) noexcept;
35+
friend bool operator!=(const canonical_path &,
36+
const canonical_path &) noexcept;
37+
friend bool operator==(std::string_view, const canonical_path &) noexcept;
38+
friend bool operator!=(std::string_view, const canonical_path &) noexcept;
39+
friend bool operator==(const canonical_path &, std::string_view) noexcept;
40+
friend bool operator!=(const canonical_path &, std::string_view) noexcept;
41+
42+
private:
43+
std::string path_;
44+
};
45+
1046
class canonical_path_result {
1147
public:
1248
explicit canonical_path_result(std::string &&path);
@@ -16,6 +52,9 @@ class canonical_path_result {
1652
std::string &&path() && noexcept;
1753
const char *c_str() const noexcept;
1854

55+
const canonical_path &canonical() const &noexcept;
56+
canonical_path &&canonical() && noexcept;
57+
1958
std::string &&error() && noexcept;
2059

2160
bool ok() const noexcept { return this->error_.empty(); }
@@ -25,14 +64,30 @@ class canonical_path_result {
2564
private:
2665
explicit canonical_path_result();
2766

28-
std::string path_;
67+
std::optional<canonical_path> path_;
2968
std::string error_;
3069
};
3170

3271
canonical_path_result canonicalize_path(const char *path);
3372
canonical_path_result canonicalize_path(const std::string &path);
3473
}
3574

75+
namespace std {
76+
template <>
77+
struct hash<quick_lint_js::canonical_path> {
78+
using is_transparent = void;
79+
80+
std::size_t operator()(const quick_lint_js::canonical_path &path) const
81+
noexcept {
82+
return std::hash<std::string_view>()(path.path());
83+
}
84+
85+
std::size_t operator()(std::string_view path) const noexcept {
86+
return std::hash<std::string_view>()(path);
87+
}
88+
};
89+
}
90+
3691
#endif
3792

3893
// quick-lint-js finds bugs in JavaScript programs.

src/quick-lint-js/have.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,12 @@
313313
#define QLJS_HAVE_SETJMP 0
314314
#endif
315315

316+
#if !defined(QLJS_HAVE_STD_TRANSPARENT_KEYS)
317+
// TODO(strager): Set this to 1 if is_transparent is supported by
318+
// std::unordered_map::find (C++20).
319+
#define QLJS_HAVE_STD_TRANSPARENT_KEYS 0
320+
#endif
321+
316322
#endif
317323

318324
// quick-lint-js finds bugs in JavaScript programs.

0 commit comments

Comments
 (0)