datactl/edlutils.cpp

datactl/edlutils.cpp

datactl/edlutils.cpp

Namespaces

Name
Syntalos
Syntalos::edl

Source code

/*
 * Copyright (C) 2025-2026 Matthias Klumpp <matthias@tenstral.net>
 *
 * Licensed under the GNU Lesser General Public License Version 3
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the license, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "edlutils.h"

#include <gio/gio.h>
#include <algorithm>
#include <cctype>
#include <chrono>
#include <cstdint>
#include <random>
#include <string>
#include <vector>

namespace Syntalos::edl
{

std::string createRandomString(size_t len)
{
    static constexpr std::string_view chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    std::random_device rd;
    std::uniform_int_distribution<size_t> dist(0, chars.size() - 1);
    std::string result;
    result.reserve(len);
    for (size_t i = 0; i < len; ++i)
        result += chars[dist(rd)];
    return result;
}

toml::date_time toToml(const EdlDateTime &dt)
{
    // Extract the local_time and utc offset from the zoned_time
    const auto sysTime = dt.get_sys_time();
    const auto localTime = dt.get_local_time();

    // Compute UTC offset in minutes
    const auto offset = std::chrono::duration_cast<std::chrono::minutes>(
        localTime.time_since_epoch() - sysTime.time_since_epoch());
    const auto offsetMinutes = static_cast<int16_t>(offset.count());

    // Decompose local time into calendar fields
    const auto localDays = std::chrono::floor<std::chrono::days>(localTime);
    const auto tod = localTime - localDays;

    const std::chrono::year_month_day ymd{localDays};
    const std::chrono::hh_mm_ss<std::chrono::system_clock::duration> hms{tod};

    toml::date d;
    d.year = static_cast<int>(ymd.year());
    d.month = static_cast<uint8_t>(static_cast<unsigned>(ymd.month()));
    d.day = static_cast<uint8_t>(static_cast<unsigned>(ymd.day()));

    toml::time t;
    t.hour = static_cast<uint8_t>(hms.hours().count());
    t.minute = static_cast<uint8_t>(hms.minutes().count());
    t.second = static_cast<uint8_t>(hms.seconds().count());
    t.nanosecond = static_cast<uint32_t>(
        std::chrono::duration_cast<std::chrono::nanoseconds>(hms.subseconds()).count());

    toml::time_offset off;
    off.minutes = offsetMinutes;

    return toml::date_time{d, t, off};
}

static toml::array metaArrayToToml(const MetaArray &arr)
{
    toml::array result;
    for (const auto &elem : arr) {
        std::visit(
            [&result](const auto &v) {
                using T = std::decay_t<decltype(v)>;
                if constexpr (std::is_same_v<T, std::nullptr_t>) {
                    result.push_back(toml::value<std::string>{"null"});
                } else if constexpr (std::is_same_v<T, bool>) {
                    result.push_back(v);
                } else if constexpr (std::is_same_v<T, int64_t>) {
                    result.push_back(v);
                } else if constexpr (std::is_same_v<T, double>) {
                    result.push_back(v);
                } else if constexpr (std::is_same_v<T, std::string>) {
                    result.push_back(v);
                } else if constexpr (std::is_same_v<T, MetaSize>) {
                    toml::table sz;
                    sz.insert("width", v.width);
                    sz.insert("height", v.height);
                    result.push_back(std::move(sz));
                } else if constexpr (std::is_same_v<T, MetaArray>) {
                    result.push_back(metaArrayToToml(v));
                } else if constexpr (std::is_same_v<T, MetaStringMap>) {
                    // Nested map in array - recurse
                    toml::table inner;
                    for (const auto &[k2, v2] : v) {
                        std::visit(
                            [&inner, &k2](const auto &iv) {
                                using IT = std::decay_t<decltype(iv)>;
                                if constexpr (std::is_same_v<IT, std::nullptr_t>) {
                                    inner.insert(k2, std::string{"null"});
                                } else if constexpr (std::is_same_v<IT, bool>) {
                                    inner.insert(k2, iv);
                                } else if constexpr (std::is_same_v<IT, int64_t>) {
                                    inner.insert(k2, iv);
                                } else if constexpr (std::is_same_v<IT, double>) {
                                    inner.insert(k2, iv);
                                } else if constexpr (std::is_same_v<IT, std::string>) {
                                    inner.insert(k2, iv);
                                } else if constexpr (std::is_same_v<IT, MetaSize>) {
                                    toml::table sz;
                                    sz.insert("width", iv.width);
                                    sz.insert("height", iv.height);
                                    inner.insert(k2, std::move(sz));
                                } else if constexpr (std::is_same_v<IT, MetaArray>) {
                                    inner.insert(k2, metaArrayToToml(iv));
                                } else if constexpr (std::is_same_v<IT, MetaStringMap>) {
                                    // Deep nesting - handled recursively via toTomlTable
                                    inner.insert(k2, toTomlTable(iv));
                                }
                            },
                            static_cast<const MetaValue::Base &>(v2));
                    }
                    result.push_back(std::move(inner));
                }
            },
            static_cast<const MetaValue::Base &>(elem));
    }
    return result;
}

toml::table toTomlTable(const std::map<std::string, MetaValue> &attrs)
{
    toml::table tab;
    for (const auto &[key, val] : attrs) {
        std::visit(
            [&tab, &key](const auto &v) {
                using T = std::decay_t<decltype(v)>;
                if constexpr (std::is_same_v<T, std::nullptr_t>) {
                    // TOML has no null; skip or emit empty string
                } else if constexpr (std::is_same_v<T, bool>) {
                    tab.insert(key, v);
                } else if constexpr (std::is_same_v<T, int64_t>) {
                    tab.insert(key, v);
                } else if constexpr (std::is_same_v<T, double>) {
                    tab.insert(key, v);
                } else if constexpr (std::is_same_v<T, std::string>) {
                    if (!v.empty())
                        tab.insert(key, v);
                } else if constexpr (std::is_same_v<T, MetaSize>) {
                    toml::table sz;
                    sz.insert("width", v.width);
                    sz.insert("height", v.height);
                    tab.insert(key, std::move(sz));
                } else if constexpr (std::is_same_v<T, MetaArray>) {
                    if (!v.empty())
                        tab.insert(key, metaArrayToToml(v));
                } else if constexpr (std::is_same_v<T, MetaStringMap>) {
                    if (!v.empty())
                        tab.insert(key, toTomlTable(v));
                }
            },
            static_cast<const MetaValue::Base &>(val));
    }
    return tab;
}

void naturalNumListSort(std::vector<std::string> &list)
{
    std::sort(list.begin(), list.end(), [](const std::string &a, const std::string &b) {
        return strverscmp(a.c_str(), b.c_str()) < 0;
    });
}

std::string guessContentType(const fs::path &filePath, bool onlyCertain)
{
    gboolean resultUncertain = FALSE;
    g_autofree gchar *guess = g_content_type_guess(filePath.c_str(), nullptr, 0, &resultUncertain);
    if (resultUncertain && onlyCertain)
        return {};

    return (guess == nullptr ? std::string() : std::string(guess));
}

} // namespace Syntalos::edl

Updated on 2026-04-24 at 06:25:18 +0000