fulgur 0.0.1 → 0.5.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 +4 -4
- data/CHANGELOG.md +27 -0
- data/Cargo.toml +30 -0
- data/README.md +210 -9
- data/ext/fulgur/Cargo.toml +30 -0
- data/ext/fulgur/extconf.rb +8 -0
- data/lib/fulgur/asset_bundle.rb +11 -0
- data/lib/fulgur/margin.rb +36 -0
- data/lib/fulgur/version.rb +5 -0
- data/lib/fulgur.rb +15 -11
- data/src/asset_bundle.rs +78 -0
- data/src/engine.rs +297 -0
- data/src/error.rs +57 -0
- data/src/gvl.rs +79 -0
- data/src/lib.rs +40 -0
- data/src/margin.rs +76 -0
- data/src/page_size.rs +93 -0
- data/src/pdf.rs +114 -0
- metadata +41 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e921af1392812aec76a28ea1bbf77598131c39c3fd344159375dd3d9642a2185
|
|
4
|
+
data.tar.gz: 75810a1529a8d327b17950cf60372bf0d0a54f5c1dbf1406297d560e41ad018c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 857299514d3931f5404e1701ae5524984ebbcc4b51e3afc164c558828d6254b6f1e3c59ab34fcf68530d0a63fc47042e85d1408a1f31a178bafd4d2a8c13ecf6
|
|
7
|
+
data.tar.gz: 6d946aad252616dc511afbd17ad895540c5923418be7435ac1648f3aba1a559920a78704a837345f515af97f62bfc254bb4da42b09f52667ba370efa728d2afa
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `fulgur` gem will be documented here.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.0.1] - 2026-04-17
|
|
8
|
+
|
|
9
|
+
Initial Ruby binding for fulgur.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `Fulgur::Engine` (kwargs constructor + builder chain)
|
|
14
|
+
- `Fulgur::EngineBuilder` for reusable engine construction
|
|
15
|
+
- `Fulgur::AssetBundle` with long (`add_*`) and short (`css`, `font_file`, etc.) aliases
|
|
16
|
+
- `Fulgur::PageSize` with `A4` / `LETTER` / `A3` constants and `.custom(w_mm, h_mm)`; accepts `Symbol`, `String`, or class constants as input
|
|
17
|
+
- `Fulgur::Margin` with CSS-style positional args, keyword args, and `.uniform` / `.symmetric` factories
|
|
18
|
+
- `Fulgur::Pdf` result object: `#to_s` (ASCII-8BIT), `#to_base64`, `#to_data_uri`, `#write_to_path`, `#write_to_io` (64 KiB chunked, binmode-guaranteed), `#bytesize`
|
|
19
|
+
- `Engine#render_html` and `Engine#render_html_to_file` release the GVL during the Rust render call
|
|
20
|
+
- Error hierarchy: `Fulgur::Error` / `Fulgur::RenderError` / `Fulgur::AssetError`, plus standard `ArgumentError` / `Errno::ENOENT`
|
|
21
|
+
- Ruby 3.3+ support
|
|
22
|
+
|
|
23
|
+
### Known Limitations
|
|
24
|
+
|
|
25
|
+
- Precompiled gems / RubyGems publish automation are tracked separately (fulgur-qyf) and not yet in place; gems must be built from source for now
|
|
26
|
+
- Streaming renderer: Krilla emits bytes at the end of rendering, so `#write_to_io` chunks a completed buffer rather than streaming during layout
|
|
27
|
+
- No Ractor safety analysis yet
|
data/Cargo.toml
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "fulgur-ruby"
|
|
3
|
+
version = "0.5.0"
|
|
4
|
+
# rb_sys cross-gem-action は `directory: crates/fulgur-ruby` のみをコンテナに
|
|
5
|
+
# マウントするため、この Cargo.toml は workspace root 不在でも cargo metadata
|
|
6
|
+
# を解決できる必要がある。したがって workspace 継承 (`.workspace = true`) は
|
|
7
|
+
# 使わず、値は root Cargo.toml [workspace.package] と同期させて inline する。
|
|
8
|
+
edition = "2024"
|
|
9
|
+
rust-version = "1.85.0"
|
|
10
|
+
license = "MIT OR Apache-2.0"
|
|
11
|
+
repository = "https://github.com/mitsuru/fulgur"
|
|
12
|
+
homepage = "https://github.com/mitsuru/fulgur"
|
|
13
|
+
description = "Ruby bindings for fulgur — offline HTML/CSS to PDF conversion"
|
|
14
|
+
publish = false
|
|
15
|
+
|
|
16
|
+
[lib]
|
|
17
|
+
name = "fulgur"
|
|
18
|
+
crate-type = ["cdylib", "rlib"]
|
|
19
|
+
|
|
20
|
+
[features]
|
|
21
|
+
ruby-api = ["dep:magnus", "dep:rb-sys"]
|
|
22
|
+
|
|
23
|
+
[dependencies]
|
|
24
|
+
fulgur = { path = "../fulgur" }
|
|
25
|
+
base64 = "0.22"
|
|
26
|
+
magnus = { version = "0.7", optional = true }
|
|
27
|
+
rb-sys = { version = "0.9", optional = true }
|
|
28
|
+
# Send/Sync 保証 (Engine) のコンパイル時 assertion で使用。
|
|
29
|
+
# ext/fulgur/Cargo.toml にも同じ依存を追加していること。
|
|
30
|
+
static_assertions = "1.1"
|
data/README.md
CHANGED
|
@@ -1,28 +1,228 @@
|
|
|
1
1
|
# fulgur
|
|
2
2
|
|
|
3
|
-
Ruby bindings for [fulgur](https://github.com/mitsuru/fulgur) — an offline,
|
|
3
|
+
Ruby bindings for [fulgur](https://github.com/mitsuru/fulgur) — an offline,
|
|
4
|
+
deterministic HTML/CSS to PDF conversion library written in Rust.
|
|
4
5
|
|
|
5
6
|
## Status
|
|
6
7
|
|
|
7
|
-
**
|
|
8
|
+
**MVP (gem v0.0.1, unreleased).** The core `Engine` / `EngineBuilder` /
|
|
9
|
+
`AssetBundle` / `PageSize` / `Margin` / `Pdf` API is available. Precompiled
|
|
10
|
+
gems, batch rendering, sandboxing, and template-engine wiring are planned
|
|
11
|
+
for later releases.
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
> **Versioning note:** the `fulgur` gem is versioned independently from the
|
|
14
|
+
> underlying `fulgur` Rust crate and the `pyfulgur` PyPI package. This gem
|
|
15
|
+
> starts at `0.0.1` as an MVP and will bump on its own cadence. "v0.5.0"
|
|
16
|
+
> elsewhere in project documents refers to the parent epic that bundles
|
|
17
|
+
> the Rust / Python / Ruby shipping milestones, not this gem's version.
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
> **Note:** v0.0.1 is an early MVP. Pre-built gems are not yet published to
|
|
22
|
+
> RubyGems; build from source for now.
|
|
23
|
+
|
|
24
|
+
Requires a Rust toolchain (1.85+) and Ruby 3.3+.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# From a checkout of the fulgur repository
|
|
28
|
+
cd crates/fulgur-ruby
|
|
29
|
+
bundle install
|
|
30
|
+
bundle exec rake compile
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Once pre-built gems ship, installation will be:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
gem install fulgur
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
For Bundler:
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
gem "fulgur"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
12
46
|
|
|
13
47
|
```ruby
|
|
14
48
|
require "fulgur"
|
|
15
49
|
|
|
16
50
|
bundle = Fulgur::AssetBundle.new
|
|
17
51
|
bundle.add_css("body { font-family: sans-serif; }")
|
|
18
|
-
bundle.add_font_file("fonts/NotoSans-Regular.ttf")
|
|
19
52
|
|
|
20
|
-
engine = Fulgur::Engine.
|
|
21
|
-
|
|
53
|
+
engine = Fulgur::Engine.new(page_size: :a4, assets: bundle)
|
|
54
|
+
pdf = engine.render_html("<h1>Hello, world!</h1>")
|
|
55
|
+
|
|
56
|
+
pdf.write_to_path("output.pdf")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Builder style:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
engine = Fulgur::Engine.builder
|
|
63
|
+
.page_size(Fulgur::PageSize::A4)
|
|
64
|
+
.landscape(false)
|
|
65
|
+
.title("My doc")
|
|
66
|
+
.assets(bundle)
|
|
67
|
+
.build
|
|
68
|
+
|
|
69
|
+
engine.render_html_to_file("<h1>Hi</h1>", "out.pdf")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## API surface
|
|
73
|
+
|
|
74
|
+
### `Fulgur::Engine`
|
|
75
|
+
|
|
76
|
+
Keyword-argument constructor:
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
engine = Fulgur::Engine.new(
|
|
80
|
+
page_size: :a4, # Symbol, String, or Fulgur::PageSize
|
|
81
|
+
margin: Fulgur::Margin.uniform(72),
|
|
82
|
+
landscape: false,
|
|
83
|
+
title: "My Document",
|
|
84
|
+
author: "Me",
|
|
85
|
+
lang: "en",
|
|
86
|
+
bookmarks: true,
|
|
87
|
+
assets: bundle, # Fulgur::AssetBundle
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Or use the builder:
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
engine = Fulgur::Engine.builder
|
|
95
|
+
.page_size(:letter)
|
|
96
|
+
.margin(Fulgur::Margin.new(72, 36, 48, 24))
|
|
97
|
+
.assets(bundle)
|
|
98
|
+
.build
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Rendering
|
|
22
102
|
|
|
23
|
-
|
|
103
|
+
```ruby
|
|
104
|
+
pdf = engine.render_html(html_string) # => Fulgur::Pdf
|
|
105
|
+
engine.render_html_to_file(html, "out.pdf") # shortcut
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`render_html` and `render_html_to_file` release the GVL, allowing other
|
|
109
|
+
Ruby threads to run concurrently during rendering.
|
|
110
|
+
|
|
111
|
+
### `Fulgur::Pdf` (render result)
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
pdf.bytesize # => Integer
|
|
115
|
+
pdf.to_s # => String (ASCII-8BIT binary)
|
|
116
|
+
pdf.to_base64 # => String (Base64)
|
|
117
|
+
pdf.to_data_uri # => "data:application/pdf;base64,..."
|
|
118
|
+
pdf.write_to_path("out.pdf") # write raw bytes to file path
|
|
119
|
+
pdf.write_to_io(io) # chunked write to any IO (calls binmode when supported)
|
|
24
120
|
```
|
|
25
121
|
|
|
122
|
+
The result object keeps bytes on the Rust side. Methods like `to_base64`
|
|
123
|
+
encode directly from the Rust buffer, avoiding an intermediate Ruby binary
|
|
124
|
+
String. For server-side batch workloads rendering many PDFs, this halves
|
|
125
|
+
peak memory compared with `Base64.strict_encode64(bytes)` on the Ruby
|
|
126
|
+
side.
|
|
127
|
+
|
|
128
|
+
### `Fulgur::AssetBundle`
|
|
129
|
+
|
|
130
|
+
Offline-first: all assets must be explicitly registered.
|
|
131
|
+
|
|
132
|
+
```ruby
|
|
133
|
+
bundle = Fulgur::AssetBundle.new
|
|
134
|
+
bundle.add_css("body { font-family: 'Noto Sans' }")
|
|
135
|
+
bundle.add_css_file("style.css")
|
|
136
|
+
bundle.add_font_file("NotoSans-Regular.ttf")
|
|
137
|
+
bundle.add_image("logo", File.binread("logo.png"))
|
|
138
|
+
bundle.add_image_file("icon", "icon.png")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Short aliases are also available:
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
bundle.css "..."
|
|
145
|
+
bundle.css_file "style.css"
|
|
146
|
+
bundle.font_file "NotoSans.ttf"
|
|
147
|
+
bundle.image "logo", bytes
|
|
148
|
+
bundle.image_file "icon", "icon.png"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### `Fulgur::PageSize`
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
Fulgur::PageSize::A4
|
|
155
|
+
Fulgur::PageSize::LETTER
|
|
156
|
+
Fulgur::PageSize::A3
|
|
157
|
+
Fulgur::PageSize.custom(100, 200) # width/height in mm
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Engine kwargs and builder also accept `:a4`, `"A4"`, etc. as shorthand.
|
|
161
|
+
|
|
162
|
+
### `Fulgur::Margin`
|
|
163
|
+
|
|
164
|
+
```ruby
|
|
165
|
+
Fulgur::Margin.new(72) # uniform
|
|
166
|
+
Fulgur::Margin.new(72, 36) # [vertical, horizontal]
|
|
167
|
+
Fulgur::Margin.new(72, 36, 48, 24) # [top, right, bottom, left]
|
|
168
|
+
Fulgur::Margin.new(top: 72, right: 36, bottom: 48, left: 24)
|
|
169
|
+
Fulgur::Margin.uniform(72)
|
|
170
|
+
Fulgur::Margin.symmetric(72, 36)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
All values are in points (pt).
|
|
174
|
+
|
|
175
|
+
## LLM integration
|
|
176
|
+
|
|
177
|
+
`Fulgur::Pdf#to_base64` and `#to_data_uri` are optimized for passing PDFs
|
|
178
|
+
to LLMs as base64-encoded payloads (e.g., Anthropic Claude, OpenAI GPT-4):
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
pdf = engine.render_html(html)
|
|
182
|
+
|
|
183
|
+
anthropic.messages.create(
|
|
184
|
+
model: "claude-opus-4-7",
|
|
185
|
+
messages: [{
|
|
186
|
+
role: "user",
|
|
187
|
+
content: [
|
|
188
|
+
{
|
|
189
|
+
type: "document",
|
|
190
|
+
source: {
|
|
191
|
+
type: "base64",
|
|
192
|
+
media_type: "application/pdf",
|
|
193
|
+
data: pdf.to_base64,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
{ type: "text", text: "Summarize this document." },
|
|
197
|
+
],
|
|
198
|
+
}],
|
|
199
|
+
)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Errors
|
|
203
|
+
|
|
204
|
+
```text
|
|
205
|
+
Fulgur::Error # base class (StandardError)
|
|
206
|
+
Fulgur::RenderError # rendering failure (HTML parse, layout, PDF generation, WOFF decode)
|
|
207
|
+
Fulgur::AssetError # asset registration failure (unsupported font format, invalid asset)
|
|
208
|
+
|
|
209
|
+
ArgumentError # invalid arguments (unknown page_size, malformed margin)
|
|
210
|
+
Errno::ENOENT # missing font/image/CSS file
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Development
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
cd crates/fulgur-ruby
|
|
217
|
+
bundle install
|
|
218
|
+
bundle exec rake compile # builds the Rust extension
|
|
219
|
+
bundle exec rspec # runs tests
|
|
220
|
+
bundle exec rake # compile + test
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The native extension lives under `ext/fulgur/`, the Ruby-side wrappers
|
|
224
|
+
under `lib/`, and Rust sources under `src/`.
|
|
225
|
+
|
|
26
226
|
## Links
|
|
27
227
|
|
|
28
228
|
- [fulgur on GitHub](https://github.com/mitsuru/fulgur)
|
|
@@ -30,4 +230,5 @@ File.binwrite("output.pdf", pdf_bytes)
|
|
|
30
230
|
|
|
31
231
|
## License
|
|
32
232
|
|
|
33
|
-
Licensed under either of Apache License, Version 2.0 or MIT license at
|
|
233
|
+
Licensed under either of Apache License, Version 2.0 or MIT license at
|
|
234
|
+
your option.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[workspace]
|
|
2
|
+
|
|
3
|
+
[package]
|
|
4
|
+
name = "fulgur"
|
|
5
|
+
version = "0.5.0"
|
|
6
|
+
# crates/fulgur-ruby/Cargo.toml と同じ src を共有 (path = "../../src/lib.rs")。
|
|
7
|
+
# workspace と同じ edition 2024 に揃える。
|
|
8
|
+
edition = "2024"
|
|
9
|
+
|
|
10
|
+
[lib]
|
|
11
|
+
name = "fulgur"
|
|
12
|
+
crate-type = ["cdylib"]
|
|
13
|
+
path = "../../src/lib.rs"
|
|
14
|
+
|
|
15
|
+
[features]
|
|
16
|
+
default = ["ruby-api"]
|
|
17
|
+
ruby-api = ["dep:magnus", "dep:rb-sys"]
|
|
18
|
+
|
|
19
|
+
[dependencies]
|
|
20
|
+
# cross-gem-action は `directory: crates/fulgur-ruby` しか container にマウントしない
|
|
21
|
+
# ため、`path = "../../../fulgur"` では cargo がパスを解決できない。crates.io 公開版
|
|
22
|
+
# を参照する (release 時は release.yml → release: published → release-ruby.yml の順で
|
|
23
|
+
# 発火するため、この時点で fulgur のバージョンは crates.io に存在する)。
|
|
24
|
+
# release-prepare.yml が sed でこの行のバージョンを同期する。
|
|
25
|
+
fulgur = "0.5.0"
|
|
26
|
+
base64 = "0.22"
|
|
27
|
+
magnus = { version = "0.7", optional = true }
|
|
28
|
+
rb-sys = { version = "0.9", optional = true }
|
|
29
|
+
# 親 crate の src/lib.rs `mod assertions` が使用。
|
|
30
|
+
static_assertions = "1.1"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fulgur
|
|
4
|
+
class AssetBundle
|
|
5
|
+
alias_method :css, :add_css
|
|
6
|
+
alias_method :css_file, :add_css_file
|
|
7
|
+
alias_method :font_file, :add_font_file
|
|
8
|
+
alias_method :image, :add_image
|
|
9
|
+
alias_method :image_file, :add_image_file
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fulgur
|
|
4
|
+
class Margin
|
|
5
|
+
class << self
|
|
6
|
+
alias_method :__native_new__, :new
|
|
7
|
+
|
|
8
|
+
def new(*args, **kwargs)
|
|
9
|
+
unless kwargs.empty?
|
|
10
|
+
raise ArgumentError, "positional and kwargs are mutually exclusive" unless args.empty?
|
|
11
|
+
|
|
12
|
+
required = %i[top right bottom left]
|
|
13
|
+
missing = required - kwargs.keys
|
|
14
|
+
raise ArgumentError, "missing keys: #{missing.join(", ")}" unless missing.empty?
|
|
15
|
+
|
|
16
|
+
t, r, b, l = kwargs.values_at(*required).map(&:to_f)
|
|
17
|
+
return __build__(t, r, b, l)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
case args.length
|
|
21
|
+
when 1
|
|
22
|
+
v = args[0].to_f
|
|
23
|
+
__build__(v, v, v, v)
|
|
24
|
+
when 2
|
|
25
|
+
vv, hh = args.map(&:to_f)
|
|
26
|
+
__build__(vv, hh, vv, hh)
|
|
27
|
+
when 4
|
|
28
|
+
t, r, b, l = args.map(&:to_f)
|
|
29
|
+
__build__(t, r, b, l)
|
|
30
|
+
else
|
|
31
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 1, 2, 4, or kwargs)"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/fulgur.rb
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
#
|
|
5
|
-
# This is a placeholder gem. The implementation is under active development.
|
|
6
|
-
# See https://github.com/mitsuru/fulgur for details.
|
|
7
|
-
module Fulgur
|
|
8
|
-
VERSION = "0.0.1"
|
|
3
|
+
require_relative "fulgur/version"
|
|
9
4
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
begin
|
|
6
|
+
minor = RUBY_VERSION[/\A\d+\.\d+/]
|
|
7
|
+
require_relative "fulgur/#{minor}/fulgur"
|
|
8
|
+
rescue LoadError
|
|
9
|
+
require_relative "fulgur/fulgur"
|
|
15
10
|
end
|
|
11
|
+
|
|
12
|
+
module Fulgur
|
|
13
|
+
class Error < StandardError; end
|
|
14
|
+
class RenderError < Error; end
|
|
15
|
+
class AssetError < Error; end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
require_relative "fulgur/margin"
|
|
19
|
+
require_relative "fulgur/asset_bundle"
|
data/src/asset_bundle.rs
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
//! `Fulgur::AssetBundle` Ruby class wrapping `fulgur::AssetBundle`.
|
|
2
|
+
//!
|
|
3
|
+
//! Ruby は GVL 下で single-threaded なので `RefCell` による内部可変で十分。
|
|
4
|
+
//! Engine builder に渡すときは `take_inner()` で中身を奪い、空の
|
|
5
|
+
//! `AssetBundle::new()` に差し替える。
|
|
6
|
+
|
|
7
|
+
use crate::error::map_fulgur_error;
|
|
8
|
+
// `fulgur::AssetBundle` root re-export は 0.4.5 には存在しないため full path で参照。
|
|
9
|
+
use fulgur::asset::AssetBundle;
|
|
10
|
+
use magnus::{Error, Module, RModule, RString, Ruby, function, method, prelude::*};
|
|
11
|
+
use std::cell::RefCell;
|
|
12
|
+
use std::path::PathBuf;
|
|
13
|
+
|
|
14
|
+
#[magnus::wrap(class = "Fulgur::AssetBundle", free_immediately, size)]
|
|
15
|
+
pub struct RbAssetBundle {
|
|
16
|
+
pub(crate) inner: RefCell<AssetBundle>,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
impl RbAssetBundle {
|
|
20
|
+
pub(crate) fn new() -> Self {
|
|
21
|
+
Self {
|
|
22
|
+
inner: RefCell::new(AssetBundle::new()),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Engine builder に渡すために中身を取り出す。
|
|
27
|
+
/// 奪った後は empty AssetBundle に差し替える。
|
|
28
|
+
#[allow(dead_code)] // Task 6 で engine builder から呼ばれる
|
|
29
|
+
pub(crate) fn take_inner(&self) -> AssetBundle {
|
|
30
|
+
std::mem::replace(&mut *self.inner.borrow_mut(), AssetBundle::new())
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fn add_css(&self, css: String) {
|
|
34
|
+
self.inner.borrow_mut().add_css(css);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fn add_css_file(&self, path: String) -> Result<(), Error> {
|
|
38
|
+
let ruby = Ruby::get().expect("ruby vm");
|
|
39
|
+
self.inner
|
|
40
|
+
.borrow_mut()
|
|
41
|
+
.add_css_file(PathBuf::from(path))
|
|
42
|
+
.map_err(|e| map_fulgur_error(&ruby, e))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fn add_font_file(&self, path: String) -> Result<(), Error> {
|
|
46
|
+
let ruby = Ruby::get().expect("ruby vm");
|
|
47
|
+
self.inner
|
|
48
|
+
.borrow_mut()
|
|
49
|
+
.add_font_file(PathBuf::from(path))
|
|
50
|
+
.map_err(|e| map_fulgur_error(&ruby, e))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fn add_image(&self, name: String, data: RString) {
|
|
54
|
+
// SAFETY: `as_slice` returns a reference tied to the Ruby VM lifetime;
|
|
55
|
+
// we immediately copy into an owned `Vec<u8>` so the borrow ends here.
|
|
56
|
+
let bytes = unsafe { data.as_slice() }.to_vec();
|
|
57
|
+
self.inner.borrow_mut().add_image(name, bytes);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fn add_image_file(&self, name: String, path: String) -> Result<(), Error> {
|
|
61
|
+
let ruby = Ruby::get().expect("ruby vm");
|
|
62
|
+
self.inner
|
|
63
|
+
.borrow_mut()
|
|
64
|
+
.add_image_file(name, PathBuf::from(path))
|
|
65
|
+
.map_err(|e| map_fulgur_error(&ruby, e))
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
pub fn define(_ruby: &Ruby, fulgur: &RModule) -> Result<(), Error> {
|
|
70
|
+
let class = fulgur.define_class("AssetBundle", magnus::class::object())?;
|
|
71
|
+
class.define_singleton_method("new", function!(RbAssetBundle::new, 0))?;
|
|
72
|
+
class.define_method("add_css", method!(RbAssetBundle::add_css, 1))?;
|
|
73
|
+
class.define_method("add_css_file", method!(RbAssetBundle::add_css_file, 1))?;
|
|
74
|
+
class.define_method("add_font_file", method!(RbAssetBundle::add_font_file, 1))?;
|
|
75
|
+
class.define_method("add_image", method!(RbAssetBundle::add_image, 2))?;
|
|
76
|
+
class.define_method("add_image_file", method!(RbAssetBundle::add_image_file, 2))?;
|
|
77
|
+
Ok(())
|
|
78
|
+
}
|