blob: 2d37002e2e58a297b4e61f07f8d40be02c12c3b6 [file] [edit]
// Copyright 2026 Google LLC
//
// Licensed 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
//
// https://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 "jumbf/uri.h"
#include <string>
#include <utility>
#include <variant>
#include "absl/base/nullability.h"
#include "absl/log/die_if_null.h"
#include "absl/status/status.h"
#include "absl/status/status_macros.h"
#include "absl/status/statusor.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "jumbf/box.h"
namespace jumbf {
namespace {
absl::string_view GetLabel(const SuperBox& box) {
if (box.description.label.has_value()) {
return *box.description.label;
}
return "";
}
// Returns the first child superbox with the given label, or error if not
// found.
absl::StatusOr<const SuperBox*> GetChild(const SuperBox* box,
absl::string_view label) {
if (label == "." || label == "..") {
return absl::InvalidArgumentError("Unsupported path component");
}
for (const ContentBox& content : box->contents) {
if (std::holds_alternative<SuperBox>(content.payload)) {
const SuperBox& child = std::get<SuperBox>(content.payload);
if (GetLabel(child) == label) {
return &child;
}
}
}
return absl::NotFoundError("");
}
} // namespace
UriResolver UriResolver::WithSingleRootChild(const SuperBox* root_child) {
return UriResolver(root_child, absl::StrCat("/", GetLabel(*root_child), "/"));
}
UriResolver::UriResolver(const SuperBox* absl_nonnull root_box,
std::string root_path)
: root_box_(*ABSL_DIE_IF_NULL(root_box)),
root_path_(std::move(root_path)) {}
absl::StatusOr<std::string> UriResolver::GetAbsolutePathFromUri(
absl::string_view uri, absl::string_view current_path) {
if (!absl::ConsumePrefix(&uri, "self#jumbf=")) {
return absl::InvalidArgumentError("self#jumbf= prefix required");
}
return GetAbsolutePath(uri, current_path);
}
std::string UriResolver::GetAbsolutePath(absl::string_view path,
absl::string_view current_path) {
if (absl::StartsWith(path, "/")) {
// Absolute path.
return std::string(path);
}
// Relative path.
return absl::StrCat(current_path, "/", path);
}
absl::StatusOr<const SuperBox*> UriResolver::ResolvePath(
absl::string_view absolute_path) const {
const SuperBox* box = nullptr;
if (!absl::ConsumePrefix(&absolute_path, root_path_)) {
return absl::NotFoundError("");
}
box = &root_box_;
for (absl::string_view label : absl::StrSplit(absolute_path, '/')) {
ABSL_ASSIGN_OR_RETURN(auto child_box, GetChild(box, label));
box = std::move(child_box);
}
return box;
}
} // namespace jumbf