WIP: feature/1-add-support-for-fs-crud #47
62 changed files with 888 additions and 172 deletions
|
@ -7,16 +7,21 @@
|
|||
# - "path/to/repo2"
|
||||
|
||||
# List of patterns to ignore during preloading
|
||||
ignore_list:
|
||||
- ^\.git/
|
||||
- ^lib.*
|
||||
- ^doc/
|
||||
exclude_list:
|
||||
- ^bin/
|
||||
- ^\.code_preloader.yml
|
||||
- ^doc/
|
||||
- ^\.drone.yml
|
||||
- ^\.git/
|
||||
- ^\.gitattributes
|
||||
- ^\.gitignore
|
||||
- ^lib.*
|
||||
- ^LICENSES/
|
||||
- ^_prompts/
|
||||
- ^\.reuse/
|
||||
- ^LICENSES/
|
||||
- ^\.vagrant/
|
||||
- ^scripts/
|
||||
- ^\.tool-versions
|
||||
- ^\.vagrant/
|
||||
|
||||
# Path to the output file (if null, output to STDOUT)
|
||||
output_path: null
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -7,3 +7,5 @@
|
|||
.vagrant
|
||||
bin
|
||||
lib
|
||||
.aider*
|
||||
.env
|
||||
|
|
7
Makefile
7
Makefile
|
@ -11,7 +11,7 @@ prepare:
|
|||
shards install
|
||||
|
||||
build:
|
||||
shards build --error-trace -Dpreview_mt
|
||||
shards build --progress --error-trace -Dpreview_mt
|
||||
@echo SUCCESS
|
||||
|
||||
watch:
|
||||
|
@ -21,10 +21,13 @@ spec: test
|
|||
test:
|
||||
crystal spec --error-trace
|
||||
|
||||
format:
|
||||
crystal tool format
|
||||
|
||||
install:
|
||||
install \
|
||||
-m 755 \
|
||||
bin/code-preloader \
|
||||
bin/mfm \
|
||||
$(PREFIX)/bin
|
||||
|
||||
.PHONY: spec test build all prepare install
|
||||
|
|
19
README.md
19
README.md
|
@ -14,6 +14,8 @@
|
|||
> version of our project, please visit our primary repository at:
|
||||
> <https://code.apps.glenux.net/glenux/mfm>.
|
||||
|
||||
<!-- hello -->
|
||||
|
||||
# Minimalist Fuse Manager (MFM)
|
||||
|
||||
MFM is a Crystal-lang CLI designed to streamline the management of various FUSE filesystems, such as sshfs, gocryptfs, httpdirfs, and more. Through its user-friendly interface, users can effortlessly mount and unmount filesystems, get real-time filesystem status, and handle errors proficiently.
|
||||
|
@ -44,18 +46,23 @@ To build from source, you'll also need:
|
|||
For Debian/Ubuntu you can use the following command:
|
||||
|
||||
```shell-session
|
||||
$ sudo apt-get update && sudo apt-get install libpcre3-dev libevent-2.1-dev
|
||||
$ sudo apt-get update && sudo apt-get install libpcre3-dev libevent-2.1-dev make
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
### 1. From Source
|
||||
|
||||
1. Clone or download the source code.
|
||||
2. Navigate to the source directory.
|
||||
3. Run `shards install` to fetch dependencies.
|
||||
4. Compile using `shards build`.
|
||||
5. The compiled binary will be in the `bin` directory.
|
||||
To get started with MFM, ensure that you have the prerequisites installed on your system (see above).
|
||||
|
||||
Then follow these steps to install:
|
||||
|
||||
git clone https://code.apps.glenux.net/glenux/mfm
|
||||
cd mfm
|
||||
make prepare
|
||||
make build
|
||||
sudo make install # either to install system-wide
|
||||
make install PREFIX=$HOME/.local # or to install as a user
|
||||
|
||||
### 2. Binary Download
|
||||
|
||||
|
|
5
Vagrantfile
vendored
5
Vagrantfile
vendored
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
# frozen_string_literal: true
|
||||
|
||||
# -*- mode: ruby -*-
|
||||
|
|
|
@ -4,6 +4,10 @@ shards:
|
|||
git: https://github.com/crystal-ameba/ameba.git
|
||||
version: 1.6.1
|
||||
|
||||
baked_file_system:
|
||||
git: https://github.com/schovi/baked_file_system.git
|
||||
version: 0.10.0
|
||||
|
||||
crinja:
|
||||
git: https://github.com/straight-shoota/crinja.git
|
||||
version: 0.8.1
|
||||
|
|
|
@ -26,6 +26,9 @@ dependencies:
|
|||
github: hugopl/version_from_shard
|
||||
tablo:
|
||||
github: hutou/tablo
|
||||
baked_file_system:
|
||||
github: schovi/baked_file_system
|
||||
version: 0.10.0
|
||||
|
||||
development_dependencies:
|
||||
ameba:
|
||||
|
|
13
spec/commands/mapping_create_spec.cr
Normal file
13
spec/commands/mapping_create_spec.cr
Normal file
|
@ -0,0 +1,13 @@
|
|||
require "../spec_helper"
|
||||
require "../../src/commands/mapping_create"
|
||||
|
||||
describe GX::Commands::MappingCreate do
|
||||
context "Initialization" do
|
||||
it "initializes with a mock FileSystemManager and RootConfig" do
|
||||
config = GX::Config.new
|
||||
root_config = GX::Models::RootConfig.new
|
||||
command = GX::Commands::MappingCreate.new(config)
|
||||
command.should be_a(GX::Commands::MappingCreate)
|
||||
end
|
||||
end
|
||||
end
|
12
spec/commands/mapping_edit_spec.cr
Normal file
12
spec/commands/mapping_edit_spec.cr
Normal file
|
@ -0,0 +1,12 @@
|
|||
require "../spec_helper"
|
||||
require "../../src/commands/mapping_edit"
|
||||
|
||||
describe GX::Commands::MappingEdit do
|
||||
context "Initialization" do
|
||||
it "initializes with a mock FileSystemManager" do
|
||||
config = GX::Config.new
|
||||
command = GX::Commands::MappingEdit.new(config)
|
||||
command.should be_a(GX::Commands::MappingEdit)
|
||||
end
|
||||
end
|
||||
end
|
108
spec/commands/mapping_list_spec.cr
Normal file
108
spec/commands/mapping_list_spec.cr
Normal file
|
@ -0,0 +1,108 @@
|
|||
|
||||
require "../spec_helper"
|
||||
require "../../src/commands/mapping_list"
|
||||
require "../../src/models/gocryptfs_config"
|
||||
require "../../src/models/sshfs_config"
|
||||
require "../../src/models/httpdirfs_config"
|
||||
|
||||
describe GX::Commands::MappingList do
|
||||
context "Initialization" do
|
||||
it "initializes with a mock FileSystemManager and RootConfig" do
|
||||
config = GX::Config.new
|
||||
root_config = GX::Models::RootConfig.new
|
||||
command = GX::Commands::MappingList.new(config)
|
||||
command.should be_a(GX::Commands::MappingList)
|
||||
end
|
||||
end
|
||||
|
||||
context "Functioning" do
|
||||
it "lists mappings when there are no filesystems" do
|
||||
config = GX::Config.new
|
||||
root_config = GX::Models::RootConfig.new
|
||||
command = GX::Commands::MappingList.new(config)
|
||||
|
||||
output = capture_output do
|
||||
command.execute
|
||||
end
|
||||
|
||||
output.should include("TYPE")
|
||||
output.should include("NAME")
|
||||
output.should include("MOUNTED")
|
||||
end
|
||||
|
||||
it "lists mappings when there are multiple filesystems" do
|
||||
config = GX::Config.new
|
||||
root_config = GX::Models::RootConfig.new
|
||||
|
||||
gocryptfs_config = GX::Models::GoCryptFSConfig.new(
|
||||
GX::Parsers::Options::MappingCreateOptions.new(
|
||||
type: "gocryptfs",
|
||||
name: "test_gocryptfs",
|
||||
encrypted_path: "/encrypted/path"
|
||||
)
|
||||
)
|
||||
sshfs_config = GX::Models::SshFSConfig.new(
|
||||
GX::Parsers::Options::MappingCreateOptions.new(
|
||||
type: "sshfs",
|
||||
name: "test_sshfs",
|
||||
remote_user: "user",
|
||||
remote_host: "host",
|
||||
remote_path: "/remote/path"
|
||||
)
|
||||
)
|
||||
httpdirfs_config = GX::Models::HttpDirFSConfig.new(
|
||||
GX::Parsers::Options::MappingCreateOptions.new(
|
||||
type: "httpdirfs",
|
||||
name: "test_httpdirfs",
|
||||
url: "http://example.com"
|
||||
)
|
||||
)
|
||||
|
||||
root_config.add_filesystem(gocryptfs_config)
|
||||
root_config.add_filesystem(sshfs_config)
|
||||
root_config.add_filesystem(httpdirfs_config)
|
||||
|
||||
command = GX::Commands::MappingList.new(config)
|
||||
|
||||
output = capture_output do
|
||||
command.execute
|
||||
end
|
||||
|
||||
output.should include("gocryptfs")
|
||||
output.should include("test_gocryptfs")
|
||||
output.should include("false")
|
||||
|
||||
output.should include("sshfs")
|
||||
output.should include("test_sshfs")
|
||||
output.should include("false")
|
||||
|
||||
output.should include("httpdirfs")
|
||||
output.should include("test_httpdirfs")
|
||||
output.should include("false")
|
||||
end
|
||||
|
||||
it "ensures the output format is correct" do
|
||||
config = GX::Config.new
|
||||
root_config = GX::Models::RootConfig.new
|
||||
config.instance_variable_set("@root", root_config)
|
||||
|
||||
gocryptfs_config = GX::Models::GoCryptFSConfig.new(
|
||||
GX::Parsers::Options::MappingCreateOptions.new(
|
||||
type: "gocryptfs",
|
||||
name: "test_gocryptfs",
|
||||
encrypted_path: "/encrypted/path"
|
||||
)
|
||||
)
|
||||
root_config.add_filesystem(gocryptfs_config)
|
||||
|
||||
command = GX::Commands::MappingList.new(config)
|
||||
|
||||
output = capture_output do
|
||||
command.execute
|
||||
end
|
||||
|
||||
output.should match(/TYPE\s+NAME\s+MOUNTED/)
|
||||
output.should match(/gocryptfs\s+test_gocryptfs\s+false/)
|
||||
end
|
||||
end
|
||||
end
|
0
spec/parsers/completion_parser_spec.cr
Normal file
0
spec/parsers/completion_parser_spec.cr
Normal file
68
spec/parsers/config_parser_spec.cr
Normal file
68
spec/parsers/config_parser_spec.cr
Normal file
|
@ -0,0 +1,68 @@
|
|||
require "../spec_helper"
|
||||
require "../../src/parsers/config_parser"
|
||||
|
||||
describe GX::Parsers::ConfigParser do
|
||||
context "Initialization" do
|
||||
it "can initialize" do
|
||||
GX::Parsers::ConfigParser.new.should be_a(GX::Parsers::ConfigParser)
|
||||
end
|
||||
end
|
||||
|
||||
context "Functioning" do
|
||||
it "can parse 'init' subcommand" do
|
||||
config = GX::Config.new
|
||||
parser = OptionParser.new
|
||||
breadcrumbs = GX::Utils::BreadCrumbs.new(["mfm"])
|
||||
|
||||
GX::Parsers::ConfigParser.new.build(parser, breadcrumbs, config)
|
||||
|
||||
# Test 'init' subcommand recognition
|
||||
config.mode.should eq(GX::Types::Mode::GlobalTui) # default
|
||||
parser.parse(["init"])
|
||||
config.mode.should eq(GX::Types::Mode::ConfigInit)
|
||||
|
||||
# Test ConfigInitOptions instantiation
|
||||
config.config_init_options.should be_a(GX::Parsers::Options::ConfigInitOptions)
|
||||
|
||||
# Test banner update
|
||||
# FIXME: parser.banner.should include("Create initial mfm configuration")
|
||||
|
||||
# Test separator presence
|
||||
# FIXME: parser.banner.should include("Init options")
|
||||
end
|
||||
|
||||
it "can parse '-p' / '--path' option for 'init' subcommand" do
|
||||
config = GX::Config.new
|
||||
parser = OptionParser.new
|
||||
breadcrumbs = GX::Utils::BreadCrumbs.new(["mfm"])
|
||||
|
||||
GX::Parsers::ConfigParser.new.build(parser, breadcrumbs, config)
|
||||
parser.parse(["init", "-p", "/test/path"])
|
||||
pp config
|
||||
config.config_init_options.try do |opts|
|
||||
opts.path.should eq("/test/path")
|
||||
end
|
||||
|
||||
config = GX::Config.new
|
||||
parser = OptionParser.new
|
||||
breadcrumbs = GX::Utils::BreadCrumbs.new(["mfm"])
|
||||
|
||||
GX::Parsers::ConfigParser.new.build(parser, breadcrumbs, config)
|
||||
parser.parse(["init", "--path", "/test/path/2"])
|
||||
config.config_init_options.try do |opts|
|
||||
opts.path.should eq("/test/path/2")
|
||||
end
|
||||
end
|
||||
|
||||
it "should include help line for 'init' subcommand" do
|
||||
config = GX::Config.new
|
||||
parser = OptionParser.new
|
||||
breadcrumbs = GX::Utils::BreadCrumbs.new(["mfm"])
|
||||
|
||||
GX::Parsers::ConfigParser.new.build(parser, breadcrumbs, config)
|
||||
|
||||
# Test help line presence
|
||||
# FIXME: parser.banner.should include("Run 'mfm config init --help' for more information on a command.")
|
||||
end
|
||||
end
|
||||
end
|
0
spec/parsers/mapping_parser_spec.cr
Normal file
0
spec/parsers/mapping_parser_spec.cr
Normal file
0
spec/parsers/root_parser_spec.cr
Normal file
0
spec/parsers/root_parser_spec.cr
Normal file
|
@ -37,10 +37,10 @@ describe GX::Utils::BreadCrumbs do
|
|||
b1.to_s.should eq("")
|
||||
|
||||
b2 = b1 + "test1"
|
||||
b2.to_a.should eq("test1")
|
||||
b2.to_s.should eq("test1")
|
||||
|
||||
b3 = b2 + "test2"
|
||||
b3.to_a.should eq("test1 test2")
|
||||
b3.to_s.should eq("test1 test2")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
11
src/cli.cr
11
src/cli.cr
|
@ -30,13 +30,22 @@ module GX
|
|||
Parsers::RootParser.new.build(parser, breadcrumbs, @config)
|
||||
end
|
||||
pparser.parse(args)
|
||||
rescue e : OptionParser::MissingOption
|
||||
STDERR.puts "ERROR: #{e.message}".colorize(:red)
|
||||
exit(1)
|
||||
end
|
||||
|
||||
def run
|
||||
command = CommandFactory.create_command(@config, @config.mode)
|
||||
abort("ERROR: unknown command for mode #{@config.mode}") if command.nil?
|
||||
|
||||
command.try &.execute
|
||||
command.execute
|
||||
rescue e : ArgumentError
|
||||
STDERR.puts "ERROR: #{e.message}".colorize(:red)
|
||||
exit(1)
|
||||
rescue e : Exception
|
||||
STDERR.puts "ERROR: #{e.message}".colorize(:red)
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./commands"
|
||||
|
||||
module GX
|
||||
|
|
|
@ -1 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./commands/*"
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "../config"
|
||||
|
||||
module GX::Commands
|
||||
|
|
|
@ -1,11 +1,51 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./abstract_command"
|
||||
require "../file_storage"
|
||||
|
||||
module GX::Commands
|
||||
class ConfigInit < AbstractCommand
|
||||
def initialize(config : GX::Config) # FIXME
|
||||
def initialize(@config : GX::Config)
|
||||
end
|
||||
|
||||
def execute
|
||||
config_dir = File.join(@config.home_dir, ".config", "mfm")
|
||||
config_file_path = File.join(config_dir, "config.yml")
|
||||
|
||||
# Override the configuration path if provided
|
||||
puts "Configuration file path: #{config_file_path}"
|
||||
puts "Configuration file path: #{@config.path}"
|
||||
# pp @config
|
||||
@config.path.try do |path|
|
||||
config_file_path = path
|
||||
config_dir = File.dirname(path)
|
||||
end
|
||||
exit 1
|
||||
|
||||
# Guard condition to exit if the configuration file already exists
|
||||
if File.exists?(config_file_path)
|
||||
puts "Configuration file already exists at #{config_file_path}. No action taken."
|
||||
return
|
||||
end
|
||||
|
||||
puts "Creating initial configuration file at #{config_file_path}"
|
||||
|
||||
# Ensure the configuration directory exists
|
||||
FileUtils.mkdir_p(config_dir)
|
||||
|
||||
# Read the default configuration content from the baked file storage
|
||||
default_config_content = FileStorage.get("sample.mfm.yaml")
|
||||
|
||||
# Write the default configuration to the target path
|
||||
File.write(config_file_path, default_config_content)
|
||||
|
||||
puts "Configuration file created successfully."
|
||||
rescue ex
|
||||
STDERR.puts "Error creating the configuration file: #{ex.message}"
|
||||
exit(1)
|
||||
end
|
||||
|
||||
def self.handles_mode
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./abstract_command"
|
||||
|
||||
module GX::Commands
|
||||
|
@ -6,10 +11,12 @@ module GX::Commands
|
|||
end
|
||||
|
||||
def execute
|
||||
puts "FIXME: detect option (either zsh or bash)"
|
||||
puts "FIXME: output the right file from embedded data"
|
||||
end
|
||||
|
||||
def self.handles_mode
|
||||
GX::Types::Mode::GlobalConfig
|
||||
GX::Types::Mode::GlobalCompletion
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./abstract_command"
|
||||
|
||||
module GX::Commands
|
||||
class GlobalConfig < AbstractCommand
|
||||
def initialize(config : GX::Config) # FIXME
|
||||
def initialize(config : GX::Config)
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./abstract_command"
|
||||
|
||||
module GX::Commands
|
||||
class GlobalHelp < AbstractCommand
|
||||
def initialize(@config : GX::Config) # FIXME
|
||||
def initialize(@config : GX::Config)
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
require "./abstract_command"
|
||||
|
||||
module GX::Commands
|
||||
class GlobalMapping < AbstractCommand
|
||||
def initialize(config : GX::Config) # FIXME
|
||||
end
|
||||
|
||||
def execute
|
||||
# FIXME: implement
|
||||
end
|
||||
|
||||
def self.handles_mode
|
||||
GX::Types::Mode::GlobalMapping
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,9 +1,14 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./abstract_command"
|
||||
require "../file_system_manager"
|
||||
|
||||
module GX::Commands
|
||||
class GlobalTui < AbstractCommand
|
||||
@file_system_manager : FileSystemManager
|
||||
# @file_system_manager : FileSystemManager
|
||||
|
||||
def initialize(@config : GX::Config)
|
||||
@config.load_from_env
|
||||
|
@ -15,7 +20,7 @@ module GX::Commands
|
|||
filesystem = @file_system_manager.choose_filesystem
|
||||
raise Models::InvalidFilesystemError.new("Invalid filesystem") if filesystem.nil?
|
||||
@file_system_manager.mount_or_umount(filesystem)
|
||||
@file_system_manager.auto_open(filesystem) if filesystem.mounted? && @config.auto_open
|
||||
@file_system_manager.auto_open(filesystem) if filesystem.mounted? && @config.auto_open?
|
||||
end
|
||||
|
||||
def self.handles_mode
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./abstract_command"
|
||||
require "../config"
|
||||
|
||||
module GX::Commands
|
||||
class GlobalVersion < AbstractCommand
|
||||
def initialize(config : GX::Config) # FIXME
|
||||
def initialize(config : GX::Config)
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
|
@ -1,16 +1,57 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./abstract_command"
|
||||
require "../models/filesystem_factory"
|
||||
|
||||
module GX::Commands
|
||||
class MappingCreate < AbstractCommand
|
||||
def initialize(config : GX::Config) # FIXME
|
||||
def initialize(@config : GX::Config)
|
||||
@config.load_from_env
|
||||
@config.load_from_file
|
||||
@config.save_to_file
|
||||
end
|
||||
|
||||
def execute
|
||||
# FIXME: implement
|
||||
# Assuming mapping_create_options is passed to this command with necessary details
|
||||
create_options = @config.mapping_create_options
|
||||
|
||||
# Validate required arguments
|
||||
if create_options.nil?
|
||||
raise ArgumentError.new("Mapping create options are required")
|
||||
end
|
||||
if create_options.name.nil? || create_options.name.try &.empty?
|
||||
raise ArgumentError.new("Name is required to create a mapping.")
|
||||
end
|
||||
if create_options.type.nil? || create_options.type.try &.empty?
|
||||
raise ArgumentError.new("Type is required to create a mapping.")
|
||||
end
|
||||
|
||||
# Create the appropriate filesystem config based on the type
|
||||
filesystem_config = GX::Models::FilesystemFactory.build(create_options)
|
||||
|
||||
# Append the new filesystem config to the root config
|
||||
@config.root.try do |root|
|
||||
root.filesystems ||= [] of GX::Models::AbstractFilesystemConfig
|
||||
root.filesystems << filesystem_config
|
||||
end
|
||||
|
||||
puts "Mapping '#{create_options.name}' created and added to configuration successfully."
|
||||
end
|
||||
|
||||
def self.handles_mode
|
||||
GX::Types::Mode::MappingCreate
|
||||
end
|
||||
|
||||
|
||||
# validate create_options.PARAMETER and display error with description if
|
||||
# missing
|
||||
macro option_check(create_options, parameter, description)
|
||||
if create_options.{{ parameter.id }}.nil? || create_options.{{ parameter.id }}.try &.empty?
|
||||
raise ArgumentError.new("Parameter for " + {{description}} + " is required")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./abstract_command"
|
||||
|
||||
module GX::Commands
|
||||
class MappingDelete < AbstractCommand
|
||||
def initialize(config : GX::Config) # FIXME
|
||||
def initialize(config : GX::Config)
|
||||
end
|
||||
|
||||
def execute
|
||||
# FIXME: implement
|
||||
# TODO: implement
|
||||
end
|
||||
|
||||
def self.handles_mode
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./abstract_command"
|
||||
|
||||
module GX::Commands
|
||||
class MappingEdit < AbstractCommand
|
||||
def initialize(config : GX::Config) # FIXME
|
||||
def initialize(config : GX::Config)
|
||||
end
|
||||
|
||||
def execute
|
||||
# FIXME: implement
|
||||
# TODO: implement
|
||||
end
|
||||
|
||||
def self.handles_mode
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./abstract_command"
|
||||
require "../file_system_manager"
|
||||
require "tablo"
|
||||
|
@ -7,7 +12,6 @@ module GX::Commands
|
|||
def initialize(@config : GX::Config)
|
||||
@config.load_from_env
|
||||
@config.load_from_file
|
||||
@file_system_manager = FileSystemManager.new(@config)
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
|
@ -1,26 +1,41 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./abstract_command"
|
||||
require "../file_system_manager"
|
||||
|
||||
module GX::Commands
|
||||
class MappingMount < AbstractCommand
|
||||
@file_system_manager : FileSystemManager
|
||||
|
||||
def initialize(@config : GX::Config) # FIXME
|
||||
def initialize(@config : GX::Config)
|
||||
@config.load_from_env
|
||||
@config.load_from_file
|
||||
@file_system_manager = FileSystemManager.new(@config)
|
||||
end
|
||||
|
||||
def execute
|
||||
filesystem = @file_system_manager.choose_filesystem
|
||||
raise Models::InvalidFilesystemError.new("Invalid filesystem") if filesystem.nil?
|
||||
# @file_system_manager.mount_or_umount(filesystem)
|
||||
filesystem.mount
|
||||
@file_system_manager.auto_open(filesystem) if filesystem.mounted? && @config.auto_open
|
||||
# get filesystem from config options
|
||||
# filesystem = @config.mapping_mount_options.filesystem
|
||||
|
||||
# raise Models::InvalidFilesystemError.new("Invalid filesystem") if filesystem.nil?
|
||||
# filesystem.mount
|
||||
# @file_system_manager.auto_open(filesystem) if filesystem.mounted? && @config.auto_open?
|
||||
|
||||
end
|
||||
|
||||
def self.handles_mode
|
||||
GX::Types::Mode::MappingMount
|
||||
end
|
||||
|
||||
private def _mount_filesystem(filesystem : Models::AbstractFilesystemConfig)
|
||||
raise Models::InvalidFilesystemError.new("Invalid filesystem") if filesystem.nil?
|
||||
if filesystem.mounted?
|
||||
Log.info { "Filesystem already mounted." }
|
||||
return
|
||||
end
|
||||
filesystem.mount
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,24 +1,40 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./abstract_command"
|
||||
require "../file_system_manager"
|
||||
|
||||
module GX::Commands
|
||||
class MappingUmount < AbstractCommand
|
||||
@file_system_manager : FileSystemManager
|
||||
|
||||
def initialize(@config : GX::Config) # FIXME
|
||||
def initialize(@config : GX::Config)
|
||||
@config.load_from_env
|
||||
@config.load_from_file
|
||||
@file_system_manager = FileSystemManager.new(@config)
|
||||
end
|
||||
|
||||
def execute
|
||||
filesystem = @file_system_manager.choose_filesystem
|
||||
raise Models::InvalidFilesystemError.new("Invalid filesystem") if filesystem.nil?
|
||||
filesystem.umount
|
||||
# root = @config.root
|
||||
# raise "Missing root config" if root.nil?
|
||||
|
||||
# filesystem = root.file_system_manager.choose_filesystem
|
||||
# raise Models::InvalidFilesystemError.new("Invalid filesystem") if filesystem.nil?
|
||||
|
||||
# filesystem.umount
|
||||
end
|
||||
|
||||
def self.handles_mode
|
||||
GX::Types::Mode::MappingUmount
|
||||
end
|
||||
|
||||
# OBSOLETE:
|
||||
private def umount_filesystem(filesystem : Models::AbstractFilesystemConfig)
|
||||
raise Models::InvalidFilesystemError.new("Invalid filesystem") if filesystem.nil?
|
||||
unless filesystem.mounted?
|
||||
Log.info { "Filesystem is not mounted." }
|
||||
return
|
||||
end
|
||||
filesystem.umount
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,6 +10,10 @@ require "./types/modes"
|
|||
require "./parsers/options/help_options"
|
||||
require "./parsers/options/config_options"
|
||||
require "./parsers/options/config_init_options"
|
||||
require "./parsers/options/mapping_create_options"
|
||||
require "./parsers/options/mapping_delete_options"
|
||||
require "./parsers/options/mapping_mount_options"
|
||||
require "./parsers/options/mapping_umount_options"
|
||||
require "./commands/abstract_command"
|
||||
|
||||
module GX
|
||||
|
@ -23,20 +27,21 @@ module GX
|
|||
record AddArgs, name : String, path : String
|
||||
record DelArgs, name : String
|
||||
|
||||
# getter filesystems : Array(Models::AbstractFilesystemConfig)
|
||||
getter home_dir : String
|
||||
getter root : Models::RootConfig?
|
||||
|
||||
property verbose : Bool
|
||||
property? verbose : Bool
|
||||
property mode : Types::Mode
|
||||
property path : String?
|
||||
property args : AddArgs.class | DelArgs.class | NoArgs.class
|
||||
property auto_open : Bool
|
||||
property? auto_open : Bool
|
||||
|
||||
# FIXME: refactor and remove these parts from here
|
||||
property help_options : Parsers::Options::HelpOptions?
|
||||
# TODO: refactor and remove these parts from here
|
||||
property config_init_options : Parsers::Options::ConfigInitOptions?
|
||||
property config_options : Parsers::Options::ConfigOptions?
|
||||
property help_options : Parsers::Options::HelpOptions?
|
||||
property mapping_create_options : Parsers::Options::MappingCreateOptions?
|
||||
property mapping_create_options : Parsers::Options::MappingCreateOptions?
|
||||
|
||||
def initialize
|
||||
raise Models::InvalidEnvironmentError.new("Home directory not found") if !ENV["HOME"]?
|
||||
|
@ -97,7 +102,12 @@ module GX
|
|||
file_data = File.read(config_path)
|
||||
file_patched = Crinja.render(file_data, {"env" => ENV.to_h})
|
||||
|
||||
root = Models::RootConfig.from_yaml(file_patched)
|
||||
begin
|
||||
root = Models::RootConfig.from_yaml(file_patched)
|
||||
rescue ex : YAML::ParseException
|
||||
STDERR.puts "Error parsing configuration file: #{ex.message}".colorize(:red)
|
||||
exit(1)
|
||||
end
|
||||
|
||||
mount_point_base_safe = root.global.mount_point_base
|
||||
raise Models::InvalidMountpointError.new("Invalid global mount point") if mount_point_base_safe.nil?
|
||||
|
@ -110,5 +120,15 @@ module GX
|
|||
end
|
||||
@root = root
|
||||
end
|
||||
|
||||
def save_to_file
|
||||
return if @path.nil?
|
||||
if @path
|
||||
File.write(@path.to_s, @root.to_yaml)
|
||||
else
|
||||
Log.error { "Configuration path is nil, cannot save configuration." }
|
||||
end
|
||||
Log.info { "Configuration saved to #{@path}" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
12
src/file_storage.cr
Normal file
12
src/file_storage.cr
Normal file
|
@ -0,0 +1,12 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "baked_file_system"
|
||||
|
||||
class FileStorage
|
||||
extend BakedFileSystem
|
||||
|
||||
bake_folder "../static"
|
||||
end
|
|
@ -1,4 +1,9 @@
|
|||
# require "./models/abstract_filesystem_config"
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./models/abstract_filesystem_config"
|
||||
require "./utils/fzf"
|
||||
|
||||
module GX
|
||||
|
@ -8,25 +13,6 @@ module GX
|
|||
def initialize(@config : Config)
|
||||
end
|
||||
|
||||
# OBSOLETE:
|
||||
# def mount_filesystem(filesystem : Models::AbstractFilesystemConfig)
|
||||
# raise Models::InvalidFilesystemError.new("Invalid filesystem") if filesystem.nil?
|
||||
# if filesystem.mounted?
|
||||
# Log.info { "Filesystem already mounted." }
|
||||
# return
|
||||
# end
|
||||
# filesystem.mount
|
||||
# end
|
||||
|
||||
# OBSOLETE:
|
||||
# def umount_filesystem(filesystem : Models::AbstractFilesystemConfig)
|
||||
# raise Models::InvalidFilesystemError.new("Invalid filesystem") if filesystem.nil?
|
||||
# unless filesystem.mounted?
|
||||
# Log.info { "Filesystem is not mounted." }
|
||||
# return
|
||||
# end
|
||||
# filesystem.umount
|
||||
# end
|
||||
|
||||
def mount_or_umount(selected_filesystem)
|
||||
if !selected_filesystem.mounted?
|
||||
|
@ -37,15 +23,15 @@ module GX
|
|||
end
|
||||
|
||||
def auto_open(filesystem)
|
||||
# FIXME: detect xdg-open and use it if possible
|
||||
# FIXME: detect mailcap and use it if no xdg-open found
|
||||
# FIXME: support user-defined command in configuration
|
||||
# FIXME: detect graphical environment
|
||||
# TODO: detect xdg-open presence and use it if possible
|
||||
# TODO: detect mailcap and use it if no xdg-open found
|
||||
# TODO: support user-defined command in configuration
|
||||
# TODO: detect graphical environment
|
||||
|
||||
mount_point_safe = filesystem.mount_point
|
||||
raise Models::InvalidMountpointError.new("Invalid filesystem") if mount_point_safe.nil?
|
||||
|
||||
if graphical_environment?
|
||||
if _graphical_environment?
|
||||
process = Process.new(
|
||||
"xdg-open", # # FIXME: make configurable
|
||||
[mount_point_safe],
|
||||
|
@ -88,24 +74,68 @@ module GX
|
|||
config_root.filesystems
|
||||
end
|
||||
|
||||
def choose_filesystem
|
||||
names_display = {} of String => NamedTuple(filesystem: Models::AbstractFilesystemConfig, ansi_name: String)
|
||||
# Get filesystem by name
|
||||
def detect_filesystem(filesystem_name : String) : GX::Models::AbstractFilesystemConfig?
|
||||
end
|
||||
|
||||
# Choose filesystem with fzf
|
||||
def choose_filesystem : GX::Models::AbstractFilesystemConfig?
|
||||
names_display = _filesystem_table
|
||||
|
||||
# FIXME: feat: allow to sort by name or by filesystem
|
||||
sorted_values = names_display.values.sort_by!(&.[:filesystem].name)
|
||||
result_filesystem_name = Utils::Fzf.run(sorted_values.map(&.[:ansi_name])).strip
|
||||
selected_filesystem = names_display[result_filesystem_name][:filesystem]
|
||||
puts ">> #{selected_filesystem.name}".colorize(:yellow)
|
||||
|
||||
if !selected_filesystem
|
||||
STDERR.puts "Mapping not found: #{selected_filesystem}.".colorize(:red)
|
||||
return
|
||||
end
|
||||
selected_filesystem
|
||||
end
|
||||
|
||||
private def _fzf_plain_name(filesystem : Models::AbstractFilesystemConfig) : String
|
||||
fs_str = filesystem.type.ljust(12, ' ')
|
||||
suffix = filesystem.mounted? ? "[open]" : ""
|
||||
"#{fs_str} #{filesystem.name} #{suffix}".strip
|
||||
end
|
||||
|
||||
private def _fzf_ansi_name(filesystem : Models::AbstractFilesystemConfig) : String
|
||||
fs_str = filesystem.type.ljust(12, ' ').colorize(:dark_gray)
|
||||
suffix = filesystem.mounted? ? "[#{"open".colorize(:green)}]" : ""
|
||||
"#{fs_str} #{filesystem.name} #{suffix}".strip
|
||||
end
|
||||
|
||||
private def _graphical_environment?
|
||||
if ENV["DISPLAY"]? || ENV["WAYLAND_DISPLAY"]?
|
||||
return true
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
|
||||
alias FilesystemTableItem =
|
||||
NamedTuple(
|
||||
filesystem: Models::AbstractFilesystemConfig,
|
||||
ansi_name: String
|
||||
)
|
||||
|
||||
alias FilesystemTable =
|
||||
Hash(
|
||||
String,
|
||||
FilesystemTableItem
|
||||
)
|
||||
|
||||
private def _filesystem_table : FilesystemTable
|
||||
names_display = {} of String => FilesystemTableItem
|
||||
|
||||
config_root = @config.root
|
||||
return if config_root.nil?
|
||||
return {} of String => FilesystemTableItem if config_root.nil?
|
||||
|
||||
config_root.filesystems.each do |filesystem|
|
||||
fs_str = filesystem.type.ljust(12, ' ')
|
||||
|
||||
suffix = ""
|
||||
suffix_ansi = ""
|
||||
if filesystem.mounted?
|
||||
suffix = "[open]"
|
||||
suffix_ansi = "[#{"open".colorize(:green)}]"
|
||||
end
|
||||
|
||||
result_name = "#{fs_str} #{filesystem.name} #{suffix}".strip
|
||||
ansi_name = "#{fs_str.colorize(:dark_gray)} #{filesystem.name} #{suffix_ansi}".strip
|
||||
result_name = _fzf_plain_name(filesystem)
|
||||
ansi_name = _fzf_ansi_name(filesystem)
|
||||
|
||||
names_display[result_name] = {
|
||||
filesystem: filesystem,
|
||||
|
@ -113,30 +143,7 @@ module GX
|
|||
}
|
||||
end
|
||||
|
||||
# # FIXME: feat: allow to sort by name or by filesystem
|
||||
sorted_values = names_display.values.sort_by { |item| item[:filesystem].name }
|
||||
result_filesystem_name = Utils::Fzf.run(sorted_values.map(&.[:ansi_name])).strip
|
||||
selected_filesystem = names_display[result_filesystem_name][:filesystem]
|
||||
puts ">> #{selected_filesystem.name}".colorize(:yellow)
|
||||
|
||||
if !selected_filesystem
|
||||
STDERR.puts "Vault not found: #{selected_filesystem}.".colorize(:red)
|
||||
return
|
||||
end
|
||||
return selected_filesystem
|
||||
end
|
||||
|
||||
private def generate_display_name(filesystem : Models::AbstractFilesystemConfig) : String
|
||||
fs_str = filesystem.type.ljust(12, ' ')
|
||||
suffix = filesystem.mounted? ? "[open]" : ""
|
||||
"#{fs_str} #{filesystem.name} #{suffix}".strip
|
||||
end
|
||||
|
||||
private def graphical_environment?
|
||||
if ENV["DISPLAY"]? || ENV["WAYLAND_DISPLAY"]?
|
||||
return true
|
||||
end
|
||||
return false
|
||||
names_display
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,6 +15,15 @@ module GX::Models
|
|||
abstract class AbstractFilesystemConfig
|
||||
include YAML::Serializable
|
||||
# include YAML::Serializable::Strict
|
||||
@@subs = [] of AbstractFilesystemConfig.class
|
||||
|
||||
macro inherited
|
||||
@@subs << {{@type.name.id}}
|
||||
end
|
||||
|
||||
def self.subs
|
||||
@@subs
|
||||
end
|
||||
|
||||
use_yaml_discriminator "type", {
|
||||
gocryptfs: GoCryptFSConfig,
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
module GX::Models::Concerns
|
||||
module Base
|
||||
def mounted? : Bool
|
||||
|
@ -31,7 +36,7 @@ module GX::Models::Concerns
|
|||
end
|
||||
end
|
||||
|
||||
def _mount_wrapper(&block) : Nil
|
||||
def _mount_wrapper(&) : Nil
|
||||
mount_point_safe = mount_point
|
||||
return if mount_point_safe.nil?
|
||||
|
||||
|
@ -46,7 +51,7 @@ module GX::Models::Concerns
|
|||
if result_status.success?
|
||||
puts "Models #{name} is now available on #{mount_point_safe}".colorize(:green)
|
||||
else
|
||||
puts "Error mounting the vault".colorize(:red)
|
||||
puts "Error mounting the mapping".colorize(:red)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
|
21
src/models/filesystem_factory.cr
Normal file
21
src/models/filesystem_factory.cr
Normal file
|
@ -0,0 +1,21 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
module GX::Models
|
||||
class FilesystemFactory
|
||||
def self.build(create_options)
|
||||
case create_options.type
|
||||
when "gocryptfs"
|
||||
GoCryptFSConfig.new(create_options)
|
||||
when "sshfs"
|
||||
SshFSConfig.new(create_options)
|
||||
when "httpdirfs"
|
||||
HttpDirFSConfig.new(create_options)
|
||||
else
|
||||
raise ArgumentError.new("Unsupported mapping type: #{create_options.type}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,6 +13,11 @@ module GX::Models
|
|||
|
||||
include Concerns::Base
|
||||
|
||||
def initialize(create_options)
|
||||
@name = create_options.name.as(String)
|
||||
@encrypted_path = create_options.encrypted_path.as(String)
|
||||
end
|
||||
|
||||
def _mounted_prefix
|
||||
"#{encrypted_path}"
|
||||
end
|
||||
|
@ -32,7 +37,8 @@ module GX::Models
|
|||
output: STDOUT,
|
||||
error: STDERR
|
||||
)
|
||||
return process.wait
|
||||
process.wait
|
||||
end
|
||||
def self.name ; "gocryptfs" ; end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,6 +13,11 @@ module GX::Models
|
|||
|
||||
include Concerns::Base
|
||||
|
||||
def initialize(create_options)
|
||||
@name = create_options.name.as(String)
|
||||
@url = create_options.url.as(String)
|
||||
end
|
||||
|
||||
def _mounted_prefix
|
||||
"httpdirfs"
|
||||
end
|
||||
|
@ -32,7 +37,8 @@ module GX::Models
|
|||
output: STDOUT,
|
||||
error: STDERR
|
||||
)
|
||||
return process.wait
|
||||
process.wait
|
||||
end
|
||||
def self.name ; "httpdirfs" ; end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,5 +36,12 @@ module GX::Models
|
|||
|
||||
@[YAML::Field(key: "filesystems")]
|
||||
getter filesystems : Array(AbstractFilesystemConfig)
|
||||
setter filesystems
|
||||
|
||||
def initialize(version = "1.0.0", global = GlobalConfig.new, filesystems = [] of AbstractFilesystemConfig)
|
||||
@version = version
|
||||
@global = global
|
||||
@filesystems = filesystems
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,6 +16,14 @@ module GX::Models
|
|||
|
||||
include Concerns::Base
|
||||
|
||||
def initialize(create_options)
|
||||
@name = create_options.name.as(String)
|
||||
@remote_user = create_options.remote_user.as(String)
|
||||
@remote_host = create_options.remote_host.as(String)
|
||||
@remote_path = create_options.remote_path.as(String)
|
||||
@remote_port = create_options.remote_port.as(String)
|
||||
end
|
||||
|
||||
def _mounted_prefix
|
||||
"#{@remote_user}@#{@remote_host}:#{@remote_path}"
|
||||
end
|
||||
|
@ -39,7 +47,8 @@ module GX::Models
|
|||
output: STDOUT,
|
||||
error: STDERR
|
||||
)
|
||||
return process.wait
|
||||
process.wait
|
||||
end
|
||||
def self.name ; "sshfs" ; end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
module GX::Parsers
|
||||
abstract class AbstractParser
|
||||
abstract def build(parser : OptionParser, ancestors : BreadCrumbs, config : Config)
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./base.cr"
|
||||
|
||||
module GX::Parsers
|
||||
|
@ -12,11 +17,11 @@ module GX::Parsers
|
|||
)
|
||||
parser.separator("\nCompletion commands:")
|
||||
|
||||
parser.on("--bash", "Generate bash completion") do |flag|
|
||||
parser.on("--bash", "Generate bash completion") do |_|
|
||||
Log.info { "Set bash completion" }
|
||||
end
|
||||
|
||||
parser.on("--zsh", "Generate zsh completion") do |flag|
|
||||
parser.on("--zsh", "Generate zsh completion") do |_|
|
||||
Log.info { "Set zsh completion" }
|
||||
end
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./options/config_options"
|
||||
require "./options/config_init_options"
|
||||
require "./base"
|
||||
|
@ -23,7 +28,7 @@ module GX::Parsers
|
|||
parser.banner = Utils.usage_line(breadcrumbs + "init", "Create initial mfm configuration")
|
||||
parser.separator("\nInit options")
|
||||
|
||||
parser.on("-p", "--path", "Set vault encrypted path") do |path|
|
||||
parser.on("-p", "--path PATH", "Set mapping encrypted path") do |path|
|
||||
config.config_init_options.try do |opts|
|
||||
opts.path = path
|
||||
end
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./base.cr"
|
||||
require "../utils/parser_lines"
|
||||
|
||||
|
@ -5,8 +10,9 @@ module GX::Parsers
|
|||
class MappingParser < AbstractParser
|
||||
def build(parser, ancestors, config)
|
||||
breadcrumbs = ancestors + "mapping"
|
||||
add_args = {name: "", path: ""}
|
||||
delete_args = {name: ""}
|
||||
mount_args = {name: ""}
|
||||
umount_args = {name: ""}
|
||||
|
||||
parser.banner = Utils.usage_line(
|
||||
breadcrumbs,
|
||||
|
@ -18,50 +24,118 @@ module GX::Parsers
|
|||
parser.on("list", "List mappings") do
|
||||
config.mode = Types::Mode::MappingList
|
||||
parser.separator(Utils.help_line(breadcrumbs + "list"))
|
||||
# abort("FIXME: Not implemented")
|
||||
end
|
||||
|
||||
parser.on("create", "Create mapping") do
|
||||
config.mode = Types::Mode::MappingCreate
|
||||
config.mode = Types::Mode::MappingCreate
|
||||
config.mapping_create_options = Parsers::Options::MappingCreateOptions.new
|
||||
|
||||
pp parser
|
||||
parser.banner = Utils.usage_line(breadcrumbs + "create", "Create mapping", true)
|
||||
parser.separator("\nCreate options")
|
||||
|
||||
parser.on("-n", "--name", "Set vault name") do |name|
|
||||
add_args = add_args.merge({name: name})
|
||||
parser.on("-t", "--type TYPE", "Set filesystem type") do |type|
|
||||
config.mapping_create_options.try do |opts|
|
||||
opts.type = type
|
||||
end
|
||||
end
|
||||
parser.on("-p", "--path", "Set vault encrypted path") do |path|
|
||||
add_args = add_args.merge({path: path})
|
||||
parser.on("-n", "--name NAME", "Set mapping name") do |name|
|
||||
config.mapping_create_options.try do |opts|
|
||||
opts.name = name
|
||||
end
|
||||
end
|
||||
|
||||
# Filesystem specific
|
||||
parser.on("--encrypted-path PATH", "Set encrypted path (for gocryptfs)") do |path|
|
||||
config.mapping_create_options.try do |opts|
|
||||
opts.encrypted_path = path
|
||||
end
|
||||
end
|
||||
parser.on("--remote-user USER", "Set SSH user (for sshfs)") do |user|
|
||||
config.mapping_create_options.try do |opts|
|
||||
opts.remote_user = user
|
||||
end
|
||||
end
|
||||
parser.on("--remote-host HOST", "Set SSH host (for sshfs)") do |host|
|
||||
config.mapping_create_options.try do |opts|
|
||||
opts.remote_host = host
|
||||
end
|
||||
end
|
||||
parser.on("--source-path PATH", "Set remote path (for sshfs)") do |path|
|
||||
config.mapping_create_options.try do |opts|
|
||||
opts.remote_path = path
|
||||
end
|
||||
end
|
||||
parser.on("--remote-port PORT", "Set SSH port (for sshfs)") do |port|
|
||||
config.mapping_create_options.try do |opts|
|
||||
opts.remote_port = port
|
||||
end
|
||||
end
|
||||
parser.on("--url URL", "Set URL (for httpdirfs)") do |url|
|
||||
config.mapping_create_options.try do |opts|
|
||||
opts.url = url
|
||||
end
|
||||
end
|
||||
|
||||
parser.separator(Utils.help_line(breadcrumbs + "create"))
|
||||
end
|
||||
|
||||
parser.on("edit", "Edit configuration") do |flag|
|
||||
parser.on("edit", "Edit configuration") do |_|
|
||||
config.mode = Types::Mode::MappingEdit
|
||||
|
||||
parser.on("--remote-user USER", "Set SSH user") do |user|
|
||||
config.mapping_create_options.try do |opts|
|
||||
opts.remote_user = user
|
||||
end
|
||||
end
|
||||
parser.on("--remote-host HOST", "Set SSH host") do |host|
|
||||
config.mapping_create_options.try do |opts|
|
||||
opts.remote_host = host
|
||||
end
|
||||
end
|
||||
parser.on("--source-path PATH", "Set remote path") do |path|
|
||||
config.mapping_create_options.try do |opts|
|
||||
opts.remote_path = path
|
||||
end
|
||||
end
|
||||
|
||||
parser.separator(Utils.help_line(breadcrumbs + "edit"))
|
||||
# abort("FIXME: Not implemented")
|
||||
end
|
||||
|
||||
parser.on("mount", "Mount mapping") do |flag|
|
||||
parser.on("mount", "Mount mapping") do |_|
|
||||
config.mode = Types::Mode::MappingMount
|
||||
|
||||
parser.banner = Utils.usage_line(breadcrumbs + "mount", "mount mapping", true)
|
||||
parser.separator("\nMount options")
|
||||
|
||||
parser.on("-n", "--name", "Set mapping name") do |name|
|
||||
mount_args = mount_args.merge({name: name})
|
||||
end
|
||||
|
||||
parser.separator(Utils.help_line(breadcrumbs + "mount"))
|
||||
# abort("FIXME: Not implemented")
|
||||
end
|
||||
|
||||
parser.on("umount", "Umount mapping") do |flag|
|
||||
parser.on("umount", "Umount mapping") do |_|
|
||||
config.mode = Types::Mode::MappingUmount
|
||||
|
||||
parser.banner = Utils.usage_line(breadcrumbs + "umount", "umount mapping", true)
|
||||
parser.separator("\nUmount options")
|
||||
|
||||
parser.on("-n", "--name", "Set mapping name") do |name|
|
||||
umount_args = umount_args.merge({name: name})
|
||||
end
|
||||
|
||||
parser.separator(Utils.help_line(breadcrumbs + "umount"))
|
||||
# abort("FIXME: Not implemented")
|
||||
end
|
||||
|
||||
parser.on("delete", "Delete mapping") do
|
||||
config.mode = Types::Mode::MappingDelete
|
||||
|
||||
parser.banner = Utils.usage_line(breadcrumbs + "delete", "Delete mapping", true)
|
||||
parser.separator("\nDelete options")
|
||||
parser.banner = Utils.usage_line(breadcrumbs + "delete", "delete mapping", true)
|
||||
parser.separator("\ndelete options")
|
||||
|
||||
parser.on("-n", "--name", "Set vault name") do |name|
|
||||
parser.on("-n", "--name", "Set mapping name") do |name|
|
||||
delete_args = delete_args.merge({name: name})
|
||||
end
|
||||
parser.separator(Utils.help_line(breadcrumbs + "delete"))
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "option_parser"
|
||||
|
||||
module GX::Parsers::Options
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "option_parser"
|
||||
|
||||
module GX::Parsers::Options
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "option_parser"
|
||||
|
||||
module GX::Parsers::Options
|
||||
|
|
19
src/parsers/options/mapping_create_options.cr
Normal file
19
src/parsers/options/mapping_create_options.cr
Normal file
|
@ -0,0 +1,19 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "option_parser"
|
||||
|
||||
module GX::Parsers::Options
|
||||
class MappingCreateOptions
|
||||
property type : String?
|
||||
property name : String?
|
||||
property encrypted_path : String?
|
||||
property remote_user : String?
|
||||
property remote_host : String?
|
||||
property remote_path : String?
|
||||
property remote_port : String?
|
||||
property url : String?
|
||||
end
|
||||
end
|
12
src/parsers/options/mapping_delete_options.cr
Normal file
12
src/parsers/options/mapping_delete_options.cr
Normal file
|
@ -0,0 +1,12 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "option_parser"
|
||||
|
||||
module GX::Parsers::Options
|
||||
class MappingDeleteOptions
|
||||
# Add your options here
|
||||
end
|
||||
end
|
12
src/parsers/options/mapping_mount_options.cr
Normal file
12
src/parsers/options/mapping_mount_options.cr
Normal file
|
@ -0,0 +1,12 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "option_parser"
|
||||
|
||||
module GX::Parsers::Options
|
||||
class MappingMountOptions
|
||||
# Add your options here
|
||||
end
|
||||
end
|
12
src/parsers/options/mapping_umount_options.cr
Normal file
12
src/parsers/options/mapping_umount_options.cr
Normal file
|
@ -0,0 +1,12 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "option_parser"
|
||||
|
||||
module GX::Parsers::Options
|
||||
class MappingUmountOptions
|
||||
# Add your options here
|
||||
end
|
||||
end
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./base"
|
||||
require "./config_parser"
|
||||
require "./mapping_parser"
|
||||
|
@ -21,24 +26,24 @@ module GX::Parsers
|
|||
config.path = path
|
||||
end
|
||||
|
||||
parser.on("-v", "--verbose", "Set more verbosity") do |flag|
|
||||
parser.on("-v", "--verbose", "Set more verbosity") do |_|
|
||||
Log.info { "Verbosity enabled" }
|
||||
config.verbose = true
|
||||
end
|
||||
|
||||
parser.on("-o", "--open", "Automatically open directory after mount") do |flag|
|
||||
parser.on("-o", "--open", "Automatically open directory after mount") do |_|
|
||||
Log.info { "Auto-open enabled" }
|
||||
config.auto_open = true
|
||||
end
|
||||
|
||||
parser.on("--version", "Show version") do |flag|
|
||||
parser.on("--version", "Show version") do |_|
|
||||
config.mode = Types::Mode::GlobalVersion
|
||||
end
|
||||
|
||||
parser.on("-h", "--help", "Show this help") do |flag|
|
||||
parser.on("-h", "--help", "Show this help") do |_|
|
||||
config.mode = Types::Mode::GlobalHelp
|
||||
config.help_options = Parsers::Options::HelpOptions.new
|
||||
config.help_options.try { |opts| opts.parser_snapshot = parser.dup }
|
||||
config.help_options.try(&.parser_snapshot=(parser.dup))
|
||||
end
|
||||
|
||||
parser.separator("\nGlobal commands:")
|
||||
|
@ -46,7 +51,7 @@ module GX::Parsers
|
|||
parser.on("config", "Manage configuration file") do
|
||||
config.mode = Types::Mode::GlobalHelp
|
||||
config.help_options = Parsers::Options::HelpOptions.new
|
||||
config.help_options.try { |opts| opts.parser_snapshot = parser.dup }
|
||||
config.help_options.try(&.parser_snapshot=(parser.dup))
|
||||
|
||||
# config.command = Commands::Config.new(config)
|
||||
Parsers::ConfigParser.new.build(parser, breadcrumbs, config)
|
||||
|
@ -59,15 +64,11 @@ module GX::Parsers
|
|||
parser.on("mapping", "Manage mappings") do
|
||||
config.mode = Types::Mode::GlobalHelp
|
||||
config.help_options = Parsers::Options::HelpOptions.new
|
||||
config.help_options.try { |opts| opts.parser_snapshot = parser.dup }
|
||||
config.help_options.try(&.parser_snapshot=(parser.dup))
|
||||
|
||||
Parsers::MappingParser.new.build(parser, breadcrumbs, config)
|
||||
end
|
||||
|
||||
# parser.on("interactive", "Interactive mapping mount/umount") do
|
||||
# abort("FIXME: Not implemented")
|
||||
# end
|
||||
|
||||
parser.on("completion", "Manage completion") do
|
||||
config.mode = Types::Mode::GlobalCompletion
|
||||
Parsers::CompletionParser.new.build(parser, breadcrumbs, config)
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
module GX::Types
|
||||
enum Mode
|
||||
None
|
||||
|
@ -7,7 +12,7 @@ module GX::Types
|
|||
GlobalCompletion
|
||||
GlobalTui
|
||||
GlobalConfig
|
||||
GlobalMapping
|
||||
# GlobalMapping
|
||||
|
||||
ConfigInit
|
||||
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
module GX::Utils
|
||||
class BreadCrumbs
|
||||
def initialize(base : Array(String))
|
||||
@ancestors = base
|
||||
end
|
||||
|
||||
def +(elem : String)
|
||||
b = BreadCrumbs.new(@ancestors + [elem])
|
||||
def +(other : String)
|
||||
BreadCrumbs.new(@ancestors + [other])
|
||||
end
|
||||
|
||||
def to_s
|
||||
@ancestors.join(" ")
|
||||
def to_s(io : IO)
|
||||
io << @ancestors.join(" ")
|
||||
end
|
||||
|
||||
def to_a
|
||||
|
|
|
@ -28,7 +28,8 @@ module GX::Utils
|
|||
exit(1)
|
||||
end
|
||||
|
||||
result = output.to_s.strip # .split.first?
|
||||
# result
|
||||
output.to_s.strip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "./breadcrumbs"
|
||||
|
||||
module GX::Utils
|
||||
def self.usage_line(breadcrumbs : BreadCrumbs, description : String, has_commands : Bool = false)
|
||||
[
|
||||
"Usage: #{breadcrumbs.to_s}#{has_commands ? " [commands]" : ""} [options]",
|
||||
"Usage: #{breadcrumbs}#{has_commands ? " [commands]" : ""} [options]",
|
||||
"",
|
||||
description,
|
||||
"",
|
||||
|
@ -12,6 +17,6 @@ module GX::Utils
|
|||
end
|
||||
|
||||
def self.help_line(breadcrumbs : BreadCrumbs)
|
||||
"\nRun '#{breadcrumbs.to_s} COMMAND --help' for more information on a command."
|
||||
"\nRun '#{breadcrumbs} COMMAND --help' for more information on a command."
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
require "version_from_shard"
|
||||
|
||||
module GX
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
# mfm Bash completion script
|
||||
|
||||
|
|
6
static/completion.zsh
Normal file
6
static/completion.zsh
Normal file
|
@ -0,0 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
# Copyright © 2024 Glenn Y. Rolland <glenux@glenux.net>
|
||||
|
||||
|
32
static/sample.mfm.yaml
Normal file
32
static/sample.mfm.yaml
Normal file
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
version: 1
|
||||
|
||||
global:
|
||||
mount_point_base: "{{env.HOME}}/mnt"
|
||||
|
||||
filesystems:
|
||||
##
|
||||
## Sample configuration for encrypted vault (gocryptfs)
|
||||
##
|
||||
# - type: gocryptfs
|
||||
# name: "Credential Vault"
|
||||
# encrypted_path: "{{env.HOME}}/Documents/Credential.Vault"
|
||||
#
|
||||
##
|
||||
## Sample configuration remote SSH directory (sshfs)
|
||||
##
|
||||
# - type: sshfs
|
||||
# name: "Remote SSH server"
|
||||
# remote_host: ssh.example.com
|
||||
# remote_user: "{{env.USER}}"
|
||||
# remote_path: "/home/{{env.USER}}"
|
||||
# remote_port: 443
|
||||
#
|
||||
##
|
||||
## Sample configuration for remote HTTP directory (httpdirfs)
|
||||
##
|
||||
- type: httpdirfs
|
||||
name: "Debian Repository"
|
||||
url: "http://ftp.debian.org/debian/"
|
||||
# mount_point: "{{env.HOME}}/another.dir"
|
||||
#
|
Loading…
Reference in a new issue