Refactor to new structure
This commit is contained in:
parent
7141e67e14
commit
e6a6e9268e
13 changed files with 334 additions and 179 deletions
|
@ -6,7 +6,7 @@ steps:
|
||||||
- name: build-shared
|
- name: build-shared
|
||||||
image: arminfriedl/xwim-build:shared
|
image: arminfriedl/xwim-build:shared
|
||||||
commands:
|
commands:
|
||||||
- meson wrap install gtest
|
- meson wrap install gtest || true
|
||||||
- meson target/shared
|
- meson target/shared
|
||||||
- ninja -C target/shared
|
- ninja -C target/shared
|
||||||
- mv target/shared/src/xwim xwim-x86_64-glibc-linux-shared
|
- mv target/shared/src/xwim xwim-x86_64-glibc-linux-shared
|
||||||
|
@ -14,7 +14,7 @@ steps:
|
||||||
- name: build-static
|
- name: build-static
|
||||||
image: arminfriedl/xwim-build:static
|
image: arminfriedl/xwim-build:static
|
||||||
commands:
|
commands:
|
||||||
- meson wrap install gtest
|
- meson wrap install gtest || true
|
||||||
- meson --default-library=static target/static
|
- meson --default-library=static target/static
|
||||||
- ninja -C target/static
|
- ninja -C target/static
|
||||||
- mv target/static/src/xwim xwim-x86_64-musl-linux-static
|
- mv target/static/src/xwim xwim-x86_64-musl-linux-static
|
||||||
|
@ -50,6 +50,7 @@ steps:
|
||||||
- name: build-shared
|
- name: build-shared
|
||||||
image: arminfriedl/xwim-build:shared
|
image: arminfriedl/xwim-build:shared
|
||||||
commands:
|
commands:
|
||||||
|
- meson wrap install gtest || true
|
||||||
- meson --buildtype=release target/shared
|
- meson --buildtype=release target/shared
|
||||||
- ninja -C target/shared
|
- ninja -C target/shared
|
||||||
- strip target/shared/src/xwim
|
- strip target/shared/src/xwim
|
||||||
|
@ -59,6 +60,7 @@ steps:
|
||||||
- name: build-static
|
- name: build-static
|
||||||
image: arminfriedl/xwim-build:static
|
image: arminfriedl/xwim-build:static
|
||||||
commands:
|
commands:
|
||||||
|
- meson wrap install gtest || true
|
||||||
- meson --buildtype=release --default-library=static target/static
|
- meson --buildtype=release --default-library=static target/static
|
||||||
- ninja -C target/static
|
- ninja -C target/static
|
||||||
- strip target/static/src/xwim
|
- strip target/static/src/xwim
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#include "Archiver.hpp"
|
#include "Archiver.hpp"
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
@ -9,6 +8,14 @@
|
||||||
|
|
||||||
#include "util/Common.hpp"
|
#include "util/Common.hpp"
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
namespace xwim {
|
namespace xwim {
|
||||||
using namespace std;
|
using namespace std;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
@ -90,7 +97,14 @@ fs::path strip_archive_extension(const fs::path& path) {
|
||||||
return tmp_path;
|
return tmp_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool can_extract(const fs::path& path) {
|
std::filesystem::path default_archive(const std::filesystem::path& base) {
|
||||||
|
string base_s = base.string();
|
||||||
|
string ext_s = default_extension;
|
||||||
|
|
||||||
|
return fs::path{fmt::format("{}{}", base_s, ext_s)};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool can_handle_archive(const fs::path& path) {
|
||||||
fs::path ext = archive_extension(path);
|
fs::path ext = archive_extension(path);
|
||||||
if (format_extensions.find(ext.string()) != format_extensions.end()) {
|
if (format_extensions.find(ext.string()) != format_extensions.end()) {
|
||||||
spdlog::debug("Found {} in known formats", ext);
|
spdlog::debug("Found {} in known formats", ext);
|
||||||
|
@ -125,4 +139,5 @@ unique_ptr<Archiver> make_archiver(const string& archive_name) {
|
||||||
archive_name};
|
archive_name};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xwim
|
} // namespace xwim
|
||||||
|
|
|
@ -40,8 +40,10 @@ class LibArchiver : public Archiver {
|
||||||
|
|
||||||
std::filesystem::path archive_extension(const std::filesystem::path& path);
|
std::filesystem::path archive_extension(const std::filesystem::path& path);
|
||||||
std::filesystem::path strip_archive_extension(const std::filesystem::path& path);
|
std::filesystem::path strip_archive_extension(const std::filesystem::path& path);
|
||||||
|
std::filesystem::path default_archive(const std::filesystem::path& base);
|
||||||
|
|
||||||
Format parse_format(const std::filesystem::path& path);
|
Format parse_format(const std::filesystem::path& path);
|
||||||
bool can_extract(const std::filesystem::path& path);
|
bool can_handle_archive(const std::filesystem::path& path);
|
||||||
|
|
||||||
std::unique_ptr<Archiver> make_archiver(const std::string& archive_name);
|
std::unique_ptr<Archiver> make_archiver(const std::string& archive_name);
|
||||||
|
|
||||||
|
|
190
src/UserIntent.cpp
Normal file
190
src/UserIntent.cpp
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
#include "UserIntent.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include "Archiver.hpp"
|
||||||
|
|
||||||
|
namespace xwim {
|
||||||
|
|
||||||
|
unique_ptr<UserIntent> make_intent(const UserOpt &userOpt) {
|
||||||
|
if (userOpt.wants_compress() && userOpt.wants_extract()) {
|
||||||
|
throw XwimError("Cannot compress and extract simultaneously");
|
||||||
|
}
|
||||||
|
if(userOpt.paths.size() == 0) {
|
||||||
|
throw XwimError("No input given...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// compression intent explicitly specified
|
||||||
|
if (userOpt.wants_compress()) {
|
||||||
|
if (userOpt.paths.size() == 1) {
|
||||||
|
return make_unique<CompressSingleIntent>(
|
||||||
|
CompressSingleIntent{
|
||||||
|
*userOpt.paths.begin(),
|
||||||
|
userOpt.out
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userOpt.out.has_value()) {
|
||||||
|
throw XwimError("Cannot guess output for multiple targets");
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_unique<CompressManyIntent>(
|
||||||
|
CompressManyIntent{
|
||||||
|
userOpt.paths,
|
||||||
|
userOpt.out.value()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// extraction intent explicitly specified
|
||||||
|
if (userOpt.wants_extract()) {
|
||||||
|
for (path p: userOpt.paths) {
|
||||||
|
if (!can_handle_archive(p)) {
|
||||||
|
throw XwimError("Cannot extract path {}", p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_unique<ExtractIntent>(
|
||||||
|
ExtractIntent{
|
||||||
|
userOpt.paths,
|
||||||
|
userOpt.out
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// no intent explicitly specified, try to infer from input
|
||||||
|
|
||||||
|
bool can_extract_all = std::all_of(
|
||||||
|
userOpt.paths.begin(), userOpt.paths.end(),
|
||||||
|
[](path path) {
|
||||||
|
return can_handle_archive(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
bool is_out_archive = userOpt.out.has_value() && can_handle_archive(userOpt.out.value());
|
||||||
|
|
||||||
|
// out is explicitly specified and an archive, assume we want compression
|
||||||
|
if(is_out_archive) {
|
||||||
|
if(userOpt.paths.size() == 1) {
|
||||||
|
return make_unique<CompressSingleIntent>(
|
||||||
|
CompressSingleIntent{
|
||||||
|
*userOpt.paths.begin(),
|
||||||
|
userOpt.out
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_unique<CompressManyIntent>(
|
||||||
|
CompressManyIntent{
|
||||||
|
userOpt.paths,
|
||||||
|
userOpt.out.value() // this is ok is_out_archive checks for has_value()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// all inputs are extractable archives, assume extraction intent
|
||||||
|
if (can_extract_all) {
|
||||||
|
return make_unique<ExtractIntent>(
|
||||||
|
ExtractIntent{
|
||||||
|
userOpt.paths,
|
||||||
|
userOpt.out
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// at this point all we can hope for is that the intention is a single-path compression:
|
||||||
|
// we don't know how to extract it; we don't know (and can't guess) output for many-path compression;
|
||||||
|
if(userOpt.paths.size() == 1) {
|
||||||
|
return make_unique<CompressSingleIntent>(
|
||||||
|
CompressSingleIntent{
|
||||||
|
*userOpt.paths.begin(),
|
||||||
|
userOpt.out
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
throw XwimError("Cannot guess intent");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ExtractIntent::execute() {
|
||||||
|
bool has_out = this->out.has_value();
|
||||||
|
bool is_single = this->archives.size() == 1;
|
||||||
|
|
||||||
|
for (path p: this->archives) {
|
||||||
|
unique_ptr<Archiver> archiver = make_archiver(p);
|
||||||
|
path out;
|
||||||
|
|
||||||
|
if(has_out) {
|
||||||
|
if(is_single) { // just dump content of archive into `out`
|
||||||
|
std::filesystem::create_directories(this->out.value());
|
||||||
|
out = this->out.value();
|
||||||
|
} else { // create an `out` folder and extract inside there
|
||||||
|
std::filesystem::create_directories(this->out.value());
|
||||||
|
out = this->out.value() / strip_archive_extension(p);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out = std::filesystem::current_path() / strip_archive_extension(p);
|
||||||
|
std::filesystem::create_directories(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
archiver->extract(p, out);
|
||||||
|
|
||||||
|
// move folder if only one entry and that entries name is already
|
||||||
|
// the stripped archive name
|
||||||
|
auto dit = std::filesystem::directory_iterator(out);
|
||||||
|
|
||||||
|
if(dit == std::filesystem::directory_iterator()) {
|
||||||
|
spdlog::debug("Archive is empty");
|
||||||
|
} else if(is_directory(dit->path())){
|
||||||
|
auto first_path = dit->path();
|
||||||
|
auto next_entry = next(dit);
|
||||||
|
|
||||||
|
if(next_entry == std::filesystem::directory_iterator()) {
|
||||||
|
spdlog::debug("Archive has single entry which is a directory");
|
||||||
|
if(std::filesystem::equivalent(first_path.filename(), out.filename())) {
|
||||||
|
spdlog::debug("Archive entry named like archive");
|
||||||
|
int i = rand_int(0, 100000);
|
||||||
|
|
||||||
|
path tmp_out = path{out};
|
||||||
|
tmp_out.concat(fmt::format(".xwim{}", i));
|
||||||
|
|
||||||
|
spdlog::debug("Moving {} to {}", first_path, tmp_out);
|
||||||
|
std::filesystem::rename(first_path, tmp_out);
|
||||||
|
spdlog::debug("Removing parent {}", out);
|
||||||
|
std::filesystem::remove(out);
|
||||||
|
spdlog::debug("Moving {} to {}", tmp_out, out);
|
||||||
|
std::filesystem::rename(tmp_out, out);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
spdlog::debug("Archive entry differs from archive name");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spdlog::debug("Archive has multiple entries");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void CompressSingleIntent::execute() {
|
||||||
|
if(this->out.has_value()) {
|
||||||
|
if(!can_handle_archive(this->out.value())) {
|
||||||
|
throw XwimError("Unknown archive format {}", this->out.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
unique_ptr<Archiver> archiver = make_archiver(this->out.value());
|
||||||
|
set<path> ins{this->in};
|
||||||
|
archiver->compress(ins, this->out.value());
|
||||||
|
} else {
|
||||||
|
path out = default_archive(strip_archive_extension(this->in).stem());
|
||||||
|
unique_ptr<Archiver> archiver = make_archiver(out);
|
||||||
|
set<path> ins{this->in};
|
||||||
|
archiver->compress(ins, out);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void CompressManyIntent::execute() {
|
||||||
|
if(!can_handle_archive(this->out)) {
|
||||||
|
throw XwimError("Unknown archive format {}", this->out);
|
||||||
|
}
|
||||||
|
|
||||||
|
unique_ptr<Archiver> archiver = make_archiver(this->out);
|
||||||
|
archiver->compress(this->in_paths, this->out);
|
||||||
|
};
|
||||||
|
} // namespace xwim
|
89
src/UserIntent.hpp
Normal file
89
src/UserIntent.hpp
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "util/Common.hpp"
|
||||||
|
#include "UserOpt.hpp"
|
||||||
|
|
||||||
|
namespace xwim {
|
||||||
|
using namespace std;
|
||||||
|
using std::filesystem::path;
|
||||||
|
|
||||||
|
class UserIntent {
|
||||||
|
public:
|
||||||
|
virtual void execute() = 0;
|
||||||
|
virtual ~UserIntent() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Factory method to construct a UserIntent which implements `execute()` */
|
||||||
|
unique_ptr<UserIntent> make_intent(const UserOpt& userOpt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extraction intent
|
||||||
|
*
|
||||||
|
* Extracts one or multiple archives. Optionally extracts them to given `out` folder. Otherwise extracts them to the
|
||||||
|
* current working directory.
|
||||||
|
*/
|
||||||
|
class ExtractIntent: public UserIntent {
|
||||||
|
private:
|
||||||
|
set<path> archives;
|
||||||
|
optional<path> out;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ExtractIntent(set<path> archives, optional<path> out): archives(archives), out(out) {};
|
||||||
|
~ExtractIntent() = default;
|
||||||
|
|
||||||
|
void execute();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress intent for a single file or folder.
|
||||||
|
*
|
||||||
|
* Compresses a single path which may be a file or a folder.
|
||||||
|
*
|
||||||
|
* No `out` path given:
|
||||||
|
* - derives the archive name from the input path
|
||||||
|
* - uses the default archive format for the platform
|
||||||
|
*
|
||||||
|
* `out` path given:
|
||||||
|
* - `out` path must be a path with a valid archive name (including extension)
|
||||||
|
* - tries to compress the input to the out archive
|
||||||
|
* - if the `out` base name is different from the input base name, puts the input into a new folder
|
||||||
|
* with base name inside the archive (archive base name is always the name of the archive content)
|
||||||
|
*/
|
||||||
|
class CompressSingleIntent : public UserIntent {
|
||||||
|
private:
|
||||||
|
path in;
|
||||||
|
optional <path> out;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CompressSingleIntent(path in, optional <path> out) : UserIntent(), in(in), out(out) {};
|
||||||
|
|
||||||
|
~CompressSingleIntent() = default;
|
||||||
|
|
||||||
|
void execute();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress intent for multiple files and/or folders.
|
||||||
|
*
|
||||||
|
* Compresses multiple files and/or folders to a single archive as given by the `out` path. Since `out` cannot be
|
||||||
|
* guessed from the input in this case it is mandatory.
|
||||||
|
*
|
||||||
|
* A new, single root folder with base name equal to base name of the `out` archive is created inside the archive. All
|
||||||
|
* input files are put into this root folder.
|
||||||
|
*/
|
||||||
|
class CompressManyIntent: public UserIntent {
|
||||||
|
private:
|
||||||
|
set<path> in_paths;
|
||||||
|
path out;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CompressManyIntent(set<path> in_paths, path out): UserIntent(), in_paths(in_paths), out(out) {};
|
||||||
|
~CompressManyIntent() = default;
|
||||||
|
|
||||||
|
void execute();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xwim
|
|
@ -34,16 +34,17 @@ UserOpt::UserOpt(int argc, char** argv) {
|
||||||
{"o", "out", "Out <file-or-path>", false, fs::path{}, "A path on the filesystem", cmd};
|
{"o", "out", "Out <file-or-path>", false, fs::path{}, "A path on the filesystem", cmd};
|
||||||
|
|
||||||
TCLAP::UnlabeledMultiArg<fs::path> arg_paths
|
TCLAP::UnlabeledMultiArg<fs::path> arg_paths
|
||||||
{"files", "Archive to extract or files to compress", true, "A path on the filesystem", cmd};
|
{"files", "Archive(s) to extract or file(s) to compress", true, "A path on the filesystem", cmd};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
cmd.parse(argc, argv);
|
cmd.parse(argc, argv);
|
||||||
|
|
||||||
this->compress = arg_compress.getValue();
|
if (arg_compress.isSet()) this->compress = arg_compress.getValue();
|
||||||
this->extract = arg_extract.getValue();
|
if (arg_extract.isSet()) this->extract = arg_extract.getValue();
|
||||||
this->interactive = arg_extract.getValue();
|
|
||||||
if (arg_outfile.isSet()) this->out = arg_outfile.getValue();
|
if (arg_outfile.isSet()) this->out = arg_outfile.getValue();
|
||||||
|
|
||||||
|
this->interactive = arg_extract.getValue();
|
||||||
|
|
||||||
if (arg_paths.isSet()) {
|
if (arg_paths.isSet()) {
|
||||||
this->paths =
|
this->paths =
|
||||||
set<fs::path>{arg_paths.getValue().begin(), arg_paths.getValue().end()};
|
set<fs::path>{arg_paths.getValue().begin(), arg_paths.getValue().end()};
|
||||||
|
|
|
@ -10,13 +10,21 @@ using namespace std;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
struct UserOpt {
|
struct UserOpt {
|
||||||
bool compress;
|
optional<bool> compress;
|
||||||
bool extract;
|
optional<bool> extract;
|
||||||
bool interactive;
|
bool interactive;
|
||||||
std::optional<fs::path> out;
|
std::optional<fs::path> out;
|
||||||
std::set<fs::path> paths;
|
std::set<fs::path> paths;
|
||||||
|
|
||||||
UserOpt(int argc, char** argv);
|
UserOpt(int argc, char** argv);
|
||||||
|
|
||||||
|
bool wants_compress() const {
|
||||||
|
return this->compress.has_value() && this->compress.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wants_extract() const {
|
||||||
|
return this->extract.has_value() && this->extract.value();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace xwim
|
} // namespace xwim
|
||||||
|
|
|
@ -17,12 +17,11 @@ namespace xwim {
|
||||||
using namespace std;
|
using namespace std;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
enum class Action { EXTRACT, COMPRESS };
|
enum class Action { EXTRACT, COMPRESS };
|
||||||
|
|
||||||
struct XwimIntent {
|
struct XwimIntent {
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class XwimBuilder {
|
class XwimBuilder {
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -1,122 +0,0 @@
|
||||||
#include "XwimIntent.hpp"
|
|
||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
#include <tclap/ArgException.h>
|
|
||||||
#include <tclap/CmdLine.h>
|
|
||||||
#include <tclap/StdOutput.h>
|
|
||||||
#include <tclap/SwitchArg.h>
|
|
||||||
#include <tclap/UnlabeledMultiArg.h>
|
|
||||||
#include <tclap/ValueArg.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <exception>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <iostream>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "Archiver.hpp"
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct::TCLAP::ArgTraits<std::filesystem::path> {
|
|
||||||
// `operator=` here for path construction because `operator>>`
|
|
||||||
// (`ValueLike`) causes a split at whitespace
|
|
||||||
typedef StringLike ValueCategory;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace xwim {
|
|
||||||
|
|
||||||
void UserOpt::parse_args(int argc, char** argv) {
|
|
||||||
// clang-format off
|
|
||||||
// TODO: read version from -DVERSION during compilation
|
|
||||||
TCLAP::CmdLine cmd {"xwim - Do What I Mean Extractor", ' ', "0.3.0"};
|
|
||||||
|
|
||||||
TCLAP::SwitchArg arg_compress
|
|
||||||
{"c", "compress", "Compress <files>", cmd, false};
|
|
||||||
|
|
||||||
TCLAP::SwitchArg arg_extract
|
|
||||||
{"x", "extract", "Extract <file>", cmd, false};
|
|
||||||
|
|
||||||
TCLAP::SwitchArg arg_noninteractive
|
|
||||||
{"i", "non-interactive", "Non-interactive, fail on ambiguity", cmd, false};
|
|
||||||
|
|
||||||
TCLAP::ValueArg<path> arg_outfile
|
|
||||||
{"o", "out", "Out <file-or-path>", false, path{}, "A path on the filesystem", cmd};
|
|
||||||
|
|
||||||
TCLAP::UnlabeledMultiArg<path> arg_paths
|
|
||||||
{"files", "Archive to extract or files to compress", true, "A path on the filesystem", cmd};
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
// TODO: ideally we'd make sure during parsing that compress and extract
|
|
||||||
// cannot both be true
|
|
||||||
|
|
||||||
cmd.parse(argc, argv);
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
|
|
||||||
// Only set things if they are actually parsed from args. Otherwise we'd
|
|
||||||
// override settings set through other means, e.g. config files
|
|
||||||
if (arg_compress.isSet()) { this->compress = arg_compress.getValue(); }
|
|
||||||
if (arg_extract.isSet()) { this->extract = arg_extract.getValue(); }
|
|
||||||
if (arg_noninteractive.isSet()) { this->interactive = !arg_noninteractive.getValue(); }
|
|
||||||
if (arg_outfile.isSet()) { this->out = arg_outfile.getValue(); }
|
|
||||||
if (arg_paths.isSet()) { this->paths = arg_paths.getValue(); }
|
|
||||||
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
void UserOpt::parse_config(path config) { // TODO
|
|
||||||
spdlog::warn("Config parsing is not implemented");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UserIntent UserOpt::guess_intent() {
|
|
||||||
return UserIntent{action_intent(), out_intent(), paths_intent()};
|
|
||||||
}
|
|
||||||
|
|
||||||
Action UserOpt::action_intent() {
|
|
||||||
if (compress && extract) {
|
|
||||||
throw XwimError("Cannot compress and extract simultaneously");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compress) return Action::COMPRESS;
|
|
||||||
if (extract) return Action::EXTRACT;
|
|
||||||
|
|
||||||
bool can_extract_all = std::all_of(
|
|
||||||
paths.begin(), paths.end(), [](path path) { return can_extract(path); });
|
|
||||||
|
|
||||||
if (can_extract_all && !out) {
|
|
||||||
return Action::EXTRACT;
|
|
||||||
} // else if can_extract_all && !is_archive(out) -> EXTRACT
|
|
||||||
|
|
||||||
if (!can_extract_all && out /* && is_archive(out) */) {
|
|
||||||
return Action::COMPRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interactive) {
|
|
||||||
std::cout << "Do you want to compress (y/n)? [y] ";
|
|
||||||
char c;
|
|
||||||
std::cin >> c;
|
|
||||||
|
|
||||||
if (c != 'y' && c != 'n' && c != '\n') {
|
|
||||||
throw XwimError("Cannot guess action. Please answer 'y' or 'n'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == 'y' || c == '\n') {
|
|
||||||
return Action::COMPRESS;
|
|
||||||
} else if (c == 'n') {
|
|
||||||
return Action::EXTRACT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw XwimError("Cannot guess action (compress/extract)");
|
|
||||||
}
|
|
||||||
|
|
||||||
path UserOpt::out_intent() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
set<path> UserOpt::paths_intent() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace xwim
|
|
|
@ -1,38 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "util/Common.hpp"
|
|
||||||
|
|
||||||
namespace xwim {
|
|
||||||
using namespace std;
|
|
||||||
using std::filesystem::path;
|
|
||||||
|
|
||||||
enum class Action { COMPRESS, EXTRACT };
|
|
||||||
struct UserIntent {
|
|
||||||
Action action;
|
|
||||||
path out;
|
|
||||||
set<path> paths;
|
|
||||||
};
|
|
||||||
|
|
||||||
class UserOpt {
|
|
||||||
private:
|
|
||||||
bool compress = true;
|
|
||||||
bool extract = false;
|
|
||||||
bool interactive = true;
|
|
||||||
optional<path> out = nullopt;
|
|
||||||
vector<path> paths = std::vector<path>{};
|
|
||||||
|
|
||||||
Action action_intent();
|
|
||||||
path out_intent();
|
|
||||||
set<path> paths_intent();
|
|
||||||
|
|
||||||
public:
|
|
||||||
void parse_config(path config);
|
|
||||||
void parse_args(int argc, char** argv);
|
|
||||||
|
|
||||||
UserIntent guess_intent();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace xwim
|
|
|
@ -27,8 +27,9 @@ void LibArchiver::compress(set<fs::path> ins, fs::path archive_out) {
|
||||||
// complete type. `archive` is forward declared only.
|
// complete type. `archive` is forward declared only.
|
||||||
shared_ptr<archive> writer;
|
shared_ptr<archive> writer;
|
||||||
writer = shared_ptr<archive>(archive_write_new(), archive_write_free);
|
writer = shared_ptr<archive>(archive_write_new(), archive_write_free);
|
||||||
archive_write_add_filter_gzip(writer.get());
|
// archive_write_add_filter_gzip(writer.get());
|
||||||
archive_write_set_format_pax_restricted(writer.get());
|
// archive_write_set_format_pax_restricted(writer.get());
|
||||||
|
archive_write_set_format_filter_by_ext(writer.get(), archive_out.c_str());
|
||||||
archive_write_open_filename(writer.get(), archive_out.c_str());
|
archive_write_open_filename(writer.get(), archive_out.c_str());
|
||||||
|
|
||||||
shared_ptr<archive> reader;
|
shared_ptr<archive> reader;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "UserOpt.hpp"
|
#include "UserOpt.hpp"
|
||||||
|
#include "UserIntent.hpp"
|
||||||
#include "util/Common.hpp"
|
#include "util/Common.hpp"
|
||||||
#include "util/Log.hpp"
|
#include "util/Log.hpp"
|
||||||
|
|
||||||
|
@ -17,4 +18,10 @@ int main(int argc, char** argv) {
|
||||||
log::init();
|
log::init();
|
||||||
|
|
||||||
UserOpt user_opt = UserOpt{argc, argv};
|
UserOpt user_opt = UserOpt{argc, argv};
|
||||||
|
try {
|
||||||
|
unique_ptr<UserIntent> user_intent = make_intent(user_opt);
|
||||||
|
user_intent->execute();
|
||||||
|
} catch(XwimError& e) {
|
||||||
|
spdlog::error(e.what());
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
xwim_src = ['main.cpp', 'Xwim.cpp', 'Archiver.cpp', 'UserOpt.cpp']
|
xwim_src = ['main.cpp', 'Archiver.cpp', 'UserOpt.cpp', 'UserIntent.cpp']
|
||||||
|
|
||||||
xwim_archiver = ['archiver/LibArchiver.cpp']
|
xwim_archiver = ['archiver/LibArchiver.cpp']
|
||||||
|
|
||||||
is_static = get_option('default_library')=='static'
|
is_static = get_option('default_library')=='static'
|
||||||
|
|
Loading…
Reference in a new issue