Restructure to util
add split Intent and Options
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
89dd5186f5
commit
70ae623a1d
17 changed files with 405 additions and 87 deletions
|
@ -5,7 +5,9 @@ project('xwim', 'cpp',
|
|||
'b_ndebug=if-release'])
|
||||
|
||||
add_global_arguments('-DVERSION='+meson.version(), language: 'cpp')
|
||||
add_global_arguments('-DSPDLOG_FMT_EXTERNAL', language: 'cpp')
|
||||
add_global_arguments('-DFMT_HEADER_ONLY', language: 'cpp')
|
||||
|
||||
subdir('src')
|
||||
subdir('doc')
|
||||
# subdir('test')
|
||||
subdir('test')
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "util/Common.hpp"
|
||||
|
||||
namespace xwim {
|
||||
using namespace std;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "util/Common.hpp"
|
||||
|
||||
namespace xwim {
|
||||
|
||||
|
|
52
src/UserOpt.cpp
Normal file
52
src/UserOpt.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
#include "UserOpt.hpp"
|
||||
|
||||
#include <tclap/ArgException.h>
|
||||
#include <tclap/CmdLine.h>
|
||||
#include <tclap/StdOutput.h>
|
||||
#include <tclap/SwitchArg.h>
|
||||
#include <tclap/UnlabeledMultiArg.h>
|
||||
#include <tclap/ValueArg.h>
|
||||
|
||||
template <>
|
||||
struct TCLAP::ArgTraits<std::filesystem::path> {
|
||||
// We use `operator=` here for path construction
|
||||
// because `operator>>` (`ValueLike`) causes a split at
|
||||
// whitespace
|
||||
typedef StringLike ValueCategory;
|
||||
};
|
||||
|
||||
namespace xwim {
|
||||
UserOpt::UserOpt(int argc, char** argv) {
|
||||
// clang-format off
|
||||
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<fs::path> arg_outfile
|
||||
{"o", "out", "Out <file-or-path>", false, fs::path{}, "A path on the filesystem", cmd};
|
||||
|
||||
TCLAP::UnlabeledMultiArg<fs::path> arg_paths
|
||||
{"files", "Archive to extract or files to compress", true, "A path on the filesystem", cmd};
|
||||
// clang-format on
|
||||
|
||||
cmd.parse(argc, argv);
|
||||
|
||||
this->compress = arg_compress.getValue();
|
||||
this->extract = arg_extract.getValue();
|
||||
this->interactive = arg_extract.getValue();
|
||||
if (arg_outfile.isSet()) this->out = arg_outfile.getValue();
|
||||
|
||||
if (arg_paths.isSet()) {
|
||||
this->paths =
|
||||
set<fs::path>{arg_paths.getValue().begin(), arg_paths.getValue().end()};
|
||||
}
|
||||
}
|
||||
} // namespace xwim
|
22
src/UserOpt.hpp
Normal file
22
src/UserOpt.hpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <set>
|
||||
|
||||
#include "util/Common.hpp"
|
||||
|
||||
namespace xwim {
|
||||
using namespace std;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
struct UserOpt {
|
||||
bool compress;
|
||||
bool extract;
|
||||
bool interactive;
|
||||
std::optional<fs::path> out;
|
||||
std::set<fs::path> paths;
|
||||
|
||||
UserOpt(int argc, char** argv);
|
||||
};
|
||||
|
||||
} // namespace xwim
|
|
@ -12,7 +12,7 @@
|
|||
#include <string>
|
||||
|
||||
#include "Archiver.hpp"
|
||||
#include "Common.hpp"
|
||||
#include "util/Common.hpp"
|
||||
|
||||
namespace xwim {
|
||||
using namespace std;
|
||||
|
|
48
src/Xwim.hpp
48
src/Xwim.hpp
|
@ -5,17 +5,61 @@
|
|||
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "Archiver.hpp"
|
||||
#include "util/Common.hpp"
|
||||
#include "UserOpt.hpp"
|
||||
|
||||
namespace xwim {
|
||||
using namespace std;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
enum class Action { UNKNOWN, EXTRACT, COMPRESS };
|
||||
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:
|
||||
|
|
15
src/XwimConfig.hpp
Normal file
15
src/XwimConfig.hpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
namespace xwim {
|
||||
|
||||
enum class Action { COMPRESS, EXTRACT };
|
||||
|
||||
class XwimConfig {
|
||||
|
||||
public:
|
||||
Action get_action();
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
122
src/XwimIntent.cpp
Normal file
122
src/XwimIntent.cpp
Normal file
|
@ -0,0 +1,122 @@
|
|||
#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
|
38
src/XwimIntent.hpp
Normal file
38
src/XwimIntent.hpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#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
|
|
@ -9,8 +9,8 @@
|
|||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "Archiver.hpp"
|
||||
#include "Common.hpp"
|
||||
#include "../Archiver.hpp"
|
||||
#include "../util/Common.hpp"
|
||||
|
||||
namespace xwim {
|
||||
using namespace std;
|
||||
|
|
65
src/main.cpp
65
src/main.cpp
|
@ -1,75 +1,20 @@
|
|||
#include <spdlog/common.h>
|
||||
#include <spdlog/logger.h>
|
||||
#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 <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "Log.hpp"
|
||||
#include "Xwim.hpp"
|
||||
#include "UserOpt.hpp"
|
||||
#include "util/Common.hpp"
|
||||
#include "util/Log.hpp"
|
||||
|
||||
using namespace xwim;
|
||||
using namespace std;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
template <>
|
||||
struct TCLAP::ArgTraits<std::filesystem::path> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
log::init();
|
||||
|
||||
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::ValueArg<fs::path> arg_outfile{
|
||||
"o", "out", "Out <file-or-path>",
|
||||
false, fs::path{}, "A path on the filesystem",
|
||||
cmd};
|
||||
TCLAP::UnlabeledMultiArg<fs::path> arg_infiles{
|
||||
"Files", "Archive to extract or files to compress", true,
|
||||
"A path on the filesystem", cmd};
|
||||
|
||||
Xwim xwim;
|
||||
|
||||
cmd.parse(argc, argv);
|
||||
|
||||
if (arg_extract.isSet() && arg_compress.isSet()) {
|
||||
// This is a bit ugly but `none-or-xor` only available in
|
||||
// tclap-1.4 which is not well supported in current
|
||||
// distributions
|
||||
auto out = TCLAP::StdOutput{};
|
||||
TCLAP::ArgException e{
|
||||
"Cannot compress `-c` and extract `-x` simultaneously"};
|
||||
try {
|
||||
out.failure(cmd, e);
|
||||
} catch (TCLAP::ExitException& e) {
|
||||
exit(e.getExitStatus());
|
||||
}
|
||||
}
|
||||
|
||||
// `none-or-xor` ensured already
|
||||
if (arg_extract.isSet()) xwim.setExtract();
|
||||
if (arg_compress.isSet()) xwim.setCompress();
|
||||
|
||||
if (arg_outfile.isSet()) xwim.setOut(arg_outfile.getValue());
|
||||
if (arg_infiles.isSet()) xwim.setIns(arg_infiles.getValue());
|
||||
|
||||
try {
|
||||
xwim.try_infer();
|
||||
xwim.dwim();
|
||||
} catch (XwimError& e) {
|
||||
spdlog::error(e.what());
|
||||
}
|
||||
UserOpt user_opt = UserOpt{argc, argv};
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
xwim_src = ['main.cpp', 'Xwim.cpp', 'Archiver.cpp']
|
||||
xwim_src = ['main.cpp', 'Xwim.cpp', 'Archiver.cpp', 'UserOpt.cpp']
|
||||
xwim_archiver = ['archiver/LibArchiver.cpp']
|
||||
|
||||
is_static = get_option('default_library')=='static'
|
||||
|
||||
xwim_libs = [dependency('libarchive', required: true, static: is_static),
|
||||
dependency('fmt', required: true, static: is_static),
|
||||
dependency('spdlog', required: true, static: is_static),
|
||||
dependency('fmt', required: true, static: is_static),
|
||||
dependency('tclap', required: true, static: is_static)]
|
||||
|
||||
executable('xwim', xwim_src+xwim_archiver, dependencies: xwim_libs)
|
||||
|
|
|
@ -2,22 +2,10 @@
|
|||
gtest_proj = subproject('gtest')
|
||||
gtest_dep = gtest_proj.get_variable('gtest_main_dep')
|
||||
|
||||
xwim_src = ['../src/archive.cpp',
|
||||
'../src/archive_sys.cpp']
|
||||
|
||||
subdir('archives')
|
||||
|
||||
archive_test_exe = executable('archive_test_exe',
|
||||
sources: ['archive_test.cpp', xwim_src],
|
||||
# subdir('archives')
|
||||
user_opt_test_exe = executable('user_opt_test_exe',
|
||||
sources: ['user_opt_test.cpp', '../src/UserOpt.cpp'],
|
||||
include_directories: ['../src'],
|
||||
dependencies: [gtest_dep, xwim_libs])
|
||||
dependencies: [gtest_dep])
|
||||
|
||||
|
||||
test('archive test', archive_test_exe)
|
||||
|
||||
fileformats_test_exe = executable('fileformats_test_exe',
|
||||
sources: ['fileformats_test.cpp', xwim_src],
|
||||
include_directories: ['../src'],
|
||||
dependencies: [gtest_dep, xwim_libs])
|
||||
|
||||
test('fileformats test', fileformats_test_exe)
|
||||
test('user opt parsing test', user_opt_test_exe)
|
||||
|
|
90
test/user_opt_test.cpp
Normal file
90
test/user_opt_test.cpp
Normal file
|
@ -0,0 +1,90 @@
|
|||
#include <gtest/gtest-death-test.h>
|
||||
#include "gtest/gtest.h"
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include "UserOpt.hpp"
|
||||
|
||||
TEST(UserOpt, compress) {
|
||||
using namespace xwim;
|
||||
|
||||
// clang-format off
|
||||
char* args[] = {
|
||||
const_cast<char*>("xwim"),
|
||||
const_cast<char*>("-c"),
|
||||
const_cast<char*>("mandator_paths"),
|
||||
nullptr};
|
||||
// clang-format on
|
||||
|
||||
UserOpt uo = UserOpt{3, args};
|
||||
ASSERT_TRUE(uo.compress);
|
||||
ASSERT_FALSE(uo.extract);
|
||||
}
|
||||
|
||||
TEST(UserOpt, exclusive_actions) {
|
||||
using namespace xwim;
|
||||
|
||||
// clang-format off
|
||||
char* args[] = {
|
||||
const_cast<char*>("xwim"),
|
||||
const_cast<char*>("-c"),
|
||||
const_cast<char*>("-x"),
|
||||
const_cast<char*>("mandatory_paths"),
|
||||
nullptr};
|
||||
// clang-format on
|
||||
|
||||
UserOpt uo = UserOpt{4, args};
|
||||
ASSERT_TRUE(uo.compress);
|
||||
ASSERT_TRUE(uo.extract);
|
||||
}
|
||||
|
||||
TEST(UserOpt, whitespace_in_path) {
|
||||
using namespace xwim;
|
||||
|
||||
// clang-format off
|
||||
char* args[] = {
|
||||
const_cast<char*>("xwim"),
|
||||
const_cast<char*>("-c"),
|
||||
const_cast<char*>("/foo/bar baz/a file"),
|
||||
nullptr};
|
||||
// clang-format on
|
||||
|
||||
UserOpt uo = UserOpt{3, args};
|
||||
ASSERT_TRUE(uo.paths.find(std::filesystem::path("/foo/bar baz/a file")) !=
|
||||
uo.paths.end());
|
||||
}
|
||||
|
||||
TEST(UserOpt, mixed_output_and_paths) {
|
||||
using namespace xwim;
|
||||
|
||||
// clang-format off
|
||||
char* args[] = {
|
||||
const_cast<char*>("xwim"),
|
||||
const_cast<char*>("-o"),
|
||||
const_cast<char*>("/foo/bar baz/output"),
|
||||
const_cast<char*>("/foo/bar baz/a path"),
|
||||
const_cast<char*>("/foo/bar baz/another path"),
|
||||
nullptr};
|
||||
// clang-format on
|
||||
|
||||
UserOpt uo = UserOpt{5, args};
|
||||
ASSERT_TRUE(uo.paths.find(std::filesystem::path("/foo/bar baz/a path")) !=
|
||||
uo.paths.end());
|
||||
ASSERT_TRUE(uo.paths.find(std::filesystem::path("/foo/bar baz/another path")) !=
|
||||
uo.paths.end());
|
||||
ASSERT_TRUE(uo.out == std::filesystem::path("/foo/bar baz/output"));
|
||||
}
|
||||
|
||||
TEST(UserOpt, output_defaults_to_nullopt) {
|
||||
using namespace xwim;
|
||||
|
||||
// clang-format off
|
||||
char* args[] = {
|
||||
const_cast<char*>("xwim"),
|
||||
const_cast<char*>("/foo/bar"),
|
||||
nullptr};
|
||||
// clang-format on
|
||||
|
||||
UserOpt uo = UserOpt{2, args};
|
||||
ASSERT_FALSE(uo.out);
|
||||
}
|
Loading…
Reference in a new issue