fluent-tools 0.2.0
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 +7 -0
- data/exe/fluent-tools +6 -0
- data/ext/fluent_tools/extconf.rb +40 -0
- data/installer.rb +212 -0
- data/lib/fluent_tools/cli.rb +96 -0
- data/lib/fluent_tools/command_executor.rb +127 -0
- data/lib/fluent_tools/utils.rb +103 -0
- data/lib/fluent_tools/version.rb +5 -0
- data/lib/fluent_tools.rb +33 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 807b6f8dbd3ccbbb76cbb8420d6093b250e88608a8a90c8fb0eda55f87bbf22c
|
4
|
+
data.tar.gz: 0533e6d8cfd25c021486c0b56931843347adb96fdb6b8ae3ba28021db8596547
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4a538a4801bfab70efc3635c2f6bc492ff866cac46222291b59fa8893fbfeaef225dc60e64e5503c556d41d12ef1c2e890117704e480b0b69cb971b6df28011a
|
7
|
+
data.tar.gz: bafabd8faabf050e41ce921ea69e9c6a5d3e950c0006ef3c50a1cb1c78112cac68d1ae4f3829c3bf380309ffbf114bdf3e6da10f542780dd1be9bfbbe829db0f
|
data/exe/fluent-tools
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../installer'
|
4
|
+
require_relative '../../lib/fluent_tools/version'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
puts "Installing fluent-tools #{FluentTools::VERSION}..."
|
8
|
+
|
9
|
+
# Create installer instance for gem context with version
|
10
|
+
installer = FluentToolsInstaller.new(version: FluentTools::VERSION)
|
11
|
+
|
12
|
+
# Run installation
|
13
|
+
success = installer.install!
|
14
|
+
|
15
|
+
# Ensure the bin directory exists and is included in the gem
|
16
|
+
bin_dir = File.expand_path('../../bin', __dir__)
|
17
|
+
FileUtils.mkdir_p(bin_dir)
|
18
|
+
|
19
|
+
# Create dummy Makefile for RubyGems compatibility
|
20
|
+
makefile_content = <<~MAKEFILE
|
21
|
+
all:
|
22
|
+
\t@echo 'Binary already installed'
|
23
|
+
|
24
|
+
install:
|
25
|
+
\t@echo 'Binary already installed'
|
26
|
+
|
27
|
+
clean:
|
28
|
+
\t@echo 'Nothing to clean'
|
29
|
+
MAKEFILE
|
30
|
+
|
31
|
+
File.write('Makefile', makefile_content)
|
32
|
+
|
33
|
+
unless success
|
34
|
+
puts '❌ Installation failed - binary could not be installed'
|
35
|
+
puts 'This may still work if the binary becomes available at runtime'
|
36
|
+
puts 'or if you build it manually using: rake build_rust'
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
|
40
|
+
puts '✅ Extension configuration complete'
|
data/installer.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Installer for fluent-rust-tools gem
|
5
|
+
# Used by extconf.rb during gem installation
|
6
|
+
|
7
|
+
require 'net/http'
|
8
|
+
require 'uri'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'rbconfig'
|
11
|
+
require 'json'
|
12
|
+
require 'pathname'
|
13
|
+
require_relative 'lib/fluent_tools/utils'
|
14
|
+
|
15
|
+
# Installer for fluent-rust-tools gem
|
16
|
+
# Downloads pre-built binaries or builds from source
|
17
|
+
class FluentToolsInstaller
|
18
|
+
include FluentTools::Utils::Logger
|
19
|
+
|
20
|
+
def initialize(version: nil)
|
21
|
+
@version = version
|
22
|
+
end
|
23
|
+
|
24
|
+
# Main installation method
|
25
|
+
def install!
|
26
|
+
log_info "🚀 Installing #{FluentTools::Utils::BINARY_NAME}#{" `#{@version}`" if @version}..."
|
27
|
+
|
28
|
+
platform = FluentTools::Utils.detect_platform
|
29
|
+
if platform
|
30
|
+
log_info "🔍 Detected platform: #{platform}"
|
31
|
+
|
32
|
+
if download_prebuilt_binary(platform)
|
33
|
+
log_success 'Installation complete using pre-built binary!'
|
34
|
+
true
|
35
|
+
else
|
36
|
+
log_warning 'Pre-built binary not available, falling back to compilation...'
|
37
|
+
build_binary_from_source
|
38
|
+
end
|
39
|
+
else
|
40
|
+
log_warning "Platform #{RUBY_PLATFORM} not supported for pre-built binaries"
|
41
|
+
build_binary_from_source
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def download_prebuilt_binary(platform)
|
48
|
+
log_info "🔍 Attempting to download pre-built binary for #{platform}..."
|
49
|
+
|
50
|
+
begin
|
51
|
+
release_data = @version ? fetch_release_by_version(@version) : fetch_latest_release
|
52
|
+
download_url = find_download_url(release_data, platform)
|
53
|
+
|
54
|
+
return false unless download_url
|
55
|
+
|
56
|
+
log_info "📦 Downloading from #{download_url}"
|
57
|
+
binary_data = download_file(download_url)
|
58
|
+
|
59
|
+
install_binary(binary_data, platform)
|
60
|
+
log_success 'Pre-built binary downloaded successfully!'
|
61
|
+
true
|
62
|
+
rescue StandardError => e
|
63
|
+
if @version
|
64
|
+
log_error "Failed to download pre-built binary for version #{@version}: #{e.message}"
|
65
|
+
else
|
66
|
+
log_error "Failed to download pre-built binary: #{e.message}"
|
67
|
+
end
|
68
|
+
false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def fetch_latest_release
|
73
|
+
api_url = "https://api.github.com/repos/#{FluentTools::Utils::REPO_OWNER}/#{FluentTools::Utils::REPO_NAME}/releases/latest"
|
74
|
+
uri = URI(api_url)
|
75
|
+
response = Net::HTTP.get_response(uri)
|
76
|
+
|
77
|
+
raise "Could not fetch release information (HTTP #{response.code})" unless response.code == '200'
|
78
|
+
|
79
|
+
JSON.parse(response.body)
|
80
|
+
end
|
81
|
+
|
82
|
+
def fetch_release_by_version(version)
|
83
|
+
api_url = "https://api.github.com/repos/#{FluentTools::Utils::REPO_OWNER}/#{FluentTools::Utils::REPO_NAME}/releases/tags/#{version}"
|
84
|
+
uri = URI(api_url)
|
85
|
+
response = Net::HTTP.get_response(uri)
|
86
|
+
|
87
|
+
case response.code
|
88
|
+
when '200'
|
89
|
+
JSON.parse(response.body)
|
90
|
+
when '404'
|
91
|
+
raise "Release #{version} not found. Available releases can be viewed at: https://github.com/#{FluentTools::Utils::REPO_OWNER}/#{FluentTools::Utils::REPO_NAME}/releases"
|
92
|
+
else
|
93
|
+
raise "Could not fetch release #{version} (HTTP #{response.code})"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def find_download_url(release_data, platform)
|
98
|
+
binary_name = FluentTools::Utils.binary_name_for_platform(platform)
|
99
|
+
|
100
|
+
asset = release_data['assets'].find { |a| a['name'] == binary_name }
|
101
|
+
|
102
|
+
unless asset
|
103
|
+
log_error "No pre-built binary found for #{platform}"
|
104
|
+
return nil
|
105
|
+
end
|
106
|
+
|
107
|
+
asset['browser_download_url']
|
108
|
+
end
|
109
|
+
|
110
|
+
def download_file(url)
|
111
|
+
uri = URI(url)
|
112
|
+
|
113
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
114
|
+
request = Net::HTTP::Get.new(uri)
|
115
|
+
response = http.request(request)
|
116
|
+
|
117
|
+
# Follow redirects (GitHub releases use them)
|
118
|
+
case response
|
119
|
+
when Net::HTTPRedirection
|
120
|
+
download_file(response['location'])
|
121
|
+
when Net::HTTPSuccess
|
122
|
+
response.body
|
123
|
+
else
|
124
|
+
raise "Download failed: HTTP #{response.code} - #{response.message}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def install_binary(binary_data, platform)
|
130
|
+
install_dir = determine_install_dir
|
131
|
+
FileUtils.mkdir_p(install_dir)
|
132
|
+
|
133
|
+
binary_extension = FluentTools::Utils.binary_extension(platform)
|
134
|
+
binary_path = File.join(install_dir, "#{FluentTools::Utils::BINARY_NAME}#{binary_extension}")
|
135
|
+
|
136
|
+
log_info "📁 Installing binary to: #{binary_path}"
|
137
|
+
log_info "📏 Binary data size: #{binary_data.length} bytes"
|
138
|
+
|
139
|
+
raise 'Downloaded binary data is empty' if binary_data.empty?
|
140
|
+
|
141
|
+
File.binwrite(binary_path, binary_data)
|
142
|
+
|
143
|
+
# Make executable on Unix-like systems
|
144
|
+
File.chmod(0o755, binary_path) unless FluentTools::Utils.windows_platform?(platform)
|
145
|
+
|
146
|
+
log_info "✅ Binary installed successfully (#{File.size(binary_path)} bytes)"
|
147
|
+
@binary_path = binary_path
|
148
|
+
end
|
149
|
+
|
150
|
+
# rubocop:disable Naming/PredicateMethod
|
151
|
+
def build_binary_from_source
|
152
|
+
log_info '🔨 Building from source...'
|
153
|
+
|
154
|
+
# Find project root (where Cargo.toml and Makefile are)
|
155
|
+
project_root = find_project_root
|
156
|
+
unless project_root
|
157
|
+
log_error 'Could not find project root (Cargo.toml)'
|
158
|
+
return false
|
159
|
+
end
|
160
|
+
|
161
|
+
# Use Makefile to build the binary
|
162
|
+
Dir.chdir(project_root) do
|
163
|
+
unless system('make build_native')
|
164
|
+
log_error 'Failed to build Rust binary using Makefile'
|
165
|
+
return false
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Copy binary to install location
|
170
|
+
copy_built_binary(project_root)
|
171
|
+
log_success 'Binary built and installed successfully!'
|
172
|
+
|
173
|
+
true
|
174
|
+
end
|
175
|
+
# rubocop:enable Naming/PredicateMethod
|
176
|
+
|
177
|
+
def find_project_root
|
178
|
+
Pathname.pwd.ascend do |dir|
|
179
|
+
return dir.to_s if File.exist?(File.join(dir, 'Cargo.toml'))
|
180
|
+
end
|
181
|
+
nil
|
182
|
+
end
|
183
|
+
|
184
|
+
def copy_built_binary(project_root)
|
185
|
+
install_dir = determine_install_dir
|
186
|
+
FileUtils.mkdir_p(install_dir)
|
187
|
+
|
188
|
+
binary_extension = FluentTools::Utils.binary_extension
|
189
|
+
source_binary = File.join(project_root, 'target', 'release', "#{FluentTools::Utils::BINARY_NAME}#{binary_extension}")
|
190
|
+
dest_binary = File.join(install_dir, "#{FluentTools::Utils::BINARY_NAME}#{binary_extension}")
|
191
|
+
|
192
|
+
raise "Built binary not found at #{source_binary}" unless File.exist?(source_binary)
|
193
|
+
|
194
|
+
FileUtils.cp(source_binary, dest_binary)
|
195
|
+
@binary_path = dest_binary
|
196
|
+
end
|
197
|
+
|
198
|
+
def determine_install_dir
|
199
|
+
# First check if we're in the development context (has Cargo.toml in parent dirs)
|
200
|
+
project_root = find_project_root
|
201
|
+
if project_root
|
202
|
+
# Development context - put in ruby/bin relative to project root
|
203
|
+
File.join(project_root, 'ruby', 'bin')
|
204
|
+
else
|
205
|
+
# We're in a gem installation context - create bin directory in current gem location
|
206
|
+
current_dir = File.expand_path('..', __dir__)
|
207
|
+
bin_dir = File.join(current_dir, 'bin')
|
208
|
+
FileUtils.mkdir_p(bin_dir)
|
209
|
+
bin_dir
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
module FluentTools
|
6
|
+
# CLI commands for Android XML conversion
|
7
|
+
class AndroidCLI < Thor
|
8
|
+
desc 'from_fluent INPUT OUTPUT', 'Convert Fluent file to Android XML strings'
|
9
|
+
long_desc <<~DESC
|
10
|
+
Convert a Fluent localization file to Android XML string resources.
|
11
|
+
|
12
|
+
INPUT: Path to the input Fluent (.ftl) file
|
13
|
+
OUTPUT: Path to the output Android XML file
|
14
|
+
DESC
|
15
|
+
def from_fluent(input, output)
|
16
|
+
command_executor = CommandExecutor.new
|
17
|
+
command_executor.fluent_to_android(input, output)
|
18
|
+
puts "Successfully converted #{input} to #{output}"
|
19
|
+
rescue Error => e
|
20
|
+
puts "Error: #{e.message}"
|
21
|
+
exit 1
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'to_fluent INPUT OUTPUT', 'Convert Android XML strings to Fluent file'
|
25
|
+
long_desc <<~DESC
|
26
|
+
Convert Android XML string resources to a Fluent localization file.
|
27
|
+
|
28
|
+
INPUT: Path to the input Android XML file
|
29
|
+
OUTPUT: Path to the output Fluent (.ftl) file
|
30
|
+
DESC
|
31
|
+
option :original_fluent, aliases: '-o', desc: 'Original Fluent file for variable mapping recovery'
|
32
|
+
def to_fluent(input, output)
|
33
|
+
command_executor = CommandExecutor.new
|
34
|
+
command_executor.android_to_fluent(input, output, original_fluent: options[:original_fluent])
|
35
|
+
|
36
|
+
message = "Successfully converted #{input} to #{output}"
|
37
|
+
message += " using original Fluent file #{options[:original_fluent]}" if options[:original_fluent]
|
38
|
+
puts message
|
39
|
+
rescue Error => e
|
40
|
+
puts "Error: #{e.message}"
|
41
|
+
exit 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# CLI commands for PO (GNU gettext) conversion
|
46
|
+
class PoCLI < Thor
|
47
|
+
desc 'from_fluent INPUT OUTPUT', 'Convert Fluent file to PO format'
|
48
|
+
long_desc <<~DESC
|
49
|
+
Convert a Fluent localization file to GNU gettext PO format.
|
50
|
+
|
51
|
+
INPUT: Path to the input Fluent (.ftl) file
|
52
|
+
OUTPUT: Path to the output PO file
|
53
|
+
DESC
|
54
|
+
option :locale, aliases: '-l', default: 'en-US', desc: 'Source locale (e.g., en-US)'
|
55
|
+
def from_fluent(input, output)
|
56
|
+
command_executor = CommandExecutor.new
|
57
|
+
command_executor.fluent_to_po(input, output, locale: options[:locale])
|
58
|
+
puts "Successfully converted #{input} to #{output}"
|
59
|
+
rescue Error => e
|
60
|
+
puts "Error: #{e.message}"
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
|
64
|
+
desc 'to_fluent INPUT OUTPUT', 'Convert PO file to Fluent format'
|
65
|
+
long_desc <<~DESC
|
66
|
+
Convert a GNU gettext PO file to Fluent localization format.
|
67
|
+
|
68
|
+
INPUT: Path to the input PO file
|
69
|
+
OUTPUT: Path to the output Fluent (.ftl) file
|
70
|
+
DESC
|
71
|
+
def to_fluent(input, output)
|
72
|
+
command_executor = CommandExecutor.new
|
73
|
+
command_executor.po_to_fluent(input, output)
|
74
|
+
puts "Successfully converted #{input} to #{output}"
|
75
|
+
rescue Error => e
|
76
|
+
puts "Error: #{e.message}"
|
77
|
+
exit 1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Main CLI interface with subcommands for different formats
|
82
|
+
class CLI < Thor
|
83
|
+
# Android conversion commands
|
84
|
+
desc 'android SUBCOMMAND', 'Android XML conversion commands'
|
85
|
+
subcommand 'android', AndroidCLI
|
86
|
+
|
87
|
+
# PO conversion commands
|
88
|
+
desc 'po SUBCOMMAND', 'PO (GNU gettext) conversion commands'
|
89
|
+
subcommand 'po', PoCLI
|
90
|
+
|
91
|
+
desc 'version', 'Show version'
|
92
|
+
def version
|
93
|
+
puts "#{FluentTools::Utils::BINARY_NAME} #{VERSION}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'pathname'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module FluentTools
|
8
|
+
# Core command executor class that interfaces with the fluent-tools Rust binary
|
9
|
+
# Handles file validation, output directory creation, and command execution
|
10
|
+
class CommandExecutor
|
11
|
+
def initialize
|
12
|
+
@binary_path = find_binary_path
|
13
|
+
end
|
14
|
+
|
15
|
+
# Convert Fluent file to Android XML
|
16
|
+
def fluent_to_android(input_path, output_path)
|
17
|
+
validate_input_file!(input_path)
|
18
|
+
ensure_output_directory(output_path)
|
19
|
+
|
20
|
+
cmd = [@binary_path, 'android', 'from-fluent', '-i', input_path, '-o', output_path]
|
21
|
+
execute_command(cmd)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Convert Android XML to Fluent file
|
25
|
+
def android_to_fluent(input_path, output_path, original_fluent: nil)
|
26
|
+
validate_input_file!(input_path)
|
27
|
+
ensure_output_directory(output_path)
|
28
|
+
|
29
|
+
cmd = [@binary_path, 'android', 'to-fluent', '-i', input_path, '-o', output_path]
|
30
|
+
cmd += ['--original-fluent', original_fluent] if original_fluent
|
31
|
+
|
32
|
+
execute_command(cmd)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Convert Fluent file to PO
|
36
|
+
def fluent_to_po(input_path, output_path, locale: 'en-US')
|
37
|
+
validate_input_file!(input_path)
|
38
|
+
ensure_output_directory(output_path)
|
39
|
+
|
40
|
+
cmd = [@binary_path, 'po', 'from-fluent', '-i', input_path, '-o', output_path, '-l', locale]
|
41
|
+
execute_command(cmd)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Convert PO file to Fluent
|
45
|
+
def po_to_fluent(input_path, output_path)
|
46
|
+
validate_input_file!(input_path)
|
47
|
+
ensure_output_directory(output_path)
|
48
|
+
|
49
|
+
cmd = [@binary_path, 'po', 'to-fluent', '-i', input_path, '-o', output_path]
|
50
|
+
execute_command(cmd)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def find_binary_path
|
56
|
+
binary_name = FluentTools::Utils::BINARY_NAME
|
57
|
+
|
58
|
+
# 1. Standard gem installation (works for all gem contexts)
|
59
|
+
gem_binary = File.join(__dir__, '..', '..', 'bin', binary_name)
|
60
|
+
return gem_binary if File.executable?(gem_binary)
|
61
|
+
|
62
|
+
# 2. Development context
|
63
|
+
if development_context?
|
64
|
+
dev_binary = File.join(project_root, 'target', 'release', binary_name)
|
65
|
+
return dev_binary if File.executable?(dev_binary)
|
66
|
+
|
67
|
+
ruby_binary = File.join(project_root, 'ruby', 'bin', binary_name)
|
68
|
+
return ruby_binary if File.executable?(ruby_binary)
|
69
|
+
end
|
70
|
+
|
71
|
+
# 3. System PATH
|
72
|
+
system_binary = `which #{binary_name} 2>/dev/null`.strip
|
73
|
+
return system_binary unless system_binary.empty?
|
74
|
+
|
75
|
+
# If nothing found, return the expected gem path for better error messages
|
76
|
+
gem_binary
|
77
|
+
end
|
78
|
+
|
79
|
+
def development_context?
|
80
|
+
!project_root.nil?
|
81
|
+
end
|
82
|
+
|
83
|
+
def project_root
|
84
|
+
@project_root ||= find_project_root
|
85
|
+
end
|
86
|
+
|
87
|
+
def find_project_root
|
88
|
+
Pathname.new(__dir__).ascend do |dir|
|
89
|
+
return dir.to_s if File.exist?(File.join(dir, 'Cargo.toml'))
|
90
|
+
end
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def validate_input_file!(path)
|
95
|
+
return if File.exist?(path)
|
96
|
+
|
97
|
+
raise Error, "Input file does not exist: #{path}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def ensure_output_directory(path)
|
101
|
+
output_dir = File.dirname(path)
|
102
|
+
return if Dir.exist?(output_dir)
|
103
|
+
|
104
|
+
begin
|
105
|
+
FileUtils.mkdir_p(output_dir)
|
106
|
+
rescue StandardError => e
|
107
|
+
raise Error, "Failed to create output directory #{output_dir}: #{e.message}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def execute_command(cmd)
|
112
|
+
unless File.executable?(@binary_path)
|
113
|
+
raise Error, "Binary not found or not executable: #{@binary_path}. " \
|
114
|
+
'Make sure the gem was installed correctly and Rust is available.'
|
115
|
+
end
|
116
|
+
|
117
|
+
stdout, stderr, status = Open3.capture3(*cmd)
|
118
|
+
|
119
|
+
unless status.success?
|
120
|
+
error_message = stderr.empty? ? stdout : stderr
|
121
|
+
raise Error, "Command failed: #{error_message}"
|
122
|
+
end
|
123
|
+
|
124
|
+
stdout
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rbconfig'
|
4
|
+
|
5
|
+
module FluentTools
|
6
|
+
# Shared utilities for fluent-tools
|
7
|
+
module Utils
|
8
|
+
# Project constants
|
9
|
+
REPO_OWNER = 'Automattic'
|
10
|
+
BINARY_NAME = 'fluent-tools'
|
11
|
+
REPO_NAME = 'fluent-rust-tools'
|
12
|
+
|
13
|
+
# Platform to Rust target mapping
|
14
|
+
PLATFORM_RUST_TARGETS = {
|
15
|
+
'x86_64-linux' => 'x86_64-unknown-linux-gnu',
|
16
|
+
'arm64-linux' => 'aarch64-unknown-linux-gnu',
|
17
|
+
'x86_64-darwin' => 'x86_64-apple-darwin',
|
18
|
+
'arm64-darwin' => 'aarch64-apple-darwin',
|
19
|
+
'x86_64-windows' => 'x86_64-pc-windows-gnu',
|
20
|
+
'arm64-windows' => 'aarch64-pc-windows-gnullvm'
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
# Platform detection using RbConfig to identify the current system
|
24
|
+
# Returns a string in the format "{architecture}-{os}" (e.g., "arm64-darwin")
|
25
|
+
# Returns nil if the platform is not supported
|
26
|
+
def self.detect_platform
|
27
|
+
host_os = RbConfig::CONFIG['host_os']
|
28
|
+
host_cpu = RbConfig::CONFIG['host_cpu']
|
29
|
+
|
30
|
+
platform = case host_os
|
31
|
+
when /linux/
|
32
|
+
'linux'
|
33
|
+
when /darwin/
|
34
|
+
'darwin'
|
35
|
+
when /mingw|mswin/
|
36
|
+
'windows'
|
37
|
+
else
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
|
41
|
+
architecture = case host_cpu
|
42
|
+
when /x86_64|amd64/
|
43
|
+
'x86_64'
|
44
|
+
when /arm64|aarch64/
|
45
|
+
'arm64'
|
46
|
+
else
|
47
|
+
return nil
|
48
|
+
end
|
49
|
+
|
50
|
+
"#{architecture}-#{platform}"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Generate the expected binary name for a given platform
|
54
|
+
# Adds .exe extension for Windows platforms
|
55
|
+
def self.binary_name_for_platform(platform)
|
56
|
+
extension = binary_extension(platform)
|
57
|
+
"#{BINARY_NAME}-#{platform}#{extension}"
|
58
|
+
end
|
59
|
+
|
60
|
+
# Check if the current platform is Windows
|
61
|
+
def self.windows_platform?(platform = nil)
|
62
|
+
platform ||= detect_platform
|
63
|
+
platform&.include?('windows') || false
|
64
|
+
end
|
65
|
+
|
66
|
+
# Get binary extension for current platform
|
67
|
+
def self.binary_extension(platform = nil)
|
68
|
+
windows_platform?(platform) ? '.exe' : ''
|
69
|
+
end
|
70
|
+
|
71
|
+
# Validate that a platform is supported
|
72
|
+
def self.validate_platform!(platform)
|
73
|
+
return if PLATFORM_RUST_TARGETS.key?(platform)
|
74
|
+
|
75
|
+
raise "Invalid platform: #{platform}. Valid platforms: #{PLATFORM_RUST_TARGETS.keys.join(', ')}"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Convert platform name to Rust target triple
|
79
|
+
def self.platform_to_rust_target(platform)
|
80
|
+
validate_platform!(platform)
|
81
|
+
PLATFORM_RUST_TARGETS[platform]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Logger mixin to provide consistent logging across tools
|
85
|
+
module Logger
|
86
|
+
def log_info(message, verbose: true)
|
87
|
+
puts message if verbose
|
88
|
+
end
|
89
|
+
|
90
|
+
def log_success(message, verbose: true)
|
91
|
+
puts "✅ #{message}" if verbose
|
92
|
+
end
|
93
|
+
|
94
|
+
def log_warning(message, verbose: true)
|
95
|
+
puts "⚠️ #{message}" if verbose
|
96
|
+
end
|
97
|
+
|
98
|
+
def log_error(message, verbose: true)
|
99
|
+
warn "❌ #{message}" if verbose
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/fluent_tools.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'fluent_tools/version'
|
4
|
+
require_relative 'fluent_tools/utils'
|
5
|
+
require_relative 'fluent_tools/command_executor'
|
6
|
+
require_relative 'fluent_tools/cli'
|
7
|
+
|
8
|
+
# Ruby bindings and CLI for fluent-rust-tools
|
9
|
+
# Provides conversion between Fluent, Android XML, and PO formats
|
10
|
+
module FluentTools
|
11
|
+
# Custom error class for fluent-tools operations
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
# Convenience method for converting Fluent to Android XML
|
15
|
+
def self.fluent_to_android(input_path, output_path)
|
16
|
+
CommandExecutor.new.fluent_to_android(input_path, output_path)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Convenience method for converting Android XML to Fluent
|
20
|
+
def self.android_to_fluent(input_path, output_path, original_fluent: nil)
|
21
|
+
CommandExecutor.new.android_to_fluent(input_path, output_path, original_fluent: original_fluent)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Convenience method for converting Fluent to PO
|
25
|
+
def self.fluent_to_po(input_path, output_path, locale: 'en-US')
|
26
|
+
CommandExecutor.new.fluent_to_po(input_path, output_path, locale: locale)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Convenience method for converting PO to Fluent
|
30
|
+
def self.po_to_fluent(input_path, output_path)
|
31
|
+
CommandExecutor.new.po_to_fluent(input_path, output_path)
|
32
|
+
end
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-tools
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Automattic
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-07-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubocop-rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop-rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: A Ruby gem that wraps a Rust CLI tool for converting between Mozilla's
|
56
|
+
Fluent localization format and other formats like Android XML string resources and
|
57
|
+
GNU gettext PO files
|
58
|
+
email:
|
59
|
+
- mobile@automattic.com
|
60
|
+
executables:
|
61
|
+
- fluent-tools
|
62
|
+
extensions:
|
63
|
+
- ext/fluent_tools/extconf.rb
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- exe/fluent-tools
|
67
|
+
- ext/fluent_tools/extconf.rb
|
68
|
+
- installer.rb
|
69
|
+
- lib/fluent_tools.rb
|
70
|
+
- lib/fluent_tools/cli.rb
|
71
|
+
- lib/fluent_tools/command_executor.rb
|
72
|
+
- lib/fluent_tools/utils.rb
|
73
|
+
- lib/fluent_tools/version.rb
|
74
|
+
homepage: https://github.com/Automattic/fluent-rust-tools
|
75
|
+
licenses:
|
76
|
+
- MPL-2.0
|
77
|
+
metadata:
|
78
|
+
allowed_push_host: https://rubygems.org
|
79
|
+
homepage_uri: https://github.com/Automattic/fluent-rust-tools
|
80
|
+
source_code_uri: https://github.com/Automattic/fluent-rust-tools
|
81
|
+
changelog_uri: https://github.com/Automattic/fluent-rust-tools/blob/main/CHANGELOG.md
|
82
|
+
post_install_message: |
|
83
|
+
This fluent-tools gem will try to use a pre-built binary for your platform.
|
84
|
+
If unavailable, it will compile the Rust binary during installation.
|
85
|
+
|
86
|
+
If compilation is needed, make sure you have Rust installed: https://rustup.rs/
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 3.2.2
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubygems_version: 3.4.10
|
102
|
+
signing_key:
|
103
|
+
specification_version: 4
|
104
|
+
summary: Convert between Fluent and other formats (Android XML, GNU gettext PO)
|
105
|
+
test_files: []
|