Echo Writes Code

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include "crucible/boba/container.hpp"
#include "crucible/boba/errors.hpp"
#include "crucible/boba/read.hpp"
#include "crucible/core/format.inl"
#include "crucible/os/errors.hpp"
#include "crucible/os/fs.hpp"

#include <cstdlib>
#include <iostream>
#include <optional>
#include <sstream>
#include <string>
#include <variant>
#include <vector>

namespace crucible::bobadump {

struct NotAFile final {
  [[nodiscard]]
  auto describe() const -> std::string {
    return "Not a regular file";
  }
};

class BobaDumpError final {
public:
  explicit BobaDumpError(const boba::errors::BobaError &variant) :
    my_variant { variant } {
  }

  explicit BobaDumpError(const os::errors::OsError &variant) :
    my_variant { variant } {
  }

  explicit BobaDumpError(const NotAFile &variant) :
    my_variant { variant } {
  }

  [[nodiscard]]
  auto describe() const -> std::string {
    auto const describer {
      [](auto &&variant) {
        return variant.describe();
      }
    };

    return std::visit(describer, my_variant);
  }

private:
  std::variant<boba::errors::BobaError, os::errors::OsError, NotAFile> my_variant;
};

template<typename T>
using BobaDumpResult = core::either::Either<BobaDumpError, T>;

[[nodiscard]]
auto read_boba_container(std::string const &boba_file_path) -> BobaDumpResult<boba::container::Container> {
  auto const is_file_result = os::fs::is_file(boba_file_path);

  if (is_file_result.has_lhs()) {
    return core::either::make_lhs<BobaDumpError>(is_file_result.get_lhs());
  }

  bool const is_file = is_file_result.get_rhs();

  if (!is_file) {
    return core::either::make_lhs<BobaDumpError>(NotAFile {});
  }

  auto read_file_data_result = os::fs::read_file_data(boba_file_path);

  if (read_file_data_result.has_lhs()) {
    return core::either::make_lhs<BobaDumpError>(read_file_data_result.get_lhs());
  }

  auto const file_contents = std::move(read_file_data_result).get_rhs();
  auto from_bytes_result = crucible::boba::read::from_bytes(file_contents);

  if (from_bytes_result.has_lhs()) {
    return core::either::make_lhs<BobaDumpError>(from_bytes_result.get_lhs());
  }

  return from_bytes_result.forward_rhs();
}

template<typename FORWARD_ITERATOR>
auto join_strings(FORWARD_ITERATOR from, FORWARD_ITERATOR const &to, std::string const &delimiter) -> std::string {
  std::string result {};

  if (from != to) {
    result += *from;
    ++from;
  }

  while (from != to) {
    result += delimiter;
    result += *from;
    ++from;
  }

  return result;
}

auto dump_boba_container(boba::container::Container const &container) -> void {
  std::vector<std::string> block_descriptions {};

  for (auto const &header : container.headers) {
    std::ostringstream buffer;

    buffer
      << "Block " << block_descriptions.size() << ":\n"
      << "  • Signature: " << core::format::format_unsigned_hexadecimal(header.signature) << "\n"
      << "  • Data size: " << header.data_size << "\n";

    block_descriptions.emplace_back(buffer.str());
  }

  std::cout << join_strings(block_descriptions.cbegin(), block_descriptions.cend(), "\n");
}

}

auto main(int argc, char *argv[]) -> int {
  std::vector<std::string> const arguments(argv + 1, argv + argc);
  std::optional<std::string> boba_file_path {};

  auto current = arguments.begin();
  auto const end = arguments.end();

  while (current != end) {
    if (!boba_file_path.has_value()) {
      boba_file_path = *current++;
      continue;
    }

    std::cerr << "Unrecognized option or argument '" << *current << "'\n";
    return EXIT_FAILURE;
  }

  if (!boba_file_path.has_value()) {
    std::cerr << "Missing argument BOBA_FILE\n";
    return EXIT_FAILURE;
  }

  auto const container = crucible::bobadump::read_boba_container(boba_file_path.value());
  if (container.has_lhs()) {
    std::cerr << argv[0] << ": " << container.get_lhs().describe() << "\n";
    return EXIT_FAILURE;
  }

  crucible::bobadump::dump_boba_container(container.get_rhs());
  return EXIT_SUCCESS;
}