#include "media/formats/hls/variable_dictionary.h"
#include <string_view>
#include "base/strings/string_util.h"
#include "media/formats/hls/parse_status.h"
#include "media/formats/hls/source_string.h"
#include "media/formats/hls/types.h"
namespace media::hls {
namespace {
struct GetNextVariableResult {
SourceString head;
std::optional<std::pair<types::VariableName, SourceString>> tail;
};
GetNextVariableResult GetNextVariable(const SourceString input) {
for (size_t ref_start = input.Str().find("{$");
ref_start != std::string_view::npos;
ref_start = input.Str().find("{$", ref_start + 2)) {
auto remaining_input = input;
const auto head = remaining_input.Consume(ref_start);
remaining_input.Consume(2);
const auto ref_end = remaining_input.Str().find_first_of('}');
if (ref_end == std::string_view::npos) {
break;
}
auto var_name_result =
types::VariableName::Parse(remaining_input.Consume(ref_end));
remaining_input.Consume(1);
if (!var_name_result.has_value()) {
continue;
}
auto var_name = std::move(var_name_result).value();
return GetNextVariableResult{
.head = head, .tail = std::make_pair(var_name, remaining_input)};
}
return GetNextVariableResult{.head = input, .tail = std::nullopt};
}
}
VariableDictionary::SubstitutionBuffer::SubstitutionBuffer() = default;
VariableDictionary::SubstitutionBuffer::~SubstitutionBuffer() = default;
VariableDictionary::VariableDictionary() = default;
VariableDictionary::~VariableDictionary() = default;
VariableDictionary::VariableDictionary(VariableDictionary&&) = default;
VariableDictionary& VariableDictionary::operator=(VariableDictionary&&) =
default;
std::optional<std::string_view> VariableDictionary::Find(
types::VariableName name) const& {
auto iter = entries_.find(name.GetName());
if (iter == entries_.end()) {
return std::nullopt;
}
return *iter->second;
}
bool VariableDictionary::Insert(types::VariableName name, std::string value) {
return entries_
.try_emplace(std::move(name).GetName(),
std::make_unique<std::string>(std::move(value)))
.second;
}
ParseStatus::Or<ResolvedSourceString> VariableDictionary::Resolve(
SourceString input,
SubstitutionBuffer& buffer) const {
auto next_var = GetNextVariable(input);
if (!next_var.tail) {
return ResolvedSourceString::Create(
{}, input.Line(), input.Column(), input.Str(),
ResolvedSourceString::SubstitutionState::kNoSubstitutions);
}
if (next_var.head.Empty() && next_var.tail->second.Empty()) {
auto value = Find(next_var.tail->first);
if (!value) {
return ParseStatus(ParseStatusCode::kVariableUndefined)
.WithData("key", next_var.tail->first.GetName());
}
return ResolvedSourceString::Create(
{}, input.Line(), input.Column(), *value,
ResolvedSourceString::SubstitutionState::kContainsSubstitutions);
}
auto& string_buf = buffer.strings_.emplace_back();
while (true) {
string_buf.append(next_var.head.Str());
if (!next_var.tail) {
break;
}
auto value = Find(next_var.tail->first);
if (!value) {
return ParseStatus(ParseStatusCode::kVariableUndefined)
.WithData("key", next_var.tail->first.GetName());
}
string_buf.append(*value);
next_var = GetNextVariable(next_var.tail->second);
}
return ResolvedSourceString::Create(
{}, input.Line(), input.Column(), string_buf,
ResolvedSourceString::SubstitutionState::kContainsSubstitutions);
}
}