itsi-server 0.1.1 → 0.1.11
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.
Potentially problematic release.
This version of itsi-server might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Cargo.lock +2926 -0
- data/Cargo.toml +7 -0
- data/Rakefile +8 -1
- data/exe/itsi +119 -29
- data/ext/itsi_error/Cargo.toml +2 -0
- data/ext/itsi_error/src/from.rs +68 -0
- data/ext/itsi_error/src/lib.rs +13 -38
- data/ext/itsi_instrument_entry/Cargo.toml +15 -0
- data/ext/itsi_instrument_entry/src/lib.rs +31 -0
- data/ext/itsi_rb_helpers/Cargo.toml +2 -0
- data/ext/itsi_rb_helpers/src/heap_value.rs +121 -0
- data/ext/itsi_rb_helpers/src/lib.rs +112 -9
- data/ext/itsi_scheduler/Cargo.toml +24 -0
- data/ext/itsi_scheduler/extconf.rb +6 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
- data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
- data/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
- data/ext/itsi_scheduler/src/lib.rs +38 -0
- data/ext/itsi_server/Cargo.lock +2956 -0
- data/ext/itsi_server/Cargo.toml +25 -4
- data/ext/itsi_server/extconf.rb +1 -1
- data/ext/itsi_server/src/body_proxy/big_bytes.rs +104 -0
- data/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +122 -0
- data/ext/itsi_server/src/body_proxy/mod.rs +2 -0
- data/ext/itsi_server/src/env.rs +43 -0
- data/ext/itsi_server/src/lib.rs +136 -8
- data/ext/itsi_server/src/request/itsi_request.rs +258 -103
- data/ext/itsi_server/src/response/itsi_response.rs +357 -0
- data/ext/itsi_server/src/response/mod.rs +1 -0
- data/ext/itsi_server/src/server/bind.rs +65 -29
- data/ext/itsi_server/src/server/bind_protocol.rs +37 -0
- data/ext/itsi_server/src/server/io_stream.rs +104 -0
- data/ext/itsi_server/src/server/itsi_server.rs +245 -139
- data/ext/itsi_server/src/server/lifecycle_event.rs +9 -0
- data/ext/itsi_server/src/server/listener.rs +237 -137
- data/ext/itsi_server/src/server/mod.rs +7 -1
- data/ext/itsi_server/src/server/process_worker.rs +203 -0
- data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +260 -0
- data/ext/itsi_server/src/server/serve_strategy/mod.rs +27 -0
- data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +276 -0
- data/ext/itsi_server/src/server/signal.rs +74 -0
- data/ext/itsi_server/src/server/thread_worker.rs +399 -0
- data/ext/itsi_server/src/server/tls/locked_dir_cache.rs +132 -0
- data/ext/itsi_server/src/server/tls.rs +187 -60
- data/ext/itsi_tracing/Cargo.toml +4 -0
- data/ext/itsi_tracing/src/lib.rs +53 -6
- data/lib/itsi/index.html +91 -0
- data/lib/itsi/request.rb +38 -14
- data/lib/itsi/server/Itsi.rb +127 -0
- data/lib/itsi/server/config.rb +36 -0
- data/lib/itsi/server/options_dsl.rb +401 -0
- data/lib/itsi/server/rack/handler/itsi.rb +36 -0
- data/lib/itsi/server/rack_interface.rb +75 -0
- data/lib/itsi/server/scheduler_interface.rb +21 -0
- data/lib/itsi/server/scheduler_mode.rb +6 -0
- data/lib/itsi/server/signal_trap.rb +23 -0
- data/lib/itsi/server/version.rb +1 -1
- data/lib/itsi/server.rb +79 -9
- data/lib/itsi/stream_io.rb +38 -0
- metadata +49 -27
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +0 -32
- data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +0 -52
- data/ext/itsi_server/src/server/transfer_protocol.rs +0 -23
- data/ext/itsi_server/src/stream_writer/mod.rs +0 -21
data/Cargo.toml
ADDED
data/Rakefile
CHANGED
@@ -3,7 +3,14 @@
|
|
3
3
|
require "bundler/gem_tasks"
|
4
4
|
require "minitest/test_task"
|
5
5
|
|
6
|
-
|
6
|
+
|
7
|
+
Minitest::TestTask.create(:test) do |t|
|
8
|
+
t.libs << 'test'
|
9
|
+
t.libs << 'lib'
|
10
|
+
t.warning = false
|
11
|
+
t.test_globs = ['test/**/*.rb']
|
12
|
+
t.test_prelude = 'require "helpers/test_helper.rb"'
|
13
|
+
end
|
7
14
|
|
8
15
|
require "rubocop/rake_task"
|
9
16
|
|
data/exe/itsi
CHANGED
@@ -2,26 +2,38 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "optparse"
|
5
|
-
require "rack"
|
6
|
-
require "etc"
|
7
5
|
|
8
6
|
# Default options used when starting Osprey from the CLI using `osprey`
|
9
7
|
DEFAULT_OPTIONS = {
|
10
8
|
# Number of workers
|
11
|
-
workers:
|
9
|
+
workers: 1,
|
12
10
|
# Number of threads per worker
|
13
11
|
threads: 1,
|
14
12
|
# Graceful shutdown timeout
|
15
|
-
shutdown_timeout:
|
13
|
+
shutdown_timeout: 5,
|
16
14
|
# Binds
|
17
|
-
binds: [
|
15
|
+
binds: ["http://0.0.0.0:3000"],
|
16
|
+
# Preload
|
17
|
+
preload: true,
|
18
|
+
# Rackup file
|
19
|
+
rackup_file: "config.ru",
|
20
|
+
# Scheduler class
|
21
|
+
scheduler_class: nil,
|
22
|
+
# Whether to stream the body or not
|
23
|
+
stream_body: false,
|
24
|
+
# Config file
|
25
|
+
config_file: nil
|
18
26
|
}
|
19
27
|
|
20
28
|
options = DEFAULT_OPTIONS.to_a.select(&:last).to_h
|
21
29
|
|
22
30
|
# Define the option parser
|
23
31
|
OptionParser.new do |opts|
|
24
|
-
opts.banner = "Usage:
|
32
|
+
opts.banner = "Usage: itsi [options]"
|
33
|
+
|
34
|
+
opts.on("-C", "--config CONFIG_FILE", String, "Itsi Configuration file to use (default: Itsi.rb)") do |config_file|
|
35
|
+
options[:config_file] = config_file
|
36
|
+
end
|
25
37
|
|
26
38
|
opts.on("-w", "--workers WORKERS", Integer, "Number of workers (default: #{options[:workers]})") do |w|
|
27
39
|
options[:workers] = w
|
@@ -31,29 +43,68 @@ OptionParser.new do |opts|
|
|
31
43
|
options[:threads] = t
|
32
44
|
end
|
33
45
|
|
34
|
-
opts.on("-
|
35
|
-
options[:
|
46
|
+
opts.on("-r", "--rackup_file FILE", String, "Rackup file to use (default: #{options[:rackup_file]})") do |rf|
|
47
|
+
options[:rackup_file] = rf
|
36
48
|
end
|
37
49
|
|
38
|
-
opts.on("-
|
39
|
-
|
50
|
+
opts.on("--worker-memory-limit MEMORY_LIMIT", Integer,
|
51
|
+
"Memory limit for each worker (default: #{options[:worker_memory_limit] || "None"}). If this limit is breached the worker is gracefully restarted") do |ml|
|
52
|
+
options[:worker_memory_limit] = ml
|
40
53
|
end
|
41
54
|
|
42
|
-
opts.on("-f", "--
|
43
|
-
"
|
44
|
-
|
55
|
+
opts.on("-f", "--fiber_scheduler [CLASS_NAME]", [String],
|
56
|
+
"Scheduler class to use (default: nil). Provide blank or true to use Itsi::Scheduler, or a classname to use an alternative scheduler") do |scheduler_class|
|
57
|
+
if scheduler_class.nil? || scheduler_class == "true"
|
58
|
+
options[:scheduler_class] = "Itsi::Scheduler"
|
59
|
+
elsif scheduler_class == "false"
|
60
|
+
options.delete(:scheduler_class)
|
61
|
+
else
|
62
|
+
options[:scheduler_class] = scheduler_class
|
63
|
+
end
|
45
64
|
end
|
46
65
|
|
47
|
-
opts.on("--
|
48
|
-
|
66
|
+
opts.on("--preload [true, false, :bundle_group_name]", String, " Toggle preloading the application") do |preload|
|
67
|
+
if preload == "true"
|
68
|
+
options[:preload] = true
|
69
|
+
elsif preload == "false"
|
70
|
+
options[:preload] = false
|
71
|
+
else
|
72
|
+
# Not supported yet
|
73
|
+
end
|
49
74
|
end
|
50
75
|
|
51
|
-
opts.on("-
|
52
|
-
|
76
|
+
opts.on("-b", "--bind BIND", String,
|
77
|
+
"Bind address (default: #{options[:binds].join(", ")}). You can specify this flag multiple times to bind to multiple addresses.") do |bind|
|
78
|
+
options[:binds].pop if options[:binds].first.frozen?
|
79
|
+
options[:binds] << bind
|
53
80
|
end
|
54
81
|
|
55
|
-
opts.on("-
|
56
|
-
|
82
|
+
opts.on("-c", "--cert_path CERT_PATH", String,
|
83
|
+
"Path to the SSL certificate file (must follow a --bind option). You can specify this flag multiple times.") do |cp|
|
84
|
+
raise OptionParser::InvalidOption, "--cert_path must follow a --bind" if options[:binds].empty?
|
85
|
+
|
86
|
+
require "uri"
|
87
|
+
|
88
|
+
# Modify the last bind entry to add/update the cert query parameter
|
89
|
+
uri = URI.parse("http://#{options[:binds].last}") # Ensure valid URI parsing
|
90
|
+
params = URI.decode_www_form(uri.query.to_s).to_h
|
91
|
+
params["cert"] = cp
|
92
|
+
query_string = params.map { |k, v| "#{k}=#{v}" }.join("&")
|
93
|
+
options[:binds][-1] = "#{uri.host}?#{query_string}"
|
94
|
+
end
|
95
|
+
|
96
|
+
opts.on("-k", "--key_path KEY_PATH", String,
|
97
|
+
"Path to the SSL key file (must follow a --bind option). You can specify this flag multiple times.") do |kp|
|
98
|
+
raise OptionParser::InvalidOption, "--key_path must follow a --bind" if options[:binds].empty?
|
99
|
+
|
100
|
+
require "uri"
|
101
|
+
|
102
|
+
# Modify the last bind entry to add/update the key query parameter
|
103
|
+
uri = URI.parse("http://#{options[:binds].last}") # Ensure valid URI parsing
|
104
|
+
params = URI.decode_www_form(uri.query.to_s).to_h
|
105
|
+
params["key"] = kp
|
106
|
+
query_string = params.map { |k, v| "#{k}=#{v}" }.join("&")
|
107
|
+
options[:binds][-1] = "#{uri.host}?#{query_string}"
|
57
108
|
end
|
58
109
|
|
59
110
|
opts.on("--shutdown_timeout SHUTDOWN_TIMEOUT", String,
|
@@ -65,20 +116,59 @@ OptionParser.new do |opts|
|
|
65
116
|
options[:script_name] = script_name
|
66
117
|
end
|
67
118
|
|
68
|
-
opts.on("--
|
119
|
+
opts.on("--stream-body", TrueClass, "Stream body frames (default: false for best compatibility)") do |stream_body|
|
120
|
+
options[:stream_body] = stream_body
|
121
|
+
end
|
122
|
+
|
123
|
+
opts.on("-h", "--help", "Show this help message") do
|
69
124
|
puts opts
|
70
125
|
exit
|
71
126
|
end
|
72
127
|
end.parse!
|
73
128
|
|
74
|
-
|
75
|
-
|
129
|
+
require "itsi/server/config"
|
130
|
+
if ARGV.pop == "init"
|
131
|
+
Itsi::Server::Config.write_default
|
132
|
+
exit(0)
|
133
|
+
# Add initialization code here
|
134
|
+
end
|
135
|
+
|
136
|
+
options = Itsi::Server::Config.load(options)
|
137
|
+
loader = nil
|
138
|
+
|
139
|
+
if options[:app] || File.exist?(options[:rackup_file])
|
140
|
+
loader ||= lambda do
|
141
|
+
require "rack"
|
142
|
+
require_relative "../lib/itsi/server/scheduler_mode" if options[:scheduler_class]
|
143
|
+
require "itsi/scheduler" if [true, "Itsi::Scheduler"].include?(options[:scheduler_class])
|
144
|
+
require "itsi/server"
|
145
|
+
return options[:app] if options[:app]
|
146
|
+
Array(Rack::Builder.parse_file(options[:rackup_file])).first
|
147
|
+
end
|
76
148
|
|
77
|
-
|
78
|
-
#
|
79
|
-
#
|
149
|
+
if options[:preload] == true
|
150
|
+
# If preload is true, we'll invoke our loader now, and just return a no-op proc
|
151
|
+
# which returns the app as loader. We still need to optionally load the scheduler class and rack
|
152
|
+
# in case the app has been provided directly in Itsi.rb instead of in config.ru
|
153
|
+
require "rack"
|
154
|
+
require_relative "../lib/itsi/server/scheduler_mode" if options[:scheduler_class]
|
155
|
+
require "itsi/scheduler" if [true, "Itsi::Scheduler"].include?(options[:scheduler_class])
|
156
|
+
loader = (options[:app] || loader[]).method(:itself).to_proc
|
157
|
+
elsif options[:preload]
|
158
|
+
# If a group name is given, we'll stick to just loading the gem group by the same name from the Gemfile.
|
159
|
+
# But we'll fall through to the default delayed loader for the full app.
|
160
|
+
require "bundler"
|
161
|
+
Bundler.setup
|
162
|
+
Bundler.require(options[:preload])
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Make sure Itsi is loaded, if not already loaded by the rack_app above.
|
168
|
+
# Start the Itsi server
|
80
169
|
require "itsi/server"
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
)
|
170
|
+
|
171
|
+
Itsi::Server.start(
|
172
|
+
loader: loader,
|
173
|
+
**options.except(:preload, :rackup_file)
|
174
|
+
)
|
data/ext/itsi_error/Cargo.toml
CHANGED
@@ -0,0 +1,68 @@
|
|
1
|
+
use magnus::{
|
2
|
+
Error,
|
3
|
+
error::ErrorType,
|
4
|
+
exception::{self, arg_error, exception},
|
5
|
+
};
|
6
|
+
use nix::errno::Errno;
|
7
|
+
|
8
|
+
use crate::ItsiError;
|
9
|
+
use std::{ffi::NulError, io};
|
10
|
+
|
11
|
+
pub static CLIENT_CONNECTION_CLOSED: &str = "Client disconnected";
|
12
|
+
|
13
|
+
impl From<httparse::Error> for ItsiError {
|
14
|
+
fn from(err: httparse::Error) -> Self {
|
15
|
+
ItsiError::ArgumentError(err.to_string())
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
impl From<Errno> for ItsiError {
|
20
|
+
fn from(err: Errno) -> Self {
|
21
|
+
ItsiError::ArgumentError(err.to_string())
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
impl From<io::Error> for ItsiError {
|
26
|
+
fn from(err: io::Error) -> Self {
|
27
|
+
ItsiError::ArgumentError(err.to_string())
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
impl From<rcgen::Error> for ItsiError {
|
32
|
+
fn from(err: rcgen::Error) -> Self {
|
33
|
+
ItsiError::ArgumentError(err.to_string())
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
impl From<NulError> for ItsiError {
|
38
|
+
fn from(err: NulError) -> Self {
|
39
|
+
ItsiError::ArgumentError(err.to_string())
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
impl From<Error> for ItsiError {
|
44
|
+
fn from(err: Error) -> Self {
|
45
|
+
match err.error_type() {
|
46
|
+
ErrorType::Jump(tag) => ItsiError::Jump(tag.to_string()),
|
47
|
+
ErrorType::Error(_exception_class, cow) => ItsiError::ArgumentError(cow.to_string()),
|
48
|
+
ErrorType::Exception(exception) => ItsiError::ArgumentError(exception.to_string()),
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
impl From<ItsiError> for Error {
|
54
|
+
fn from(err: ItsiError) -> Self {
|
55
|
+
match err {
|
56
|
+
ItsiError::InvalidInput(msg) => Error::new(arg_error(), msg),
|
57
|
+
ItsiError::InternalServerError(msg) => Error::new(exception(), msg),
|
58
|
+
ItsiError::UnsupportedProtocol(msg) => Error::new(arg_error(), msg),
|
59
|
+
ItsiError::ArgumentError(msg) => Error::new(arg_error(), msg),
|
60
|
+
ItsiError::Jump(msg) => Error::new(exception::local_jump_error(), msg),
|
61
|
+
ItsiError::Break() => Error::new(exception::interrupt(), "Break"),
|
62
|
+
ItsiError::ClientConnectionClosed => {
|
63
|
+
Error::new(exception::eof_error(), CLIENT_CONNECTION_CLOSED)
|
64
|
+
}
|
65
|
+
ItsiError::Pass() => Error::new(exception::interrupt(), "Pass"),
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
data/ext/itsi_error/src/lib.rs
CHANGED
@@ -1,49 +1,24 @@
|
|
1
|
+
pub mod from;
|
1
2
|
use thiserror::Error;
|
2
3
|
|
3
4
|
pub type Result<T> = std::result::Result<T, ItsiError>;
|
4
5
|
|
5
|
-
#[derive(Error, Debug)]
|
6
|
+
#[derive(Error, Debug, Clone)]
|
6
7
|
pub enum ItsiError {
|
7
|
-
#[error("Invalid input")]
|
8
|
+
#[error("Invalid input {0}")]
|
8
9
|
InvalidInput(String),
|
9
|
-
#[error("Internal server error")]
|
10
|
-
InternalServerError,
|
11
|
-
#[error("Unsupported protocol")]
|
10
|
+
#[error("Internal server error {0}")]
|
11
|
+
InternalServerError(String),
|
12
|
+
#[error("Unsupported protocol {0}")]
|
12
13
|
UnsupportedProtocol(String),
|
13
|
-
#[error("Argument error")]
|
14
|
+
#[error("Argument error: {0}")]
|
14
15
|
ArgumentError(String),
|
16
|
+
#[error("Client Connection Closed")]
|
17
|
+
ClientConnectionClosed,
|
15
18
|
#[error("Jump")]
|
16
19
|
Jump(String),
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
magnus::Error::new(magnus::exception::runtime_error(), err.to_string())
|
22
|
-
}
|
23
|
-
}
|
24
|
-
|
25
|
-
impl From<std::io::Error> for ItsiError {
|
26
|
-
fn from(err: std::io::Error) -> Self {
|
27
|
-
ItsiError::ArgumentError(err.to_string())
|
28
|
-
}
|
29
|
-
}
|
30
|
-
|
31
|
-
impl From<rcgen::Error> for ItsiError {
|
32
|
-
fn from(err: rcgen::Error) -> Self {
|
33
|
-
ItsiError::ArgumentError(err.to_string())
|
34
|
-
}
|
35
|
-
}
|
36
|
-
|
37
|
-
impl From<magnus::Error> for ItsiError {
|
38
|
-
fn from(err: magnus::Error) -> Self {
|
39
|
-
match err.error_type() {
|
40
|
-
magnus::error::ErrorType::Jump(tag) => ItsiError::Jump(tag.to_string()),
|
41
|
-
magnus::error::ErrorType::Error(_exception_class, cow) => {
|
42
|
-
ItsiError::ArgumentError(cow.to_string())
|
43
|
-
}
|
44
|
-
magnus::error::ErrorType::Exception(exception) => {
|
45
|
-
ItsiError::ArgumentError(exception.to_string())
|
46
|
-
}
|
47
|
-
}
|
48
|
-
}
|
20
|
+
#[error("Break")]
|
21
|
+
Break(),
|
22
|
+
#[error("Pass")]
|
23
|
+
Pass(),
|
49
24
|
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
[package]
|
2
|
+
name = "itsi_instrument_entry"
|
3
|
+
version = "0.1.0"
|
4
|
+
edition = "2021"
|
5
|
+
authors = ["Wouter Coppieters <wc@pico.net.nz>"]
|
6
|
+
license = "MIT"
|
7
|
+
publish = false
|
8
|
+
|
9
|
+
|
10
|
+
[lib]
|
11
|
+
proc-macro = true
|
12
|
+
[dependencies]
|
13
|
+
proc-macro2 = "1.0"
|
14
|
+
quote = "1.0"
|
15
|
+
syn = { version = "1.0", features = ["full"] }
|
@@ -0,0 +1,31 @@
|
|
1
|
+
use proc_macro::TokenStream;
|
2
|
+
use proc_macro2::TokenStream as TokenStream2;
|
3
|
+
use quote::quote;
|
4
|
+
use syn::{parse_macro_input, ItemFn};
|
5
|
+
|
6
|
+
#[proc_macro_attribute]
|
7
|
+
pub fn instrument_with_entry(attr: TokenStream, item: TokenStream) -> TokenStream {
|
8
|
+
let attr_tokens = TokenStream2::from(attr);
|
9
|
+
let input_fn = parse_macro_input!(item as ItemFn);
|
10
|
+
let attrs = input_fn.attrs;
|
11
|
+
let vis = input_fn.vis;
|
12
|
+
let sig = input_fn.sig;
|
13
|
+
let block = input_fn.block;
|
14
|
+
let output = quote! {
|
15
|
+
#[cfg(debug_assertions)]
|
16
|
+
#[tracing::instrument(#attr_tokens)]
|
17
|
+
#(#attrs)*
|
18
|
+
#vis #sig {
|
19
|
+
tracing::trace!("");
|
20
|
+
#block
|
21
|
+
}
|
22
|
+
|
23
|
+
#[cfg(not(debug_assertions))]
|
24
|
+
#(#attrs)*
|
25
|
+
#vis #sig {
|
26
|
+
#block
|
27
|
+
}
|
28
|
+
};
|
29
|
+
|
30
|
+
output.into()
|
31
|
+
}
|
@@ -0,0 +1,121 @@
|
|
1
|
+
use magnus::IntoValue;
|
2
|
+
use magnus::rb_sys::AsRawValue;
|
3
|
+
use magnus::value::BoxValue;
|
4
|
+
use magnus::{Ruby, Value, value::ReprValue};
|
5
|
+
use std::fmt::{self, Debug, Formatter};
|
6
|
+
use std::ops::Deref;
|
7
|
+
|
8
|
+
/// HeapVal is a wrapper for heap-allocated magnus ReprVa;ies
|
9
|
+
/// that is marked as thread-safe(Send and Sync)
|
10
|
+
/// It's up to the user to actually ensure this though,
|
11
|
+
/// typically by only interacting with the value from a thread which
|
12
|
+
/// holds the GVL.
|
13
|
+
pub struct HeapValue<T>(pub BoxValue<T>)
|
14
|
+
where
|
15
|
+
T: ReprValue;
|
16
|
+
|
17
|
+
impl<T> PartialEq for HeapValue<T>
|
18
|
+
where
|
19
|
+
T: ReprValue,
|
20
|
+
{
|
21
|
+
fn eq(&self, other: &Self) -> bool {
|
22
|
+
self.0.as_raw() == other.0.as_raw()
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
impl<T> Deref for HeapValue<T>
|
27
|
+
where
|
28
|
+
T: ReprValue,
|
29
|
+
{
|
30
|
+
type Target = T;
|
31
|
+
|
32
|
+
fn deref(&self) -> &Self::Target {
|
33
|
+
&self.0
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
impl<T> HeapValue<T>
|
38
|
+
where
|
39
|
+
T: ReprValue,
|
40
|
+
{
|
41
|
+
pub fn inner(self) -> T {
|
42
|
+
*self.0
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
impl<T> IntoValue for HeapValue<T>
|
47
|
+
where
|
48
|
+
T: ReprValue,
|
49
|
+
{
|
50
|
+
fn into_value_with(self, _: &Ruby) -> Value {
|
51
|
+
self.0.into_value()
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
impl<T> From<T> for HeapValue<T>
|
56
|
+
where
|
57
|
+
T: ReprValue,
|
58
|
+
{
|
59
|
+
fn from(value: T) -> Self {
|
60
|
+
HeapValue(BoxValue::new(value))
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
impl<T> Clone for HeapValue<T>
|
65
|
+
where
|
66
|
+
T: ReprValue + Clone,
|
67
|
+
{
|
68
|
+
fn clone(&self) -> Self {
|
69
|
+
HeapValue(BoxValue::new(*self.0.deref()))
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
impl<T> Debug for HeapValue<T>
|
74
|
+
where
|
75
|
+
T: ReprValue + Debug,
|
76
|
+
{
|
77
|
+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
78
|
+
write!(f, "{:?}", self.0)
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
unsafe impl<T> Send for HeapValue<T> where T: ReprValue {}
|
83
|
+
unsafe impl<T> Sync for HeapValue<T> where T: ReprValue {}
|
84
|
+
|
85
|
+
/// HeapVal is a wrapper for heap-allocated magnus Values
|
86
|
+
/// that is marked as thread-safe(Send and Sync)
|
87
|
+
/// It's up to the user to actually ensure this though,
|
88
|
+
/// typically by only interacting with the value from a thread which
|
89
|
+
/// holds the GVL.
|
90
|
+
pub struct HeapVal(HeapValue<Value>);
|
91
|
+
impl Deref for HeapVal {
|
92
|
+
type Target = Value;
|
93
|
+
|
94
|
+
fn deref(&self) -> &Self::Target {
|
95
|
+
&self.0
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
impl IntoValue for HeapVal {
|
100
|
+
fn into_value_with(self, _: &Ruby) -> Value {
|
101
|
+
self.0.into_value()
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
impl From<Value> for HeapVal {
|
106
|
+
fn from(value: Value) -> Self {
|
107
|
+
HeapVal(HeapValue(BoxValue::new(value)))
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
impl Clone for HeapVal {
|
112
|
+
fn clone(&self) -> Self {
|
113
|
+
HeapVal(HeapValue(BoxValue::new(*self.0.deref())))
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
impl Debug for HeapVal {
|
118
|
+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
119
|
+
write!(f, "{:?}", self.0)
|
120
|
+
}
|
121
|
+
}
|