hx_ruby 0.3.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/ext/hx_ruby/Cargo.toml +41 -0
- data/ext/hx_ruby/extconf.rb +8 -0
- data/ext/hx_ruby/src/lib.rs +129 -0
- data/lib/hx_ruby/version.rb +7 -0
- data/lib/hx_ruby.rb +30 -0
- metadata +69 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ed966ca9ef6f53c6fc8eb54f403e685ccce968c18857095d48bc46c0fb8eda14
|
|
4
|
+
data.tar.gz: f7cc0f292ffba4285a9612fd2d8e14d7dd78290ce18998475306b20874a5af57
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4418e948a6106c092c8d40ac27bac2b427e4835f677ac4593825f2ef8aa091cef139a71d325c977617540553b984adff1f98c67ffecc7d6672569dab1b7bd2f5
|
|
7
|
+
data.tar.gz: fae787afd40a83fd3f7e35342148d84ca3426807767f2059e7b2495fe913ec24b48021893f2a4bf55949e3eb9750c86e2c125773bcac6adecf307b1cf1fe39f4
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Every value is written out, and nothing here is inherited from the workspace.
|
|
2
|
+
#
|
|
3
|
+
# This is the one manifest in the repository that leaves it: `gem build` copies
|
|
4
|
+
# it verbatim into the gem, and it is compiled on a machine where there is no
|
|
5
|
+
# workspace to inherit from. `edition.workspace = true` there is not a fallback,
|
|
6
|
+
# it is an error - "failed to find a workspace root" - and the gem will not
|
|
7
|
+
# install. CI checks that nothing in this file says `workspace`.
|
|
8
|
+
#
|
|
9
|
+
# The cost is that the version is written in two places. `.github/workflows/ci.yml`
|
|
10
|
+
# fails if it stops matching the workspace's.
|
|
11
|
+
[package]
|
|
12
|
+
name = "hx-ruby"
|
|
13
|
+
description = "Ruby bindings over hx-catalog's preset inspector, so the tone browser reads .hlx files with the same code the desktop uses"
|
|
14
|
+
version = "0.3.0"
|
|
15
|
+
edition = "2021"
|
|
16
|
+
license = "MIT"
|
|
17
|
+
repository = "https://github.com/crmne/tonepush"
|
|
18
|
+
rust-version = "1.87"
|
|
19
|
+
|
|
20
|
+
[lib]
|
|
21
|
+
# The name Ruby dlopen's and the `Init_hx_ruby` entry it calls; keep it in step
|
|
22
|
+
# with the gem's `require "hx_ruby/hx_ruby"`.
|
|
23
|
+
name = "hx_ruby"
|
|
24
|
+
crate-type = ["cdylib"]
|
|
25
|
+
|
|
26
|
+
[dependencies]
|
|
27
|
+
# The whole point: parse presets with hx-catalog's inspector, never a Ruby
|
|
28
|
+
# reimplementation. hx-proto is the preset model those facts ultimately describe
|
|
29
|
+
# and is kept as a direct dependency so this binding can surface its types
|
|
30
|
+
# without a manifest change.
|
|
31
|
+
#
|
|
32
|
+
# Named by version and not by path, unlike every other crate here, because this
|
|
33
|
+
# manifest is the one that leaves the repository: `gem build` copies it verbatim
|
|
34
|
+
# into the gem, and it is then compiled on a machine that has no workspace and
|
|
35
|
+
# no sibling crates. A path would point at nothing there. The workspace root
|
|
36
|
+
# patches both back to the local sources, so building here still compiles what
|
|
37
|
+
# is checked out rather than what was last published.
|
|
38
|
+
hx-catalog = "0.3.0"
|
|
39
|
+
hx-proto = "0.3.0"
|
|
40
|
+
magnus = "0.8"
|
|
41
|
+
serde_json = "1"
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
//! Ruby bindings over `hx_catalog`'s preset inspector.
|
|
2
|
+
//!
|
|
3
|
+
//! The tone browser is a Rails app, but it must not grow a second, drifting
|
|
4
|
+
//! copy of the `.hlx` reader in Ruby. So parsing stays here: this crate hands a
|
|
5
|
+
//! preset's JSON to [`hx_catalog::inspect`] - the very reader the desktop uses -
|
|
6
|
+
//! and returns the facts as a plain Ruby Hash. The Rails side is then a thin
|
|
7
|
+
//! value object over those facts, never a parser.
|
|
8
|
+
|
|
9
|
+
use hx_catalog::{Catalog, ChainContent, OutputTarget, Tone};
|
|
10
|
+
use magnus::{function, prelude::*, Error, ExceptionClass, RHash, Ruby};
|
|
11
|
+
|
|
12
|
+
/// `HxRuby.inspect_hlx(json)` -> a Hash of the tone facts a browser sorts by.
|
|
13
|
+
///
|
|
14
|
+
/// Raises `HxRuby::CatalogNotInstalled` when the HX Edit resources are missing,
|
|
15
|
+
/// `HxRuby::Error` for any other catalog read failure, and `ArgumentError` when
|
|
16
|
+
/// the string is not valid preset JSON. It resolves both DSPs and every block,
|
|
17
|
+
/// and by construction never panics.
|
|
18
|
+
fn inspect_hlx(ruby: &Ruby, json: String) -> Result<RHash, Error> {
|
|
19
|
+
let value: serde_json::Value = serde_json::from_str(&json).map_err(|e| {
|
|
20
|
+
Error::new(
|
|
21
|
+
ruby.exception_arg_error(),
|
|
22
|
+
format!("not a valid .hlx preset: {e}"),
|
|
23
|
+
)
|
|
24
|
+
})?;
|
|
25
|
+
|
|
26
|
+
let catalog = Catalog::load().map_err(|e| load_error(ruby, &e))?;
|
|
27
|
+
let tone = hx_catalog::inspect(&value, &catalog);
|
|
28
|
+
tone_to_hash(ruby, &tone)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Turn a catalog load failure into the right Ruby exception. "Not installed"
|
|
32
|
+
/// is its own class so the Rails app can rescue the one recoverable case - the
|
|
33
|
+
/// resources are absent - apart from a genuine read or parse fault.
|
|
34
|
+
fn load_error(ruby: &Ruby, error: &hx_catalog::Error) -> Error {
|
|
35
|
+
let class = match error {
|
|
36
|
+
hx_catalog::Error::NotInstalled(_) => hx_error_class(ruby, "CatalogNotInstalled"),
|
|
37
|
+
_ => hx_error_class(ruby, "Error"),
|
|
38
|
+
};
|
|
39
|
+
Error::new(class, error.to_string())
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// Re-resolve one of the crate's exception classes, defined in [`init`]. Falls
|
|
43
|
+
/// back to `RuntimeError` if the module is somehow gone, so a lookup miss still
|
|
44
|
+
/// raises rather than panics.
|
|
45
|
+
fn hx_error_class(ruby: &Ruby, name: &str) -> ExceptionClass {
|
|
46
|
+
ruby.define_module("HxRuby")
|
|
47
|
+
.and_then(|module| module.const_get(name))
|
|
48
|
+
.unwrap_or_else(|_| ruby.exception_runtime_error())
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
fn tone_to_hash(ruby: &Ruby, tone: &Tone) -> Result<RHash, Error> {
|
|
52
|
+
let hash = ruby.hash_new();
|
|
53
|
+
hash.aset(ruby.to_symbol("name"), tone.name.as_str())?;
|
|
54
|
+
|
|
55
|
+
let blocks = ruby.ary_new();
|
|
56
|
+
for block in &tone.blocks {
|
|
57
|
+
let entry = ruby.hash_new();
|
|
58
|
+
entry.aset(ruby.to_symbol("path"), block.path)?;
|
|
59
|
+
entry.aset(ruby.to_symbol("position"), block.position)?;
|
|
60
|
+
entry.aset(ruby.to_symbol("model_number"), block.model_number)?;
|
|
61
|
+
entry.aset(ruby.to_symbol("model_name"), block.model_name.as_str())?;
|
|
62
|
+
// nil when the model sits in no browse category, which is rare.
|
|
63
|
+
entry.aset(ruby.to_symbol("category"), block.category)?;
|
|
64
|
+
entry.aset(ruby.to_symbol("enabled"), block.enabled)?;
|
|
65
|
+
|
|
66
|
+
let params = ruby.hash_new();
|
|
67
|
+
for (name, value) in &block.params {
|
|
68
|
+
params.aset(name.as_str(), *value)?;
|
|
69
|
+
}
|
|
70
|
+
entry.aset(ruby.to_symbol("params"), params)?;
|
|
71
|
+
|
|
72
|
+
blocks.push(entry)?;
|
|
73
|
+
}
|
|
74
|
+
hash.aset(ruby.to_symbol("blocks"), blocks)?;
|
|
75
|
+
|
|
76
|
+
let models_used = ruby.ary_new();
|
|
77
|
+
for number in &tone.models_used {
|
|
78
|
+
models_used.push(*number)?;
|
|
79
|
+
}
|
|
80
|
+
hash.aset(ruby.to_symbol("models_used"), models_used)?;
|
|
81
|
+
|
|
82
|
+
hash.aset(ruby.to_symbol("has_amp"), tone.has_amp)?;
|
|
83
|
+
hash.aset(ruby.to_symbol("has_cab_or_ir"), tone.has_cab_or_ir)?;
|
|
84
|
+
hash.aset(
|
|
85
|
+
ruby.to_symbol("chain_content"),
|
|
86
|
+
chain_content_name(tone.chain_content),
|
|
87
|
+
)?;
|
|
88
|
+
hash.aset(
|
|
89
|
+
ruby.to_symbol("output_target"),
|
|
90
|
+
output_target_name(tone.output_target_guess),
|
|
91
|
+
)?;
|
|
92
|
+
|
|
93
|
+
let skipped = ruby.ary_new();
|
|
94
|
+
for note in &tone.skipped {
|
|
95
|
+
skipped.push(note.as_str())?;
|
|
96
|
+
}
|
|
97
|
+
hash.aset(ruby.to_symbol("skipped"), skipped)?;
|
|
98
|
+
|
|
99
|
+
Ok(hash)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// Snake-case names that line up with the Rails `Tone` enums, so the value
|
|
103
|
+
/// object can hand them straight to Active Record.
|
|
104
|
+
fn chain_content_name(content: ChainContent) -> &'static str {
|
|
105
|
+
match content {
|
|
106
|
+
ChainContent::FullRig => "full_rig",
|
|
107
|
+
ChainContent::AmpAndCab => "amp_and_cab",
|
|
108
|
+
ChainContent::AmpOnly => "amp_only",
|
|
109
|
+
ChainContent::EffectsOnly => "effects_only",
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// The inspector only knows whether the tone carries its own speaker, so it
|
|
114
|
+
/// offers two honest guesses rather than the browser's full output vocabulary.
|
|
115
|
+
fn output_target_name(target: OutputTarget) -> &'static str {
|
|
116
|
+
match target {
|
|
117
|
+
OutputTarget::FrfrPa => "frfr_pa",
|
|
118
|
+
OutputTarget::GuitarCabOrDi => "guitar_cab_or_di",
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#[magnus::init]
|
|
123
|
+
fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
124
|
+
let module = ruby.define_module("HxRuby")?;
|
|
125
|
+
let base = module.define_error("Error", ruby.exception_standard_error())?;
|
|
126
|
+
module.define_error("CatalogNotInstalled", base)?;
|
|
127
|
+
module.define_singleton_method("inspect_hlx", function!(inspect_hlx, 1))?;
|
|
128
|
+
Ok(())
|
|
129
|
+
}
|
data/lib/hx_ruby.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "hx_ruby/version"
|
|
4
|
+
|
|
5
|
+
# Load the compiled Rust extension. rake-compiler installs it under a Ruby
|
|
6
|
+
# version directory for gems that ship several; a plain extconf build (what
|
|
7
|
+
# Bundler runs for a path gem) drops it straight in lib/hx_ruby. Try the
|
|
8
|
+
# versioned path first, then fall back.
|
|
9
|
+
begin
|
|
10
|
+
ruby_version = RUBY_VERSION[/\d+\.\d+/]
|
|
11
|
+
require_relative "hx_ruby/#{ruby_version}/hx_ruby"
|
|
12
|
+
rescue LoadError
|
|
13
|
+
begin
|
|
14
|
+
require_relative "hx_ruby/hx_ruby"
|
|
15
|
+
rescue LoadError => e
|
|
16
|
+
# Bundler does not compile a path gem's extension on install, so a fresh
|
|
17
|
+
# checkout has to build it once.
|
|
18
|
+
raise LoadError, "#{e.message}\n\nThe hx_ruby native extension is not " \
|
|
19
|
+
"built yet. From the gem directory run: bundle exec rake compile"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Ruby bindings over hx-catalog's `.hlx` inspector.
|
|
24
|
+
#
|
|
25
|
+
# The native `HxRuby.inspect_hlx(json)` is defined in Rust (see ext/hx_ruby).
|
|
26
|
+
# It takes a preset's JSON string and returns a Hash of tone facts, raising
|
|
27
|
+
# {HxRuby::CatalogNotInstalled} when the HX Edit resources are missing and
|
|
28
|
+
# {HxRuby::Error} for any other catalog read failure.
|
|
29
|
+
module HxRuby
|
|
30
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hx_ruby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Carmine Paolino
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-08-10 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rb_sys
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0.9'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0.9'
|
|
27
|
+
description: Ruby bindings over hx-catalog's preset inspector, so the tone browser
|
|
28
|
+
reads .hlx files with the same Rust code the desktop uses instead of reimplementing
|
|
29
|
+
parsing in Ruby.
|
|
30
|
+
email:
|
|
31
|
+
- carmine@paolino.me
|
|
32
|
+
executables: []
|
|
33
|
+
extensions:
|
|
34
|
+
- ext/hx_ruby/extconf.rb
|
|
35
|
+
extra_rdoc_files: []
|
|
36
|
+
files:
|
|
37
|
+
- ext/hx_ruby/Cargo.toml
|
|
38
|
+
- ext/hx_ruby/extconf.rb
|
|
39
|
+
- ext/hx_ruby/src/lib.rs
|
|
40
|
+
- lib/hx_ruby.rb
|
|
41
|
+
- lib/hx_ruby/version.rb
|
|
42
|
+
homepage: https://docs.tonepush.rocks
|
|
43
|
+
licenses:
|
|
44
|
+
- MIT
|
|
45
|
+
metadata:
|
|
46
|
+
homepage_uri: https://docs.tonepush.rocks
|
|
47
|
+
source_code_uri: https://github.com/crmne/tonepush
|
|
48
|
+
changelog_uri: https://github.com/crmne/tonepush/releases
|
|
49
|
+
rubygems_mfa_required: 'true'
|
|
50
|
+
post_install_message:
|
|
51
|
+
rdoc_options: []
|
|
52
|
+
require_paths:
|
|
53
|
+
- lib
|
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: 3.1.0
|
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: '0'
|
|
64
|
+
requirements: []
|
|
65
|
+
rubygems_version: 3.5.22
|
|
66
|
+
signing_key:
|
|
67
|
+
specification_version: 4
|
|
68
|
+
summary: Parse Line 6 .hlx presets into tone facts with the TonePush Rust catalog
|
|
69
|
+
test_files: []
|