blob: 143049e55ed91d6745647ab55dfb915f8ddf885e [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "paimon/common/utils/path_util.h"
#include <unistd.h>
#include <cerrno>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <utility>
#include "fmt/format.h"
#include "paimon/common/utils/string_utils.h"
#include "paimon/common/utils/uuid.h"
#include "paimon/status.h"
namespace paimon {
std::string Path::ToString() const {
std::string ret;
if (!scheme.empty()) {
ret = scheme + ":";
}
if (!authority.empty()) {
ret += "//";
ret += authority;
}
if (!path.empty()) {
ret += path;
}
return ret;
}
std::string PathUtil::JoinPath(const std::string& path, const std::string& name) noexcept {
if (path.empty()) {
return name;
}
if (name.empty()) {
return path;
}
int32_t slash_cnt = (*(path.rbegin()) == '/') + (*(name.begin()) == '/');
if (!slash_cnt) {
return path + "/" + name;
} else if (slash_cnt == 2) {
return path + name.substr(1);
}
return path + name;
}
std::string PathUtil::NormalizeInnerPath(const std::string& path) noexcept {
if (path.empty()) {
return path;
}
std::string ret;
ret.reserve(path.size());
char last_char = path[0];
ret.append(1, last_char);
for (size_t i = 1; i < path.size(); ++i) {
if (last_char == '/' && path[i] == '/') {
continue;
}
last_char = path[i];
ret.append(1, last_char);
}
TrimLastDelim(&ret);
return ret;
}
Result<std::string> PathUtil::NormalizePath(const std::string& path_str) noexcept {
PAIMON_ASSIGN_OR_RAISE(Path path, ToPath(path_str));
return path.ToString();
}
Result<Path> PathUtil::ToPath(const std::string& path) noexcept {
// TODO(yonghao.fyh): support Windows Driver
if (path.empty()) {
return Status::Invalid("path is an empty string.");
}
std::string scheme;
std::string authority;
int32_t start = 0;
// parse scheme
auto colon = path.find(':');
auto slash = path.find('/');
if ((colon != std::string::npos) && (slash == std::string::npos || colon < slash)) {
// has a scheme
scheme.append(path, 0, colon);
start = colon + 1;
}
// parse authority
if (StringUtils::StartsWith(path, "//", start) && (path.length() - start > 2)) {
// has authority
int32_t next_slash = path.find('/', start + 2);
int32_t auth_end = next_slash > 0 ? next_slash : path.length();
authority = path.substr(start + 2, auth_end - start - 2);
start = auth_end;
}
// parse path in uri
std::string inner_path = NormalizeInnerPath(path.substr(start));
return Path(scheme, authority, inner_path);
}
std::string PathUtil::GetParentDirPath(const std::string& path) noexcept {
std::string::const_reverse_iterator it;
for (it = path.rbegin(); it != path.rend() && *it == '/'; it++) {
}
for (; it != path.rend() && *it != '/'; it++) {
}
for (; it != path.rend() && *it == '/'; it++) {
}
return path.substr(0, path.rend() - it);
}
std::string PathUtil::GetName(const std::string& path) noexcept {
std::string dir_path = path;
TrimLastDelim(&dir_path);
std::string::const_reverse_iterator it;
for (it = dir_path.rbegin(); it != dir_path.rend() && *it != '/'; it++) {
}
return dir_path.substr(dir_path.rend() - it);
}
void PathUtil::TrimLastDelim(std::string* dir_path) noexcept {
if (dir_path == nullptr || dir_path->empty()) {
return;
}
if (dir_path->length() > 1 && *(dir_path->rbegin()) == '/') {
dir_path->erase(dir_path->size() - 1, 1);
}
}
Result<std::string> PathUtil::GetWorkingDirectory() noexcept {
char* path = getcwd(nullptr, 0);
if (path != nullptr) {
std::string ret(path);
free(path);
return ret;
}
return Status::IOError(
fmt::format("get working directory failed, ec: {}", std::strerror(errno)));
}
Result<std::string> PathUtil::CreateTempPath(const std::string& path) noexcept {
std::string uuid;
if (!UUID::Generate(&uuid)) {
return Status::Invalid("generate uuid failed");
}
return JoinPath(GetParentDirPath(path), fmt::format(".{}.{}.tmp", GetName(path), uuid));
}
} // namespace paimon