idn2_wrap 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f6393803ea2a8301848ea439a8149786dc2374f3e8a81241d55eb5e11a2d96eb
4
+ data.tar.gz: a6b1ea77c0239796b8c33901ad3c91c282f92d02af86a8611a4959b8880e9b95
5
+ SHA512:
6
+ metadata.gz: c20f49a55e11dbaaad8c0915c73596a297ab5632c5fc667d7a35ed36d6ce017aa8a4a15e7ad906cc86a1a87669c5144e800ae838cfa247cfbeb32266de6013b8
7
+ data.tar.gz: 8e25e81052d24c426384b430636bd8cf568096b8ee6b250c6db2a13ca1e6574bbfed2f0015ea460220f92479775f57dd3f6b38134ecf3eab0580c1c7945f8d64
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ begin
6
+ require "rspec/core/rake_task"
7
+ RSpec::Core::RakeTask.new(:spec)
8
+ task default: :spec
9
+ #rescue LoadError
10
+ # If rspec isn't installed yet, keep a build fallback:
11
+ # task default: :build
12
+ end
data/idn2_wrap.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "idn2_wrap"
5
+ spec.version = File.read(File.expand_path("lib/idn2_wrap/version.rb", __dir__)).match(/VERSION\s*=\s*["'](.+)["']/)[1]
6
+ spec.authors = ["Alexey Makhotkin / ChatGPT"]
7
+ spec.email = ["squadette@gmail.com"]
8
+
9
+ spec.summary = "Minimal FFI wrapper for libidn2: to_ascii / to_unicode"
10
+ spec.description = "A tiny Ruby FFI wrapper exposing libidn2's to_ascii and to_unicode functions."
11
+ spec.homepage = "https://github.com/squadette/idn2_wrap"
12
+ spec.license = "MIT"
13
+
14
+ spec.files = Dir["lib/**/*", "README.md", "Rakefile", "Gemfile", "idn2_wrap.gemspec"]
15
+ spec.require_paths = ["lib"]
16
+
17
+ spec.add_dependency "ffi", ">= 1.15", "< 2.0"
18
+
19
+ spec.add_development_dependency "rspec", "~> 3.12"
20
+
21
+ spec.metadata = {
22
+ "source_code_uri" => spec.homepage
23
+ }
24
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ffi"
4
+
5
+ module Idn2Wrap
6
+ module LibC
7
+ extend FFI::Library
8
+ ffi_lib FFI::Library::LIBC
9
+ attach_function :free, [:pointer], :void
10
+ end
11
+
12
+ module IDN2
13
+ extend FFI::Library
14
+ # On most systems this will resolve correctly as "idn2".
15
+ # If needed, set ENV["IDN2_LIB"] to an explicit path or library name.
16
+ ffi_lib(ENV["IDN2_LIB"] || "idn2")
17
+
18
+ # int idn2_to_ascii_8z(const char *input, char **output, int flags);
19
+ attach_function :idn2_to_ascii_8z, [:string, :pointer, :int], :int
20
+
21
+ # int idn2_to_unicode_8z8z(const char *input, char **output, int flags);
22
+ attach_function :idn2_to_unicode_8z8z, [:string, :pointer, :int], :int
23
+
24
+ # const char *idn2_strerror(int rc);
25
+ attach_function :idn2_strerror, [:int], :string
26
+
27
+ # const char *idn2_check_version(const char *req_version);
28
+ attach_function :idn2_check_version, [:string], :string
29
+ end
30
+
31
+ # Raised on non-zero libidn2 return codes.
32
+ class Error < StandardError
33
+ attr_reader :code
34
+ def initialize(code, message)
35
+ @code = code
36
+ super("[libidn2 #{code}] #{message}")
37
+ end
38
+ end
39
+
40
+ # Common flags people often want (values are stable in libidn2,
41
+ # but we keep them optional to avoid drift; pass integers if you prefer).
42
+ # If you know specific flags you want, pass them via `flags:`.
43
+ module Flags
44
+ # These values match libidn2 headers as of typical distributions.
45
+ # They are provided for convenience; you can omit and keep flags=0.
46
+ NFC_INPUT = 0x0001 # IDN2_NFC_INPUT
47
+ ALLOW_UNASSIGNED = 0x0002 # IDN2_ALLOW_UNASSIGNED
48
+ USE_STD3_ASCII = 0x0004 # IDN2_USE_STD3_ASCII_RULES
49
+ TRANSITIONAL = 0x0008 # IDN2_TRANSITIONAL
50
+ NONTRANSITIONAL = 0x0010 # IDN2_NONTRANSITIONAL
51
+ end
52
+
53
+ module_function
54
+
55
+ # Convert a Unicode domain (e.g., "münich.example") to ASCII/Punycode ("xn--mnich-kva.example").
56
+ #
57
+ # @param input [String] Unicode domain or label
58
+ # @param flags [Integer] Optional libidn2 flags (default 0)
59
+ # @return [String] ASCII/Punycode domain
60
+ # @raise [Idn2Wrap::Error] on libidn2 failure
61
+ def to_ascii(input, flags: 0)
62
+ out_pp = FFI::MemoryPointer.new(:pointer)
63
+ rc = IDN2.idn2_to_ascii_8z(input, out_pp, flags)
64
+ raise Error.new(rc, IDN2.idn2_strerror(rc)) unless rc == 0
65
+
66
+ out_p = out_pp.read_pointer
67
+ begin
68
+ out_p.read_string
69
+ ensure
70
+ LibC.free(out_p)
71
+ end
72
+ end
73
+
74
+ # Convert an ASCII/Punycode domain (e.g., "xn--mnich-kva.example") to Unicode ("münich.example").
75
+ #
76
+ # @param input [String] ASCII/Punycode domain or label
77
+ # @param flags [Integer] Optional libidn2 flags (default 0)
78
+ # @return [String] Unicode domain
79
+ # @raise [Idn2Wrap::Error] on libidn2 failure
80
+ def to_unicode(input, flags: 0)
81
+ out_pp = FFI::MemoryPointer.new(:pointer)
82
+ rc = IDN2.idn2_to_unicode_8z8z(input, out_pp, flags)
83
+ raise Error.new(rc, IDN2.idn2_strerror(rc)) unless rc == 0
84
+
85
+ out_p = out_pp.read_pointer
86
+ begin
87
+ out_p.read_string.force_encoding("UTF-8").encode("UTF-8", invalid: :replace, undef: :replace)
88
+ ensure
89
+ LibC.free(out_p)
90
+ end
91
+ end
92
+
93
+ # Return the libidn2 version string, or nil if the runtime version is
94
+ # older than the given minimum.
95
+ #
96
+ # @param req_version [String, nil] Minimum version required (e.g., "2.3.0"), or nil
97
+ # @return [String, nil] Actual version string, or nil if requirement not met
98
+ def check_version(req_version = nil)
99
+ v = IDN2.idn2_check_version(req_version)
100
+ v.nil? ? nil : v.dup.force_encoding("UTF-8")
101
+ end
102
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Idn2Wrap
4
+ VERSION = "0.1.0"
5
+ end
data/lib/idn2_wrap.rb ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "idn2_wrap/version"
4
+ require_relative "idn2_wrap/ffi"
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: idn2_wrap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexey Makhotkin / ChatGPT
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ffi
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '1.15'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '1.15'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '2.0'
32
+ - !ruby/object:Gem::Dependency
33
+ name: rspec
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '3.12'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '3.12'
46
+ description: A tiny Ruby FFI wrapper exposing libidn2's to_ascii and to_unicode functions.
47
+ email:
48
+ - squadette@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - Gemfile
54
+ - Rakefile
55
+ - idn2_wrap.gemspec
56
+ - lib/idn2_wrap.rb
57
+ - lib/idn2_wrap/ffi.rb
58
+ - lib/idn2_wrap/version.rb
59
+ homepage: https://github.com/squadette/idn2_wrap
60
+ licenses:
61
+ - MIT
62
+ metadata:
63
+ source_code_uri: https://github.com/squadette/idn2_wrap
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubygems_version: 3.6.9
79
+ specification_version: 4
80
+ summary: 'Minimal FFI wrapper for libidn2: to_ascii / to_unicode'
81
+ test_files: []