Compare commits

..

53 commits

Author SHA1 Message Date
bb7510704f refactor: split FileSystemManager choose_filesystem into simpler pieces
Some checks reported errors
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build was killed
2024-12-11 01:14:18 +01:00
7243935bf9 fix: tui should work again
Some checks reported errors
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build was killed
2024-12-11 00:52:51 +01:00
a3d827bdb0 fix: prepare for evolution of (u)mount management
Some checks reported errors
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build was killed
2024-12-11 00:41:04 +01:00
16b81ed038 feat: add initializer for root_config with version and filesystems parameters
Some checks failed
continuous-integration/drone/push Build is failing
2024-12-11 00:08:58 +01:00
f383694870 feat: add support for tracking inherited filesystem config subclasses 2024-12-11 00:08:56 +01:00
06e3d1895c refactor: Remove direct initialization of file_system_manager in commands 2024-12-11 00:08:56 +01:00
01804f1139 feat: add configuration saving functionality to MappingCreate command 2024-12-11 00:08:54 +01:00
4acbaf7d83 fix: cleanup code and comments 2024-12-10 23:54:52 +01:00
ec98b89206 fix: show progress during build 2024-12-10 23:53:30 +01:00
bc9aa7c0f9 feat: add tests for mapping commands (broken) 2024-10-27 22:09:47 +01:00
ad3af05032 refactor: remove hardcoded filesystem testing 2024-10-27 22:09:20 +01:00
ef4ca70eed fix: use ? suffit for boolean variables 2024-10-27 21:59:57 +01:00
60a7356f8b fix: add better handling of exceptions (parsing, arguments, options, ...) 2024-10-27 21:55:58 +01:00
8b0d64ad86 fix: remove useless assignation in fzf 2024-10-27 20:47:24 +01:00
995ab4d496 feat: add format target into makefile 2024-10-27 20:45:01 +01:00
8dc7e29758 fix: add missing SPDX headers to files 2024-10-27 20:42:57 +01:00
ced3168471 STASH
Some checks reported errors
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build was killed
2024-10-25 17:33:07 +02:00
6d15d331d8 fix: git must ignore .aider and .env 2024-10-25 17:31:29 +02:00
e27495eecf fix: update param in code_preloader 2024-10-25 17:30:45 +02:00
bfbc9bb301 fix: Add FIXME comment to verify filesystem validity in mapping_create command 2024-10-25 17:29:31 +02:00
d39c7117a4 refactor: Move mapping_create_options property to the correct position 2024-10-25 17:29:26 +02:00
da0cf858a4 refactor: Replace root_config initialization with config.root usage 2024-10-25 17:29:24 +02:00
44b5daf5c7 fix: add missing parameter to --path option 2024-10-13 15:06:01 +02:00
07f2275a41 feat: add new spec files 2024-10-13 15:03:33 +02:00
c541a2556d fix: breadcrumb spec for string conversion 2024-10-13 15:03:09 +02:00
9e68d3bf70 feat: add spec for commands/mapping
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-10-06 14:30:41 +02:00
c270df01e7 refactor(file_system_manager): extract fzf display as two functions
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2024-08-04 23:50:42 +02:00
c3217a75e3 feat: start adding support for mappings with encrypted paths
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2024-08-04 22:58:40 +02:00
df89dd3dfa feat: start implementing MappingCreate 2024-08-04 22:58:09 +02:00
65fe1bf0b9 refactor: remove useless code refering to interactive mode 2024-08-04 22:57:09 +02:00
68934c3be8 fix: disable useless GlobalMapping action 2024-08-04 22:56:01 +02:00
26510531e7 doc: add stupid comment for demo
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-12 11:53:49 +01:00
0e2ddde081 doc: improve build instructions
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-24 14:15:42 +01:00
6ec7ae0ec7 fix: implement config init
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-24 14:13:29 +01:00
92aaf5f0b5 fix: remove useless comment
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-24 11:08:16 +01:00
59ab4ce272 fix: change binary name 2024-01-24 01:25:44 +01:00
1f5a2f33ec fix: follow the crystal way for to_s
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2024-01-24 00:28:46 +01:00
cb99019be5 fix: add missing cli options for mapping command
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-01-24 00:20:28 +01:00
91f2e7a554 fix: update code_preloader ignore list
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2024-01-24 00:07:29 +01:00
bb5941a86a fix: remove useless FIXME comments
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2024-01-24 00:06:52 +01:00
1a5c2cd223 feat: prepare (empty) files for completion and sample config 2024-01-24 00:06:28 +01:00
be8980b74c fix: run ameba --fix on parser_lines.cr
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-01-23 23:48:29 +01:00
8f145189c0 fix: run ameba --fix on root_parser.cr 2024-01-23 23:48:29 +01:00
dbb0a42e91 fix: run ameba --fix on mapping_parser.cr 2024-01-23 23:48:29 +01:00
ed2cf5227f fix: run ameba --fix on completion_parser.cr 2024-01-23 23:48:29 +01:00
b59f1011ac fix: run ameba --fix on sshfs_config.cr 2024-01-23 23:48:29 +01:00
f5d28671a2 fix: run ameba --fix on httpdirfs_config.cr 2024-01-23 23:48:29 +01:00
531cba0dc7 fix: run ameba --fix on gocryptfs_config.cr 2024-01-23 23:48:28 +01:00
16bb660fc2 fix: run ameba --fix on base.cr 2024-01-23 23:48:28 +01:00
275f66d19d fix: run ameba --fix on file_system_manager.cr 2024-01-23 23:48:28 +01:00
8f1862eb43 fix: run ameba --fix on global_completion.cr 2024-01-23 23:48:28 +01:00
29ab85a61f fix: run ameba --fix on config_init.cr 2024-01-23 23:48:28 +01:00
aec45eebd4 fix: replace vault by mapping
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
As the software object has evolved along the way, and the handling of
vaults has become generic FUSE filesystems mapping management, we need
to correct all the places that still mention vaults.
2024-01-15 09:08:36 +01:00
63 changed files with 898 additions and 190 deletions

View file

@ -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
View file

@ -7,3 +7,5 @@
.vagrant
bin
lib
.aider*
.env

View file

@ -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

View file

@ -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
@ -180,5 +187,5 @@ By contributing, you agree to our code of conduct and license terms.
## License
GNU GPL-3
GNU GPL-3

5
Vagrantfile vendored
View file

@ -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 -*-

View file

@ -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

View file

@ -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:

View 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

View 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

View 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

View file

View 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

View file

View file

View 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

View file

@ -16,7 +16,7 @@ module GX
class Cli
Log = ::Log.for("cli")
@config : GX::Config
@config : GX::Config
def initialize
# Main execution starts here
@ -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

View file

@ -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

View file

@ -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/*"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
View 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

View file

@ -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

View file

@ -31,6 +31,6 @@ Log.setup do |config|
end
end
cli = GX::Cli.new
cli.parse_command_line(ARGV)
cli.run
app = GX::Cli.new
app.parse_command_line(ARGV)
app.run

View file

@ -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,

View file

@ -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

View 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -13,10 +13,17 @@ module GX::Models
getter remote_user : String = ""
getter remote_host : String = ""
getter remote_port : String = "22"
getter options : Array(String) = [] of String
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
@ -29,25 +36,19 @@ module GX::Models
mount_point_safe = @mount_point
raise InvalidMountpointError.new("Invalid mount point") if mount_point_safe.nil?
options = [] of String
# merge sshfs options
@options.each do |option|
options.push("-o", option)
end
options.push("-p", remote_port)
options.push(
"#{@remote_user}@#{@remote_host}:#{@remote_path}",
mount_point_safe
)
process = Process.new(
"sshfs",
options,
[
"-p", remote_port,
"#{@remote_user}@#{@remote_host}:#{@remote_path}",
mount_point_safe,
],
input: STDIN,
output: STDOUT,
error: STDERR
)
return process.wait
process.wait
end
def self.name ; "sshfs" ; end
end
end

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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"))

View file

@ -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

View file

@ -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

View file

@ -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

View 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

View 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

View 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

View 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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
View 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
View 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"
#