ukiryu 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/docs.yml +63 -0
- data/.github/workflows/links.yml +99 -0
- data/.github/workflows/rake.yml +19 -0
- data/.github/workflows/release.yml +27 -0
- data/.gitignore +18 -4
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +213 -0
- data/Gemfile +12 -8
- data/README.adoc +613 -0
- data/Rakefile +2 -2
- data/docs/assets/logo.svg +1 -0
- data/exe/ukiryu +11 -0
- data/lib/ukiryu/action/base.rb +77 -0
- data/lib/ukiryu/cache.rb +199 -0
- data/lib/ukiryu/cli.rb +133 -307
- data/lib/ukiryu/cli_commands/base_command.rb +155 -0
- data/lib/ukiryu/cli_commands/commands_command.rb +120 -0
- data/lib/ukiryu/cli_commands/commands_command.rb.fixed +40 -0
- data/lib/ukiryu/cli_commands/config_command.rb +249 -0
- data/lib/ukiryu/cli_commands/describe_command.rb +326 -0
- data/lib/ukiryu/cli_commands/describe_command.rb.fixed +254 -0
- data/lib/ukiryu/cli_commands/exec_inline_command.rb.fixed +180 -0
- data/lib/ukiryu/cli_commands/extract_command.rb +84 -0
- data/lib/ukiryu/cli_commands/info_command.rb +156 -0
- data/lib/ukiryu/cli_commands/list_command.rb +70 -0
- data/lib/ukiryu/cli_commands/opts_command.rb +106 -0
- data/lib/ukiryu/cli_commands/opts_command.rb.fixed +105 -0
- data/lib/ukiryu/cli_commands/response_formatter.rb +240 -0
- data/lib/ukiryu/cli_commands/run_command.rb +375 -0
- data/lib/ukiryu/cli_commands/run_file_command.rb +215 -0
- data/lib/ukiryu/cli_commands/system_command.rb +90 -0
- data/lib/ukiryu/cli_commands/validate_command.rb +87 -0
- data/lib/ukiryu/cli_commands/version_command.rb +16 -0
- data/lib/ukiryu/cli_commands/which_command.rb +166 -0
- data/lib/ukiryu/command_builder.rb +205 -0
- data/lib/ukiryu/config/env_provider.rb +64 -0
- data/lib/ukiryu/config/env_schema.rb +63 -0
- data/lib/ukiryu/config/override_resolver.rb +68 -0
- data/lib/ukiryu/config/type_converter.rb +59 -0
- data/lib/ukiryu/config.rb +249 -0
- data/lib/ukiryu/errors.rb +3 -0
- data/lib/ukiryu/executable_locator.rb +114 -0
- data/lib/ukiryu/execution/command_info.rb +64 -0
- data/lib/ukiryu/execution/metadata.rb +97 -0
- data/lib/ukiryu/execution/output.rb +144 -0
- data/lib/ukiryu/execution/result.rb +194 -0
- data/lib/ukiryu/execution.rb +15 -0
- data/lib/ukiryu/execution_context.rb +251 -0
- data/lib/ukiryu/executor.rb +76 -493
- data/lib/ukiryu/extractors/base_extractor.rb +63 -0
- data/lib/ukiryu/extractors/extractor.rb +150 -0
- data/lib/ukiryu/extractors/help_parser.rb +188 -0
- data/lib/ukiryu/extractors/native_extractor.rb +47 -0
- data/lib/ukiryu/io.rb +196 -0
- data/lib/ukiryu/logger.rb +544 -0
- data/lib/ukiryu/models/argument.rb +28 -0
- data/lib/ukiryu/models/argument_definition.rb +119 -0
- data/lib/ukiryu/models/arguments.rb +113 -0
- data/lib/ukiryu/models/command_definition.rb +176 -0
- data/lib/ukiryu/models/command_info.rb +37 -0
- data/lib/ukiryu/models/components.rb +107 -0
- data/lib/ukiryu/models/env_var_definition.rb +30 -0
- data/lib/ukiryu/models/error_response.rb +41 -0
- data/lib/ukiryu/models/execution_metadata.rb +31 -0
- data/lib/ukiryu/models/execution_report.rb +236 -0
- data/lib/ukiryu/models/exit_codes.rb +74 -0
- data/lib/ukiryu/models/flag_definition.rb +67 -0
- data/lib/ukiryu/models/option_definition.rb +102 -0
- data/lib/ukiryu/models/output_info.rb +25 -0
- data/lib/ukiryu/models/platform_profile.rb +153 -0
- data/lib/ukiryu/models/routing.rb +211 -0
- data/lib/ukiryu/models/search_paths.rb +39 -0
- data/lib/ukiryu/models/success_response.rb +85 -0
- data/lib/ukiryu/models/tool_definition.rb +145 -0
- data/lib/ukiryu/models/tool_metadata.rb +82 -0
- data/lib/ukiryu/models/validation_result.rb +80 -0
- data/lib/ukiryu/models/version_compatibility.rb +152 -0
- data/lib/ukiryu/models/version_detection.rb +39 -0
- data/lib/ukiryu/models.rb +23 -0
- data/lib/ukiryu/options/base.rb +95 -0
- data/lib/ukiryu/options_builder/formatter.rb +87 -0
- data/lib/ukiryu/options_builder/validator.rb +43 -0
- data/lib/ukiryu/options_builder.rb +311 -0
- data/lib/ukiryu/platform.rb +6 -6
- data/lib/ukiryu/registry.rb +143 -183
- data/lib/ukiryu/response/base.rb +217 -0
- data/lib/ukiryu/runtime.rb +179 -0
- data/lib/ukiryu/schema_validator.rb +8 -10
- data/lib/ukiryu/shell/bash.rb +3 -3
- data/lib/ukiryu/shell/cmd.rb +4 -4
- data/lib/ukiryu/shell/fish.rb +1 -1
- data/lib/ukiryu/shell/powershell.rb +3 -3
- data/lib/ukiryu/shell/sh.rb +1 -1
- data/lib/ukiryu/shell/zsh.rb +1 -1
- data/lib/ukiryu/shell.rb +146 -39
- data/lib/ukiryu/thor_ext.rb +208 -0
- data/lib/ukiryu/tool.rb +649 -258
- data/lib/ukiryu/tool_index.rb +224 -0
- data/lib/ukiryu/tools/base.rb +381 -0
- data/lib/ukiryu/tools/class_generator.rb +132 -0
- data/lib/ukiryu/tools/executable_finder.rb +29 -0
- data/lib/ukiryu/tools/generator.rb +154 -0
- data/lib/ukiryu/tools.rb +109 -0
- data/lib/ukiryu/type.rb +28 -43
- data/lib/ukiryu/validation/constraints.rb +281 -0
- data/lib/ukiryu/validation/validator.rb +188 -0
- data/lib/ukiryu/validation.rb +21 -0
- data/lib/ukiryu/version.rb +1 -1
- data/lib/ukiryu/version_detector.rb +51 -0
- data/lib/ukiryu.rb +31 -15
- data/ukiryu-proposal.md +2952 -0
- data/ukiryu.gemspec +18 -14
- metadata +137 -5
- data/.github/workflows/test.yml +0 -143
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ukiryu
|
|
4
|
+
# Version compatibility model
|
|
5
|
+
#
|
|
6
|
+
# Checks if an installed tool version is compatible with
|
|
7
|
+
# the version requirements specified in a tool profile.
|
|
8
|
+
class VersionCompatibility
|
|
9
|
+
attr_reader :installed_version, :required_version, :compatible, :reason
|
|
10
|
+
|
|
11
|
+
# Initialize version compatibility check
|
|
12
|
+
#
|
|
13
|
+
# @param installed_version [String] the installed tool version
|
|
14
|
+
# @param required_version [String] the version requirement (e.g., ">= 2.30")
|
|
15
|
+
# @param compatible [Boolean] whether the versions are compatible
|
|
16
|
+
# @param reason [String, nil] the reason for incompatibility
|
|
17
|
+
def initialize(installed_version:, required_version:, compatible:, reason: nil)
|
|
18
|
+
@installed_version = installed_version
|
|
19
|
+
@required_version = required_version
|
|
20
|
+
@compatible = compatible
|
|
21
|
+
@reason = reason
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Check if versions are compatible
|
|
25
|
+
#
|
|
26
|
+
# @return [Boolean] true if compatible
|
|
27
|
+
def compatible?
|
|
28
|
+
@compatible
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Check if versions are incompatible
|
|
32
|
+
#
|
|
33
|
+
# @return [Boolean] true if incompatible
|
|
34
|
+
def incompatible?
|
|
35
|
+
!@compatible
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Get a human-readable status message
|
|
39
|
+
#
|
|
40
|
+
# @return [String] the status message
|
|
41
|
+
def status_message
|
|
42
|
+
if @compatible
|
|
43
|
+
"Version #{@installed_version} is compatible with requirement #{@required_version}"
|
|
44
|
+
else
|
|
45
|
+
@reason || "Version #{@installed_version} is not compatible with requirement #{@required_version}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Check compatibility against a version requirement
|
|
50
|
+
#
|
|
51
|
+
# @param installed_version [String] the installed version
|
|
52
|
+
# @param requirement [String] the version requirement (e.g., ">= 2.30")
|
|
53
|
+
# @return [VersionCompatibility] the compatibility result
|
|
54
|
+
def self.check(installed_version, requirement)
|
|
55
|
+
return new(installed_version: installed_version, required_version: requirement, compatible: true, reason: nil) if !requirement || requirement.empty?
|
|
56
|
+
|
|
57
|
+
parser = RequirementParser.new(requirement)
|
|
58
|
+
compatible = parser.satisfied_by?(installed_version)
|
|
59
|
+
|
|
60
|
+
if compatible
|
|
61
|
+
new(installed_version: installed_version, required_version: requirement, compatible: true, reason: nil)
|
|
62
|
+
else
|
|
63
|
+
new(installed_version: installed_version, required_version: requirement, compatible: false,
|
|
64
|
+
reason: "Version #{installed_version} does not satisfy requirement: #{requirement}")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Requirement parser for semantic versioning
|
|
69
|
+
class RequirementParser
|
|
70
|
+
# Parse a version requirement
|
|
71
|
+
#
|
|
72
|
+
# @param requirement [String] the requirement string (e.g., ">= 2.30, < 3.0")
|
|
73
|
+
def initialize(requirement)
|
|
74
|
+
@requirement = requirement
|
|
75
|
+
@constraints = parse_requirements
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Check if a version satisfies the requirements
|
|
79
|
+
#
|
|
80
|
+
# @param version [String] the version to check
|
|
81
|
+
# @return [Boolean] true if satisfied
|
|
82
|
+
def satisfied_by?(version)
|
|
83
|
+
return true if @constraints.empty?
|
|
84
|
+
|
|
85
|
+
@constraints.all? { |constraint| satisfied?(version, constraint) }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
# Parse requirement string into constraint array
|
|
91
|
+
#
|
|
92
|
+
# @return [Array<Hash>] array of constraint hashes
|
|
93
|
+
def parse_requirements
|
|
94
|
+
@requirement.split(',').map(&:strip).map do |req|
|
|
95
|
+
if req =~ /^([><=!~]+)\s*(.+)/
|
|
96
|
+
{ operator: Regexp.last_match(1), version: Regexp.last_match(2) }
|
|
97
|
+
else
|
|
98
|
+
# Default to equality
|
|
99
|
+
{ operator: '==', version: req }
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Check if a version satisfies a single constraint
|
|
105
|
+
#
|
|
106
|
+
# @param version [String] the version to check
|
|
107
|
+
# @param constraint [Hash] the constraint hash
|
|
108
|
+
# @return [Boolean] true if satisfied
|
|
109
|
+
def satisfied?(version, constraint)
|
|
110
|
+
v = parse_version(version)
|
|
111
|
+
req_v = parse_version(constraint[:version])
|
|
112
|
+
|
|
113
|
+
case constraint[:operator]
|
|
114
|
+
when '>', '>='
|
|
115
|
+
compare_versions(v, req_v) > 0 || (constraint[:operator] == '>=' && v == req_v)
|
|
116
|
+
when '<', '<='
|
|
117
|
+
compare_versions(v, req_v) < 0 || (constraint[:operator] == '<=' && v == req_v)
|
|
118
|
+
when '==', '='
|
|
119
|
+
v == req_v
|
|
120
|
+
when '!='
|
|
121
|
+
v != req_v
|
|
122
|
+
when '~>'
|
|
123
|
+
# Optimistic operator (~> 2.5 means >= 2.5 and < 3.0)
|
|
124
|
+
compare_versions(v, req_v) >= 0 && (v[0] == req_v[0])
|
|
125
|
+
else
|
|
126
|
+
false
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Parse version string into array of integers
|
|
131
|
+
#
|
|
132
|
+
# @param version [String] the version string
|
|
133
|
+
# @return [Array<Integer>] the version components
|
|
134
|
+
def parse_version(version)
|
|
135
|
+
version.split('.').map(&:to_i)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Compare two version arrays
|
|
139
|
+
#
|
|
140
|
+
# @param v1 [Array<Integer>] first version
|
|
141
|
+
# @param v2 [Array<Integer>] second version
|
|
142
|
+
# @return [Integer] -1, 0, or 1
|
|
143
|
+
def compare_versions(v1, v2)
|
|
144
|
+
max_length = [v1.length, v2.length].max
|
|
145
|
+
v1_padded = v1 + [0] * (max_length - v1.length)
|
|
146
|
+
v2_padded = v2 + [0] * (max_length - v2.length)
|
|
147
|
+
|
|
148
|
+
v1_padded <=> v2_padded
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lutaml/model'
|
|
4
|
+
|
|
5
|
+
module Ukiryu
|
|
6
|
+
module Models
|
|
7
|
+
# Version detection configuration
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# vd = VersionDetection.new(
|
|
11
|
+
# command: '--version',
|
|
12
|
+
# pattern: '(\d+\.\d+)',
|
|
13
|
+
# modern_threshold: '7.0'
|
|
14
|
+
# )
|
|
15
|
+
class VersionDetection < Lutaml::Model::Serializable
|
|
16
|
+
attribute :command, :string, collection: true, default: []
|
|
17
|
+
attribute :pattern, :string
|
|
18
|
+
attribute :modern_threshold, :string
|
|
19
|
+
|
|
20
|
+
yaml do
|
|
21
|
+
map_element 'command', to: :command
|
|
22
|
+
map_element 'pattern', to: :pattern
|
|
23
|
+
map_element 'modern_threshold', to: :modern_threshold
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Hash-like access for Base.detect_version compatibility
|
|
27
|
+
#
|
|
28
|
+
# @param key [Symbol, String] the attribute key
|
|
29
|
+
# @return [Object] the attribute value
|
|
30
|
+
def [](key)
|
|
31
|
+
key_sym = key.to_sym
|
|
32
|
+
# Return nil for unknown keys
|
|
33
|
+
return nil unless respond_to?(key_sym, true)
|
|
34
|
+
|
|
35
|
+
send(key_sym)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'models/tool_metadata'
|
|
4
|
+
require_relative 'models/tool_definition'
|
|
5
|
+
require_relative 'models/platform_profile'
|
|
6
|
+
require_relative 'models/command_definition'
|
|
7
|
+
require_relative 'models/option_definition'
|
|
8
|
+
require_relative 'models/flag_definition'
|
|
9
|
+
require_relative 'models/argument_definition'
|
|
10
|
+
require_relative 'models/version_detection'
|
|
11
|
+
require_relative 'models/search_paths'
|
|
12
|
+
require_relative 'models/execution_report'
|
|
13
|
+
require_relative 'models/version_compatibility'
|
|
14
|
+
require_relative 'models/exit_codes'
|
|
15
|
+
require_relative 'models/components'
|
|
16
|
+
|
|
17
|
+
module Ukiryu
|
|
18
|
+
module Models
|
|
19
|
+
# Models namespace for tool profile model classes
|
|
20
|
+
#
|
|
21
|
+
# These models use lutaml-model for proper YAML serialization/deserialization
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ukiryu
|
|
4
|
+
module Options
|
|
5
|
+
# Abstract base class for all option classes
|
|
6
|
+
#
|
|
7
|
+
# This class provides common functionality for all dynamically generated
|
|
8
|
+
# option classes, including batch setting via .set() and execution via .run().
|
|
9
|
+
#
|
|
10
|
+
# @abstract
|
|
11
|
+
class Base
|
|
12
|
+
# Set multiple options at once
|
|
13
|
+
#
|
|
14
|
+
# This method allows batch assignment of options. Each key in the params
|
|
15
|
+
# hash should correspond to an attribute name.
|
|
16
|
+
#
|
|
17
|
+
# @param params [Hash] hash of option names to values
|
|
18
|
+
# @return [self] returns self for method chaining
|
|
19
|
+
#
|
|
20
|
+
# @example Batch setting options
|
|
21
|
+
# options.set(inputs: ["image.png"], resize: "50%")
|
|
22
|
+
# options.output = "output.jpg"
|
|
23
|
+
def set(params)
|
|
24
|
+
params.each do |key, value|
|
|
25
|
+
setter = "#{key}="
|
|
26
|
+
send(setter, value) if respond_to?(setter)
|
|
27
|
+
end
|
|
28
|
+
self
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Validate the options
|
|
32
|
+
#
|
|
33
|
+
# Performs comprehensive validation using constraint-based validation.
|
|
34
|
+
# This includes:
|
|
35
|
+
# - Type checking using TypeConstraint
|
|
36
|
+
# - Required argument checking using RequiredConstraint
|
|
37
|
+
# - Value constraints (min, max, range) using RangeConstraint
|
|
38
|
+
# - Enum value validation using EnumConstraint
|
|
39
|
+
# - Option dependencies using DependencyConstraint
|
|
40
|
+
#
|
|
41
|
+
# @return [Boolean] true if valid
|
|
42
|
+
# @raise [Ukiryu::ValidationError] if validation fails
|
|
43
|
+
def validate!
|
|
44
|
+
command_def = self.class.command_def
|
|
45
|
+
return true unless command_def
|
|
46
|
+
|
|
47
|
+
validator = Validation::Validator.new(self, command_def)
|
|
48
|
+
validator.validate!
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Check if options are valid without raising errors
|
|
52
|
+
#
|
|
53
|
+
# @return [Boolean] true if valid, false otherwise
|
|
54
|
+
def valid?
|
|
55
|
+
validate!
|
|
56
|
+
true
|
|
57
|
+
rescue Ukiryu::ValidationError
|
|
58
|
+
false
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Get validation errors without raising exceptions
|
|
62
|
+
#
|
|
63
|
+
# @return [Array<String>] list of error messages
|
|
64
|
+
def validation_errors
|
|
65
|
+
command_def = self.class.command_def
|
|
66
|
+
return [] unless command_def
|
|
67
|
+
|
|
68
|
+
validator = Validation::Validator.new(self, command_def)
|
|
69
|
+
validator.errors
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Convert options to shell command string
|
|
73
|
+
#
|
|
74
|
+
# @param shell_type [Symbol] the shell type (:bash, :zsh, :fish, :powershell, etc.)
|
|
75
|
+
# @return [String] the formatted shell command (without executable)
|
|
76
|
+
def to_shell(shell_type: :bash)
|
|
77
|
+
raise NotImplementedError, 'Subclasses must implement to_shell'
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Get the command definition
|
|
81
|
+
#
|
|
82
|
+
# @return [Hash, nil] the command definition from the profile
|
|
83
|
+
def command_def
|
|
84
|
+
self.class.command_def
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Get the command name
|
|
88
|
+
#
|
|
89
|
+
# @return [Symbol] the command name
|
|
90
|
+
def command_name
|
|
91
|
+
self.class.command_name
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../shell'
|
|
4
|
+
|
|
5
|
+
module Ukiryu
|
|
6
|
+
module OptionsBuilder
|
|
7
|
+
# Formatting utilities for options, flags, and arguments
|
|
8
|
+
#
|
|
9
|
+
# This module handles the conversion of option values into shell command
|
|
10
|
+
# arguments according to various format styles.
|
|
11
|
+
module Formatter
|
|
12
|
+
# Format an option according to its definition
|
|
13
|
+
#
|
|
14
|
+
# @param opt_def [OptionDefinition] the option definition
|
|
15
|
+
# @param value [Object] the value to format
|
|
16
|
+
# @param shell_instance [Shell::Base] the shell instance
|
|
17
|
+
# @return [String, Array<String>] the formatted option(s)
|
|
18
|
+
def self.format_option(opt_def, value, _shell_instance)
|
|
19
|
+
type = opt_def.type
|
|
20
|
+
|
|
21
|
+
# Handle boolean types - just return the CLI flag
|
|
22
|
+
if [:boolean, TrueClass, 'boolean'].include?(type)
|
|
23
|
+
return nil if value.nil? || value == false
|
|
24
|
+
|
|
25
|
+
return opt_def.cli || ''
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
cli = opt_def.cli || ''
|
|
29
|
+
format = opt_def.format || 'double_dash_equals'
|
|
30
|
+
format_sym = format.is_a?(String) ? format.to_sym : format
|
|
31
|
+
|
|
32
|
+
value_str = value.is_a?(Symbol) ? value.to_s : value.to_s
|
|
33
|
+
|
|
34
|
+
# Handle array values
|
|
35
|
+
if value.is_a?(Array) && opt_def.separator
|
|
36
|
+
joined = value.join(opt_def.separator)
|
|
37
|
+
return case format_sym
|
|
38
|
+
when :double_dash_equals
|
|
39
|
+
"#{cli}#{joined}"
|
|
40
|
+
when :double_dash_space, :single_dash_space
|
|
41
|
+
[cli, joined]
|
|
42
|
+
else
|
|
43
|
+
"#{cli}#{joined}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
case format_sym
|
|
48
|
+
when :double_dash_equals
|
|
49
|
+
"#{cli}=#{value_str}"
|
|
50
|
+
when :double_dash_space, :single_dash_space
|
|
51
|
+
[cli, value_str]
|
|
52
|
+
when :single_dash_equals
|
|
53
|
+
"#{cli}=#{value_str}"
|
|
54
|
+
when :slash_colon
|
|
55
|
+
"#{cli}:#{value_str}"
|
|
56
|
+
when :slash_space
|
|
57
|
+
"#{cli} #{value_str}"
|
|
58
|
+
else
|
|
59
|
+
"#{cli}=#{value_str}"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Format a flag according to its definition
|
|
64
|
+
#
|
|
65
|
+
# @param flag_def [FlagDefinition] the flag definition
|
|
66
|
+
# @param shell_instance [Shell::Base] the shell instance
|
|
67
|
+
# @return [String, nil] the formatted flag
|
|
68
|
+
def self.format_flag(flag_def, _shell_instance)
|
|
69
|
+
flag_def.cli || ''
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Format an argument value
|
|
73
|
+
#
|
|
74
|
+
# @param value [Object] the value to format
|
|
75
|
+
# @param arg_def [ArgumentDefinition] the argument definition
|
|
76
|
+
# @param shell_instance [Shell::Base] the shell instance
|
|
77
|
+
# @return [String] the formatted argument
|
|
78
|
+
def self.format_arg(value, arg_def, shell_instance)
|
|
79
|
+
if arg_def.type == :file
|
|
80
|
+
shell_instance.format_path(value.to_s)
|
|
81
|
+
else
|
|
82
|
+
value.to_s
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../errors'
|
|
4
|
+
|
|
5
|
+
module Ukiryu
|
|
6
|
+
module OptionsBuilder
|
|
7
|
+
# Validation utilities for options classes
|
|
8
|
+
#
|
|
9
|
+
# This module provides validation methods for dynamically generated
|
|
10
|
+
# options classes, ensuring required arguments are present.
|
|
11
|
+
module Validator
|
|
12
|
+
# Define validation method on an options class
|
|
13
|
+
#
|
|
14
|
+
# @param klass [Class] the class to define the method on
|
|
15
|
+
# @param command_def [CommandDefinition] the command definition
|
|
16
|
+
def self.define_validation_method(klass, command_def)
|
|
17
|
+
klass.define_method(:validate!) do
|
|
18
|
+
errors = []
|
|
19
|
+
|
|
20
|
+
# Check required arguments
|
|
21
|
+
(command_def.arguments || []).each do |arg_def|
|
|
22
|
+
attr_name = arg_def.name
|
|
23
|
+
value = instance_variable_get("@#{attr_name}")
|
|
24
|
+
|
|
25
|
+
# Check if required (no min specified or min > 0)
|
|
26
|
+
min = arg_def.min || (arg_def.variadic ? 1 : 1)
|
|
27
|
+
errors << "Missing required argument: #{attr_name}" if min.positive? && (value.nil? || (value.is_a?(Array) && value.empty?))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
raise ValidationError, errors.join(', ') if errors.any?
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Validation error for options
|
|
35
|
+
#
|
|
36
|
+
class ValidationError < Error
|
|
37
|
+
def initialize(messages)
|
|
38
|
+
super("Validation failed: #{messages.join(', ')}")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|