Compare commits
No commits in common. "master" and "0.6" have entirely different histories.
7 changed files with 193 additions and 484 deletions
247
.gitignore
vendored
247
.gitignore
vendored
|
@ -2,11 +2,11 @@
|
|||
build/
|
||||
target/
|
||||
compile_commands.json
|
||||
.vscode
|
||||
.ccls-cache
|
||||
.idea/codeStyles/**
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/c++,vim,emacs,linux,macos,ninja,windows,jetbrains+all,clion+all,visualstudiocode
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=c++,vim,emacs,linux,macos,ninja,windows,jetbrains+all,clion+all,visualstudiocode
|
||||
# Created by https://www.gitignore.io/api/vim,c++,emacs,ninja
|
||||
# Edit at https://www.gitignore.io/?templates=vim,c++,emacs,ninja
|
||||
|
||||
### C++ ###
|
||||
# Prerequisites
|
||||
|
@ -42,94 +42,6 @@ compile_commands.json
|
|||
*.out
|
||||
*.app
|
||||
|
||||
### CLion+all ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### CLion+all Patch ###
|
||||
# Ignore everything but code style settings and run configurations
|
||||
# that are supposed to be shared within teams.
|
||||
|
||||
.idea/*
|
||||
|
||||
!.idea/codeStyles
|
||||
!.idea/runConfigurations
|
||||
|
||||
### Emacs ###
|
||||
# -*- mode: gitignore; -*-
|
||||
*~
|
||||
|
@ -181,108 +93,6 @@ flycheck_*.el
|
|||
/network-security.data
|
||||
|
||||
|
||||
### JetBrains+all ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
|
||||
# AWS User-specific
|
||||
|
||||
# Generated files
|
||||
|
||||
# Sensitive or high-churn files
|
||||
|
||||
# Gradle
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
|
||||
# Mongo Explorer plugin
|
||||
|
||||
# File-based project format
|
||||
|
||||
# IntelliJ
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
|
||||
# JIRA plugin
|
||||
|
||||
# Cursive Clojure plugin
|
||||
|
||||
# SonarLint plugin
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
|
||||
# Editor-based Rest Client
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
|
||||
### JetBrains+all Patch ###
|
||||
# Ignore everything but code style settings and run configurations
|
||||
# that are supposed to be shared within teams.
|
||||
|
||||
|
||||
|
||||
### Linux ###
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### macOS Patch ###
|
||||
# iCloud generated files
|
||||
*.icloud
|
||||
|
||||
### Ninja ###
|
||||
.ninja_deps
|
||||
.ninja_log
|
||||
|
@ -290,7 +100,6 @@ Temporary Items
|
|||
### Vim ###
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
!*.svg # comment out if you don't need vector files
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
|
@ -302,54 +111,14 @@ Sessionx.vim
|
|||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
|
||||
# Persistent undo
|
||||
[._]*.un~
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
# Coc configuration directory
|
||||
.vim
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
.ionide
|
||||
|
||||
### Windows ###
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/c++,vim,emacs,linux,macos,ninja,windows,jetbrains+all,clion+all,visualstudiocode
|
||||
# End of https://www.gitignore.io/api/vim,c++,emacs,ninja
|
||||
|
|
|
@ -1,226 +1,190 @@
|
|||
#include "UserIntent.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include "Archiver.hpp"
|
||||
|
||||
namespace xwim {
|
||||
unique_ptr<UserIntent> make_compress_intent(const UserOpt &userOpt) {
|
||||
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");
|
||||
}
|
||||
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...");
|
||||
}
|
||||
|
||||
return make_unique<CompressManyIntent>(
|
||||
CompressManyIntent{userOpt.paths, userOpt.out.value()});
|
||||
}
|
||||
// compression intent explicitly specified
|
||||
if (userOpt.wants_compress()) {
|
||||
if (userOpt.paths.size() == 1) {
|
||||
return make_unique<CompressSingleIntent>(
|
||||
CompressSingleIntent{
|
||||
*userOpt.paths.begin(),
|
||||
userOpt.out
|
||||
});
|
||||
}
|
||||
|
||||
unique_ptr<UserIntent> make_extract_intent(const UserOpt &userOpt) {
|
||||
for (const path &p : userOpt.paths) {
|
||||
if (!can_handle_archive(p)) {
|
||||
throw XwimError("Cannot extract path {}", p);
|
||||
}
|
||||
}
|
||||
if (!userOpt.out.has_value()) {
|
||||
throw XwimError("Cannot guess output for multiple targets");
|
||||
}
|
||||
|
||||
return make_unique<ExtractIntent>(ExtractIntent{userOpt.paths, userOpt.out});
|
||||
}
|
||||
return make_unique<CompressManyIntent>(
|
||||
CompressManyIntent{
|
||||
userOpt.paths,
|
||||
userOpt.out.value()
|
||||
});
|
||||
}
|
||||
|
||||
unique_ptr<UserIntent> try_infer_compress_intent(const UserOpt &userOpt) {
|
||||
if (!userOpt.out.has_value()) {
|
||||
spdlog::debug("No <out> provided");
|
||||
if (userOpt.paths.size() != 1) {
|
||||
spdlog::debug(
|
||||
"Not a single-path compression. Cannot guess <out> for many-path "
|
||||
"compression");
|
||||
return nullptr;
|
||||
// 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");
|
||||
}
|
||||
|
||||
spdlog::debug("Only one <path> provided. Assume single-path compression.");
|
||||
return make_unique<CompressSingleIntent>(
|
||||
CompressSingleIntent{*userOpt.paths.begin(), userOpt.out});
|
||||
}
|
||||
|
||||
spdlog::debug("<out> provided: {}", userOpt.out.value());
|
||||
if (can_handle_archive(userOpt.out.value())) {
|
||||
spdlog::debug("{} given and a known archive format, assume compression",
|
||||
userOpt.out.value());
|
||||
return make_compress_intent(userOpt);
|
||||
}
|
||||
void ExtractIntent::execute() {
|
||||
bool has_out = this->out.has_value();
|
||||
bool is_single = this->archives.size() == 1;
|
||||
|
||||
spdlog::debug(
|
||||
"Cannot compress multiple paths without a user-provided output archive");
|
||||
return nullptr;
|
||||
}
|
||||
for (path p: this->archives) {
|
||||
unique_ptr<Archiver> archiver = make_archiver(p);
|
||||
path out;
|
||||
|
||||
unique_ptr<UserIntent> try_infer_extract_intent(const UserOpt &userOpt) {
|
||||
bool can_extract_all =
|
||||
std::all_of(userOpt.paths.begin(), userOpt.paths.end(),
|
||||
[](const path &path) { return can_handle_archive(path); });
|
||||
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);
|
||||
}
|
||||
|
||||
if (!can_extract_all) {
|
||||
spdlog::debug(
|
||||
"Cannot extract all provided <paths>. Assume this is not an "
|
||||
"extraction.");
|
||||
for (const path &p : userOpt.paths) {
|
||||
if (!can_handle_archive(p)) {
|
||||
spdlog::debug("Cannot handle {}", p);
|
||||
}
|
||||
}
|
||||
archiver->extract(p, out);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
// move folder if only one entry and that entries name is already
|
||||
// the stripped archive name
|
||||
auto dit = std::filesystem::directory_iterator(out);
|
||||
|
||||
if (userOpt.out.has_value() && can_handle_archive(userOpt.out.value())) {
|
||||
spdlog::debug(
|
||||
"Could extract all provided <paths>. But also {} looks like an "
|
||||
"archive. Ambiguous intent. Assume this is not an extraction.",
|
||||
userOpt.out.value());
|
||||
return nullptr;
|
||||
}
|
||||
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);
|
||||
|
||||
spdlog::debug(
|
||||
"Could extract all provided <paths>. But also <out> looks like an "
|
||||
"archive. Ambiguous intent. Assume this is not an extraction.");
|
||||
return make_extract_intent(userOpt);
|
||||
}
|
||||
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);
|
||||
|
||||
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.empty()) {
|
||||
throw XwimError("No input given...");
|
||||
}
|
||||
path tmp_out = path{out};
|
||||
tmp_out.concat(fmt::format(".xwim{}", i));
|
||||
|
||||
// explicitly specified intent
|
||||
if (userOpt.wants_compress()) return make_compress_intent(userOpt);
|
||||
if (userOpt.wants_extract()) return make_extract_intent(userOpt);
|
||||
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);
|
||||
|
||||
spdlog::info("Intent not explicitly provided, trying to infer intent");
|
||||
} else {
|
||||
spdlog::debug("Archive entry differs from archive name");
|
||||
}
|
||||
} else {
|
||||
spdlog::debug("Archive has multiple entries");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (auto intent = try_infer_extract_intent(userOpt)) {
|
||||
spdlog::info("Extraction intent inferred");
|
||||
return intent;
|
||||
}
|
||||
spdlog::info("Cannot infer extraction intent");
|
||||
void CompressSingleIntent::execute() {
|
||||
if(this->out.has_value()) {
|
||||
if(!can_handle_archive(this->out.value())) {
|
||||
throw XwimError("Unknown archive format {}", this->out.value());
|
||||
}
|
||||
|
||||
if (auto intent = try_infer_compress_intent(userOpt)) {
|
||||
spdlog::info("Compression intent inferred");
|
||||
return intent;
|
||||
}
|
||||
spdlog::info("Cannot infer compression intent");
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
throw XwimError("Cannot guess intent");
|
||||
}
|
||||
void CompressManyIntent::execute() {
|
||||
if(!can_handle_archive(this->out)) {
|
||||
throw XwimError("Unknown archive format {}", this->out);
|
||||
}
|
||||
|
||||
void ExtractIntent::dwim_reparent(const path &out) {
|
||||
// move extraction if extraction resulted in only one entry and that entries
|
||||
// name is already the stripped archive name, i.e. reduce unnecessary nesting
|
||||
auto dit = std::filesystem::directory_iterator(out);
|
||||
auto dit_path = dit->path();
|
||||
|
||||
if (dit == std::filesystem::directory_iterator()) {
|
||||
spdlog::debug(
|
||||
"Cannot flatten extraction folder: extraction folder is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_directory(dit_path)) {
|
||||
spdlog::debug("Cannot flatten extraction folder: {} is not a directory",
|
||||
dit_path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (next(dit) != std::filesystem::directory_iterator()) {
|
||||
spdlog::debug("Cannot flatten extraction folder: multiple items extracted");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!std::filesystem::equivalent(dit_path.filename(), out.filename())) {
|
||||
spdlog::debug(
|
||||
"Cannot flatten extraction folder: archive entry differs from archive "
|
||||
"name [extraction folder: {}, archive entry: {}]",
|
||||
out.filename(), dit_path.filename());
|
||||
return;
|
||||
}
|
||||
|
||||
spdlog::debug("Output folder [{}] is equivalent to archive entry [{}]", out,
|
||||
dit_path);
|
||||
spdlog::info("Flattening extraction folder");
|
||||
|
||||
int i = rand_int(0, 100000);
|
||||
path tmp_out = path{out};
|
||||
tmp_out.concat(fmt::format(".xwim{}", i));
|
||||
spdlog::debug("Move {} to {}", dit_path, tmp_out);
|
||||
std::filesystem::rename(dit_path, tmp_out);
|
||||
spdlog::debug("Remove parent path {}", out);
|
||||
std::filesystem::remove(out);
|
||||
spdlog::debug("Moving {} to {}", tmp_out, out);
|
||||
std::filesystem::rename(tmp_out, out);
|
||||
}
|
||||
|
||||
path ExtractIntent::out_path(const path &p) {
|
||||
if (!this->out.has_value()) {
|
||||
// not out path given, create from archive name
|
||||
path out = std::filesystem::current_path() / strip_archive_extension(p);
|
||||
create_directories(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
if (this->archives.size() == 1) {
|
||||
// out given and only one archive to extract, just extract into `out`
|
||||
create_directories(this->out.value());
|
||||
return this->out.value();
|
||||
}
|
||||
|
||||
// out given and multiple archives to extract, create subfolder
|
||||
// for each archive
|
||||
create_directories(this->out.value());
|
||||
path out = this->out.value() / strip_archive_extension(p);
|
||||
return out;
|
||||
}
|
||||
|
||||
void ExtractIntent::execute() {
|
||||
for (const path &p : this->archives) {
|
||||
std::unique_ptr<Archiver> archiver = make_archiver(p);
|
||||
path out = this->out_path(p);
|
||||
archiver->extract(p, out);
|
||||
this->dwim_reparent(out);
|
||||
}
|
||||
}
|
||||
|
||||
path CompressSingleIntent::out_path() {
|
||||
if (this->out.has_value()) {
|
||||
if (!can_handle_archive(this->out.value())) {
|
||||
throw XwimError("Unknown archive format {}", this->out.value());
|
||||
}
|
||||
|
||||
return this->out.value();
|
||||
}
|
||||
|
||||
return default_archive(strip_archive_extension(this->in).stem());
|
||||
}
|
||||
|
||||
void CompressSingleIntent::execute() {
|
||||
path out = this->out_path();
|
||||
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);
|
||||
}
|
||||
unique_ptr<Archiver> archiver = make_archiver(this->out);
|
||||
archiver->compress(this->in_paths, this->out);
|
||||
};
|
||||
} // namespace xwim
|
||||
|
|
|
@ -30,14 +30,11 @@ private:
|
|||
set<path> archives;
|
||||
optional<path> out;
|
||||
|
||||
void dwim_reparent(const path& out);
|
||||
path out_path(const path& p);
|
||||
|
||||
public:
|
||||
public:
|
||||
ExtractIntent(set<path> archives, optional<path> out): archives(archives), out(out) {};
|
||||
~ExtractIntent() override = default;
|
||||
~ExtractIntent() = default;
|
||||
|
||||
void execute() override;
|
||||
void execute();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -58,15 +55,14 @@ private:
|
|||
class CompressSingleIntent : public UserIntent {
|
||||
private:
|
||||
path in;
|
||||
optional<path> out;
|
||||
|
||||
path out_path();
|
||||
optional <path> out;
|
||||
|
||||
public:
|
||||
CompressSingleIntent(path in, optional<path> out) : UserIntent(), in(in), out(out) {};
|
||||
~CompressSingleIntent() override = default;
|
||||
CompressSingleIntent(path in, optional <path> out) : UserIntent(), in(in), out(out) {};
|
||||
|
||||
void execute() override;
|
||||
~CompressSingleIntent() = default;
|
||||
|
||||
void execute();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -85,9 +81,9 @@ private:
|
|||
|
||||
public:
|
||||
CompressManyIntent(set<path> in_paths, path out): UserIntent(), in_paths(in_paths), out(out) {};
|
||||
~CompressManyIntent() override = default;
|
||||
~CompressManyIntent() = default;
|
||||
|
||||
void execute() override;
|
||||
void execute();
|
||||
};
|
||||
|
||||
} // namespace xwim
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
#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> {
|
||||
|
@ -28,9 +33,6 @@ UserOpt::UserOpt(int argc, char** argv) {
|
|||
TCLAP::ValueArg<fs::path> arg_outfile
|
||||
{"o", "out", "Out <file-or-path>", false, fs::path{}, "A path on the filesystem", cmd};
|
||||
|
||||
TCLAP::MultiSwitchArg arg_verbose
|
||||
{"v", "verbose", "Verbosity level", cmd, 0};
|
||||
|
||||
TCLAP::UnlabeledMultiArg<fs::path> arg_paths
|
||||
{"files", "Archive(s) to extract or file(s) to compress", true, "A path on the filesystem", cmd};
|
||||
// clang-format on
|
||||
|
@ -41,8 +43,7 @@ UserOpt::UserOpt(int argc, char** argv) {
|
|||
if (arg_extract.isSet()) this->extract = arg_extract.getValue();
|
||||
if (arg_outfile.isSet()) this->out = arg_outfile.getValue();
|
||||
|
||||
this->verbosity = arg_verbose.getValue();
|
||||
this->interactive = !arg_noninteractive.getValue();
|
||||
this->interactive = arg_extract.getValue();
|
||||
|
||||
if (arg_paths.isSet()) {
|
||||
this->paths =
|
||||
|
|
|
@ -13,7 +13,6 @@ struct UserOpt {
|
|||
optional<bool> compress;
|
||||
optional<bool> extract;
|
||||
bool interactive;
|
||||
int verbosity;
|
||||
std::optional<fs::path> out;
|
||||
std::set<fs::path> paths;
|
||||
|
||||
|
|
15
src/main.cpp
15
src/main.cpp
|
@ -1,11 +1,13 @@
|
|||
#include <spdlog/common.h>
|
||||
#include <spdlog/logger.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
#include "UserIntent.hpp"
|
||||
#include "UserOpt.hpp"
|
||||
#include "UserIntent.hpp"
|
||||
#include "util/Common.hpp"
|
||||
#include "util/Log.hpp"
|
||||
|
||||
|
@ -14,13 +16,12 @@ using namespace std;
|
|||
|
||||
int main(int argc, char** argv) {
|
||||
log::init();
|
||||
UserOpt user_opt = UserOpt{argc, argv};
|
||||
log::init(user_opt.verbosity);
|
||||
|
||||
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());
|
||||
unique_ptr<UserIntent> user_intent = make_intent(user_opt);
|
||||
user_intent->execute();
|
||||
} catch(XwimError& e) {
|
||||
spdlog::error(e.what());
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#ifdef NDEBUG
|
||||
#define XWIM_LOGLEVEL SPDLOG_LEVEL_ERROR
|
||||
|
@ -58,27 +57,7 @@ spdlog::level::level_enum _init_from_compile() {
|
|||
* The determined level is then set for the default logger via
|
||||
* `spdlog::set_level`.
|
||||
*/
|
||||
void init(int verbosity = -1,
|
||||
spdlog::level::level_enum level = spdlog::level::level_enum::off) {
|
||||
if (verbosity != -1) {
|
||||
switch (verbosity) {
|
||||
case 0:
|
||||
spdlog::set_level(spdlog::level::off);
|
||||
break;
|
||||
case 1:
|
||||
spdlog::set_level(spdlog::level::info);
|
||||
break;
|
||||
case 2:
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
spdlog::set_level(spdlog::level::trace);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void init(spdlog::level::level_enum level = spdlog::level::level_enum::off) {
|
||||
if (spdlog::level::level_enum::off != level) {
|
||||
spdlog::set_level(level);
|
||||
return;
|
||||
|
|
Loading…
Reference in a new issue