Add more formats, delete leftovers
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/tag Build is passing
continuous-integration/drone Build is passing

This commit is contained in:
Armin Friedl 2022-06-25 21:36:08 +02:00
parent e6a6e9268e
commit c6e0e92db2
Signed by: armin
GPG key ID: 48C726EEE7FBCBC8
6 changed files with 64 additions and 304 deletions

View file

@ -1,4 +1,5 @@
#include "Archiver.hpp"
#include "Formats.hpp"
#include <spdlog/spdlog.h>
@ -37,9 +38,9 @@ fs::path archive_extension(const fs::path& path) {
while (tmp_path.has_extension()) {
tmp_ext = tmp_path.extension() += tmp_ext;
auto search = extensions_format.find(tmp_ext);
Format format = find_extension_format(tmp_ext);
if (search != extensions_format.end()) {
if (format != Format::UNKNOWN) {
// (Combined) extension known. Remember as `ext` and keep
// looking for even longer extensions.
ext = tmp_ext;
@ -76,9 +77,9 @@ fs::path strip_archive_extension(const fs::path& path) {
tmp_ext = tmp_path.extension() += tmp_ext;
spdlog::debug("Looking for {} in known extensions", tmp_ext);
auto search = extensions_format.find(tmp_ext);
Format format = find_extension_format(tmp_ext);
tmp_longest_ext++;
if (search != extensions_format.end()) {
if (format != Format::UNKNOWN) {
// (Combined) extension known. Remember as `longest_ext` and keep
// looking for even longer extensions.
longest_ext = tmp_longest_ext;
@ -119,17 +120,20 @@ Format parse_format(const fs::path& path) {
spdlog::debug("Looking for path {}", path);
fs::path ext = archive_extension(path);
spdlog::debug("Looking for ext {}", ext);
auto search = extensions_format.find(ext);
if (search == extensions_format.end()) {
Format format = find_extension_format(ext);
if (format == Format::UNKNOWN) {
throw XwimError{"No known archiver for {}", path};
}
return search->second;
return format;
}
unique_ptr<Archiver> make_archiver(const string& archive_name) {
switch (parse_format(archive_name)) {
case Format::TAR_GZ:
case Format::TAR_GZIP: case Format::TAR_BZIP2:
case Format::TAR_COMPRESS: case Format::TAR_LZIP:
case Format::TAR_XZ: case Format::TAR_ZSTD:
case Format::ZIP:
return make_unique<LibArchiver>();
default:

View file

@ -8,17 +8,10 @@
#include <set>
#include "util/Common.hpp"
#include "Formats.hpp"
namespace xwim {
// Invariant:
// `extensions_format` defines a surjection from `format_extensions`
// to `Formats`
const std::set<std::string> format_extensions{".tar.gz", ".zip"};
enum class Format { TAR_GZ, ZIP };
const std::map<std::string, Format> extensions_format{
{".tar.gz", Format::TAR_GZ}, {".zip", Format::ZIP}};
class Archiver {
public:
virtual void compress(std::set<std::filesystem::path> ins,

49
src/Formats.hpp Normal file
View file

@ -0,0 +1,49 @@
#pragma once
namespace xwim {
using namespace std;
// Invariant:
// `extensions_format` defines a surjection from `format_extensions`
// to `Formats`
enum class Format {
UNKNOWN,
TAR_BZIP2, TAR_GZIP, TAR_LZIP, TAR_XZ, TAR_COMPRESS, TAR_ZSTD,
ZIP
};
const set<string> format_extensions{
// tar formats see: https://en.wikipedia.org/wiki/Tar_(computing)#Suffixes_for_compressed_files
/* bzip2 */ ".tar.bz2", ".tb2", ".tbz", ".tbz2", ".tz2",
/* gzip */ ".tar.gz", ".taz", ".tgz",
/* lzip */ ".tar.lz",
/* xz */ ".tar.xz", ".txz",
/* compress */ ".tar.Z", ".tZ", ".taZ",
/* zstd */ ".tar.zst", ".tzst",
/* zip */ ".zip"
};
const map<set<string>, Format> extensions_format{
{{".tar.bz2", ".tb2", ".tbz", ".tbz2", ".tz2"}, Format::TAR_BZIP2},
{{".tar.gz", ".taz", ".tgz"}, Format::TAR_GZIP},
{{".tar.lz"}, Format::TAR_LZIP},
{{".tar.xz", ".txz"}, Format::TAR_XZ},
{{".tar.Z", ".tZ", ".taZ"}, Format::TAR_COMPRESS},
{{".tar.zst", ".tzst"}, Format::TAR_ZSTD},
{{".zip"}, Format::ZIP}
};
inline Format find_extension_format(const string& ext) {
for(auto ef: extensions_format) {
auto f = ef.first.find(ext);
if(f != ef.first.end()) {
return ef.second;
}
}
return Format::UNKNOWN;
}
}

View file

@ -1,165 +0,0 @@
#include "Xwim.hpp"
#include <spdlog/spdlog.h>
#include <algorithm>
#include <cstdlib>
#include <filesystem>
#include <ios>
#include <iostream>
#include <iterator>
#include <random>
#include <string>
#include "Archiver.hpp"
#include "util/Common.hpp"
namespace xwim {
using namespace std;
namespace fs = std::filesystem;
#if defined(unix) || defined(__unix__) || defined(__unix)
std::string default_extension = ".tar.gz";
#elif defined(_win32) || defined(__win32__) || defined(__windows__)
std::string default_extension = ".zip";
#else
std::string default_extension = ".zip";
#endif
Xwim::Xwim() : action{Action::UNKNOWN} {}
void Xwim::try_infer() {
infer_action();
infer_output();
if (action == Action::COMPRESS) {
archiver = make_archiver(out.string());
} else if (action == Action::EXTRACT) {
// we can only handle one archive for extraction at a time.
// Checked in `infer_extraction_output`
archiver = make_archiver(ins.begin()->string());
}
}
void Xwim::dwim() {
switch (action) {
case Action::COMPRESS:
this->archiver->compress(ins, out);
break;
case Action::EXTRACT:
this->archiver->extract(*ins.begin(), out);
sanitize_output();
break;
default:
spdlog::error("Unknown action");
}
}
void Xwim::sanitize_output() {
fs::path in_stripped = xwim::strip_archive_extension(*ins.begin());
int count = 0;
fs::directory_entry first_entry;
for(auto& e: fs::directory_iterator(out)) {
count++;
if(first_entry.path().empty()) {
first_entry = e;
}
}
if (count >= 2) {
spdlog::debug("Found multiple entries in extraction directory. Moving {} to {}", out, in_stripped);
fs::rename(out, in_stripped);
} else {
if(first_entry.is_directory()) {
spdlog::debug("Found single directory in extraction directory. Moving {} to {}",
first_entry.path(), in_stripped);
fs::rename(first_entry, in_stripped);
fs::remove(out);
} else {
spdlog::debug(
"Found single file in extraction directory. Moving {} to {}", out, in_stripped);
fs::rename(out, in_stripped);
}
}
}
void Xwim::infer_action() {
if (action != Action::UNKNOWN) return;
if (ins.size() == 1 && can_extract(*ins.begin())) {
action = Action::EXTRACT;
} else {
action = Action::COMPRESS;
}
spdlog::debug("Inferred action: {}", action);
}
void Xwim::infer_output() {
if (!out.empty()) return;
switch (action) {
case Action::COMPRESS:
infer_compression_output();
break;
case Action::EXTRACT:
infer_extraction_output();
break;
default:
throw XwimError{"Cannot infer output, action is unknown"};
}
spdlog::debug("Inferred out: {}", out.string());
}
void Xwim::infer_compression_output() {
if (ins.size() == 1) {
// archive name is just the name of the input with default archive
// extension
fs::path archive_stem = xwim::strip_archive_extension(*ins.begin());
archive_stem += default_extension;
out = archive_stem;
} else {
// We cannot guess the name of the output archive
// TODO use readline/lineoise/editline for path completion
cout << "Archive name: ";
cin >> out;
out = fs::path(out);
}
}
void Xwim::infer_extraction_output() {
if (ins.size() > 1) {
throw XwimError{"Cannot extract more than one archive at a time"};
}
// create a temporary path for extraction
fs::path archive_stem = xwim::strip_archive_extension(*ins.begin());
// note: we use here what is considered an `extensions` by `fs::path` so that
// we can strip it again easily later on
archive_stem += ".";
archive_stem += to_string(rand_int(999, 99999));
archive_stem += ".tmp";
this->out = archive_stem;
}
void Xwim::setCompress() {
this->action = Action::COMPRESS;
spdlog::debug("Set action to {}", this->action);
}
void Xwim::setExtract() {
this->action = Action::EXTRACT;
spdlog::debug("Set action to {}", this->action);
}
void Xwim::setOut(fs::path path) {
this->out = path;
spdlog::debug("Set out to {}", this->out);
}
void Xwim::setIns(vector<fs::path> ins) {
this->ins.insert(ins.begin(), ins.end());
if (this->ins.size() != ins.size()) {
spdlog::warn("Duplicate input files found. Removed {} duplicate(s).",
(ins.size() - this->ins.size()));
}
}
} // namespace xwim

View file

@ -1,106 +0,0 @@
#pragma once
#include <fmt/core.h>
#include <fmt/format.h>
#include <exception>
#include <memory>
#include <optional>
#include <set>
#include <stdexcept>
#include "Archiver.hpp"
#include "util/Common.hpp"
#include "UserOpt.hpp"
namespace xwim {
using namespace std;
namespace fs = std::filesystem;
enum class Action { EXTRACT, COMPRESS };
struct XwimIntent {
};
class XwimBuilder {
private:
UserOpt user_opt;
public:
XwimBuilder(UserOpt user_opt) : user_opt(user_opt){};
Xwim build();
};
class Xwim {
public:
virtual XwimResult dwim() = 0;
};
class XwimCompressor : public Xwim {
private:
fs::path archive;
std::set<fs::path> paths;
};
class XwimExtractor : public Xwim {};
class XwimConfig {
public:
Action get_action();
}
class Xwim {
private:
XwimEngine xwim_engine;
UserOpt user_opt;
public:
Xwim(UserOpt user_opt);
void dwim();
}
class Xwim {
private:
Action action;
fs::path out;
set<fs::path> ins;
unique_ptr<Archiver> archiver;
void infer_action();
void infer_output();
void infer_compression_output();
void infer_extraction_output();
void sanitize_output();
public:
Xwim();
void try_infer();
void dwim();
void setCompress();
void setExtract();
void setOut(fs::path);
void setIns(vector<fs::path> ins);
};
} // namespace xwim
template <>
struct fmt::formatter<xwim::Action> {
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
template <typename FormatContext>
auto format(const xwim::Action& action, FormatContext& ctx) {
switch (action) {
case xwim::Action::UNKNOWN:
return format_to(ctx.out(), "UNKNOWN");
case xwim::Action::EXTRACT:
return format_to(ctx.out(), "EXTRACT");
case xwim::Action::COMPRESS:
return format_to(ctx.out(), "COMPRESS");
};
return format_to(ctx.out(), "");
}
};

View file

@ -1,15 +0,0 @@
#pragma once
namespace xwim {
enum class Action { COMPRESS, EXTRACT };
class XwimConfig {
public:
Action get_action();
}
}