mirror of
git://git.proxmox.com/git/perlmod.git
synced 2025-01-01 01:17:40 +03:00
import
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
commit
f7cc8c37fc
5
.cargo/config
Normal file
5
.cargo/config
Normal file
@ -0,0 +1,5 @@
|
||||
[source]
|
||||
[source.debian-packages]
|
||||
directory = "/usr/share/cargo/registry"
|
||||
[source.crates-io]
|
||||
replace-with = "debian-packages"
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
/RSPM
|
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"perlmod",
|
||||
"perlmod-macro",
|
||||
"perlmod-test",
|
||||
]
|
14
perlmod-macro/Cargo.toml
Normal file
14
perlmod-macro/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "perlmod-macro"
|
||||
version = "0.1.0"
|
||||
authors = ["Wolfgang Bumiller <w.bumiller@proxmox.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
failure = "0.1"
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "1.0", features = [ "full" ] }
|
1
perlmod-macro/debian/cargo-checksum.json
Normal file
1
perlmod-macro/debian/cargo-checksum.json
Normal file
@ -0,0 +1 @@
|
||||
{"package":"proxmox-api-macro","files":{}}
|
5
perlmod-macro/debian/changelog
Normal file
5
perlmod-macro/debian/changelog
Normal file
@ -0,0 +1,5 @@
|
||||
rust-perlmod-macro (0.1.0) proxmox-rust; urgency=medium
|
||||
|
||||
* Initial packaging.
|
||||
|
||||
-- Proxmox Support Team <support@proxmox.com> Thu, 09 Jan 2020 13:24:33 +0100
|
1
perlmod-macro/debian/compat
Normal file
1
perlmod-macro/debian/compat
Normal file
@ -0,0 +1 @@
|
||||
12
|
33
perlmod-macro/debian/control
Normal file
33
perlmod-macro/debian/control
Normal file
@ -0,0 +1,33 @@
|
||||
Source: rust-perlmod-macro
|
||||
Section: rust
|
||||
Priority: optional
|
||||
Build-Depends: debhelper (>= 12),
|
||||
dh-cargo (>= 21~),
|
||||
cargo:native <!nocheck>,
|
||||
rustc:native <!nocheck>,
|
||||
libstd-rust-dev <!nocheck>,
|
||||
librust-failure-0.1+default-dev <!nocheck>,
|
||||
librust-proc-macro2-1.0+default-dev <!nocheck>,
|
||||
librust-quote-1.0+default-dev <!nocheck>,
|
||||
librust-syn-1.0+full-dev <!nocheck>,
|
||||
Maintainer: Proxmox Support Team <support@proxmox.com>
|
||||
Standards-Version: 4.4.1
|
||||
|
||||
Package: librust-proxmox-api-macro-dev
|
||||
Architecture: any
|
||||
Depends:
|
||||
${misc:Depends},
|
||||
librust-failure-0.1+default-dev,
|
||||
librust-proc-macro2-1.0+default-dev,
|
||||
librust-quote-1.0+default-dev,
|
||||
librust-syn-1.0+full-dev,
|
||||
Provides:
|
||||
librust-perlmod-macro+default-dev (= ${binary:Version}),
|
||||
librust-perlmod-macro-0-dev (= ${binary:Version}),
|
||||
librust-perlmod-macro-0+default-dev (= ${binary:Version}),
|
||||
librust-perlmod-macro-0.1-dev (= ${binary:Version}),
|
||||
librust-perlmod-macro-0.1+default-dev (= ${binary:Version}),
|
||||
librust-perlmod-macro-0.1.0-dev (= ${binary:Version}),
|
||||
librust-perlmod-macro-0.1.0+default-dev (= ${binary:Version}),
|
||||
Description: Macro to create perl modules to exported rust functions. - Rust source code
|
||||
This package contains the source for the Rust perlmod crate.
|
16
perlmod-macro/debian/copyright
Normal file
16
perlmod-macro/debian/copyright
Normal file
@ -0,0 +1,16 @@
|
||||
Copyright (C) 2020 Proxmox Server Solutions GmbH
|
||||
|
||||
This software is written by Proxmox Server Solutions GmbH <support@proxmox.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
3
perlmod-macro/debian/rules
Executable file
3
perlmod-macro/debian/rules
Executable file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/make -f
|
||||
%:
|
||||
dh $@ --buildsystem cargo
|
330
perlmod-macro/src/lib.rs
Normal file
330
perlmod-macro/src/lib.rs
Normal file
@ -0,0 +1,330 @@
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use failure::Error;
|
||||
|
||||
use proc_macro::TokenStream as TokenStream_1;
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
|
||||
use quote::quote;
|
||||
use syn::parse_macro_input;
|
||||
use syn::AttributeArgs;
|
||||
|
||||
macro_rules! format_err {
|
||||
($span:expr => $($msg:tt)*) => { syn::Error::new_spanned($span, format!($($msg)*)) };
|
||||
($span:expr, $($msg:tt)*) => { syn::Error::new($span, format!($($msg)*)) };
|
||||
}
|
||||
|
||||
macro_rules! bail {
|
||||
($span:expr => $($msg:tt)*) => { return Err(format_err!($span => $($msg)*).into()) };
|
||||
($span:expr, $($msg:tt)*) => { return Err(format_err!($span, $($msg)*).into()) };
|
||||
}
|
||||
|
||||
fn handle_error(mut item: TokenStream, data: Result<TokenStream, Error>) -> TokenStream {
|
||||
match data {
|
||||
Ok(output) => output,
|
||||
Err(err) => match err.downcast::<syn::Error>() {
|
||||
Ok(err) => {
|
||||
item.extend(err.to_compile_error());
|
||||
item
|
||||
}
|
||||
Err(err) => panic!("error in api/router macro: {}", err),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
struct XSub {
|
||||
rust_name: Ident,
|
||||
xs_name: Ident,
|
||||
tokens: TokenStream,
|
||||
}
|
||||
|
||||
/// Macro for exporting rust functions as perl xsubs.
|
||||
#[proc_macro_attribute]
|
||||
pub fn package(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 {
|
||||
let attr = parse_macro_input!(attr as AttributeArgs);
|
||||
let item: TokenStream = item.into();
|
||||
handle_error(item.clone(), perlmod_impl(attr, item)).into()
|
||||
}
|
||||
|
||||
fn perlmod_impl(attr: AttributeArgs, item: TokenStream) -> Result<TokenStream, Error> {
|
||||
let item: syn::Item = syn::parse2(item)?;
|
||||
|
||||
match item {
|
||||
syn::Item::Fn(func) => {
|
||||
let func = handle_function(func)?;
|
||||
Ok(func.tokens)
|
||||
}
|
||||
syn::Item::Mod(module) => handle_module(attr, module),
|
||||
_ => bail!(item => "expected module or function"),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_function(func: syn::ItemFn) -> Result<XSub, Error> {
|
||||
//let vis = core::mem::replace(&mut func.vis, syn::Visibility::Inherited);
|
||||
//if let syn::Visibility::Public(_) = vis {
|
||||
// // ok
|
||||
//} else {
|
||||
// bail!(func.sig.fn_token => "only public functions can be exported as xsubs");
|
||||
//}
|
||||
|
||||
let sig = &func.sig;
|
||||
if !sig.generics.params.is_empty() {
|
||||
bail!(&sig.generics => "generic functions cannot be exported as xsubs");
|
||||
}
|
||||
|
||||
if sig.asyncness.is_some() {
|
||||
bail!(&sig.asyncness => "async fns cannot be exported as xsubs");
|
||||
}
|
||||
|
||||
let name = &sig.ident;
|
||||
let xs_name = Ident::new(&format!("xs_{}", name), name.span());
|
||||
let impl_xs_name = Ident::new(&format!("impl_xs_{}", name), name.span());
|
||||
|
||||
let mut extract_arguments = TokenStream::new();
|
||||
let mut deserialized_arguments = TokenStream::new();
|
||||
let mut passed_arguments = TokenStream::new();
|
||||
for arg in &sig.inputs {
|
||||
let pat_ty = match arg {
|
||||
syn::FnArg::Receiver(_) => bail!(arg => "cannot export self-taking methods as xsubs"),
|
||||
syn::FnArg::Typed(pt) => pt,
|
||||
};
|
||||
|
||||
let arg_name = match &*pat_ty.pat {
|
||||
syn::Pat::Ident(ident) => {
|
||||
if ident.by_ref.is_some() {
|
||||
bail!(ident => "xsub does not support by-ref parameters");
|
||||
}
|
||||
if ident.subpat.is_some() {
|
||||
bail!(ident => "xsub does not support sub-patterns on parameters");
|
||||
}
|
||||
&ident.ident
|
||||
}
|
||||
_ => bail!(&pat_ty.pat => "xsub does not support this kind of parameter"),
|
||||
};
|
||||
|
||||
let arg_type = &*pat_ty.ty;
|
||||
|
||||
let extracted_name = Ident::new(&format!("extracted_arg_{}", arg_name), arg_name.span());
|
||||
let deserialized_name =
|
||||
Ident::new(&format!("deserialized_arg_{}", arg_name), arg_name.span());
|
||||
|
||||
let missing_message = syn::LitStr::new("missing required parameter: '{}'", arg_name.span());
|
||||
|
||||
extract_arguments.extend(quote! {
|
||||
let #extracted_name: ::perlmod::Value = match args.next() {
|
||||
Some(arg) => ::perlmod::Value::from(arg),
|
||||
None => {
|
||||
return Err(::perlmod::Value::new_string(#missing_message)
|
||||
.into_mortal()
|
||||
.into_raw());
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
deserialized_arguments.extend(quote! {
|
||||
let #deserialized_name: #arg_type = match ::perlmod::from_value(#extracted_name) {
|
||||
Ok(data) => data,
|
||||
Err(err) => {
|
||||
return Err(::perlmod::Value::new_string(&err.to_string())
|
||||
.into_mortal()
|
||||
.into_raw());
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
if passed_arguments.is_empty() {
|
||||
passed_arguments.extend(quote! { #deserialized_name });
|
||||
} else {
|
||||
passed_arguments.extend(quote! {, #deserialized_name });
|
||||
}
|
||||
}
|
||||
|
||||
let tokens = quote! {
|
||||
#func
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn #xs_name(cv: &::perlmod::ffi::CV) {
|
||||
unsafe {
|
||||
match #impl_xs_name(cv) {
|
||||
Ok(sv) => ::perlmod::ffi::stack_push_raw(sv),
|
||||
Err(sv) => ::perlmod::ffi::croak(sv),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn #impl_xs_name(
|
||||
_cv: &::perlmod::ffi::CV,
|
||||
) -> Result<*mut ::perlmod::ffi::SV, *mut ::perlmod::ffi::SV> {
|
||||
let argmark = unsafe { ::perlmod::ffi::pop_arg_mark() };
|
||||
let mut args = argmark.iter();
|
||||
|
||||
#extract_arguments
|
||||
|
||||
drop(args);
|
||||
|
||||
#deserialized_arguments
|
||||
|
||||
unsafe {
|
||||
argmark.set_stack();
|
||||
}
|
||||
|
||||
let result = match #name(#passed_arguments) {
|
||||
Ok(output) => output,
|
||||
Err(err) => {
|
||||
return Err(::perlmod::Value::new_string(&err.to_string())
|
||||
.into_mortal()
|
||||
.into_raw());
|
||||
}
|
||||
};
|
||||
|
||||
match ::perlmod::to_value(&result) {
|
||||
Ok(value) => Ok(value.into_mortal().into_raw()),
|
||||
Err(err) => Err(::perlmod::Value::new_string(&err.to_string())
|
||||
.into_mortal()
|
||||
.into_raw()),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(XSub {
|
||||
rust_name: name.to_owned(),
|
||||
xs_name,
|
||||
tokens,
|
||||
})
|
||||
}
|
||||
|
||||
const LIB_NAME_DEFAULT: &str = r#"($pkg =~ /(?:^|::)([^:]+)$/)"#;
|
||||
|
||||
const MODULE_HEAD: &str = r#"
|
||||
use strict;
|
||||
use warnings;
|
||||
use DynaLoader ();
|
||||
|
||||
my $LIB;
|
||||
|
||||
sub __load_shared_lib {
|
||||
return if $LIB;
|
||||
|
||||
my ($pkg) = @_;
|
||||
|
||||
my $auto_path = ($pkg =~ s!::!/!gr);
|
||||
my ($mod_name) = {{LIB_NAME}};
|
||||
|
||||
my @dirs = (map "-L$_/auto/$auto_path", @INC);
|
||||
my (@mod_files) = DynaLoader::dl_findfile(@dirs, '-L./target/debug', $mod_name);
|
||||
die "failed to locate shared library for '$pkg' (lib${mod_name}.so)\n" if !@mod_files;
|
||||
|
||||
$LIB = DynaLoader::dl_load_file($mod_files[0])
|
||||
or die "failed to load library '$mod_files[0]'\n";
|
||||
}
|
||||
|
||||
sub newXS {
|
||||
my ($perl_func_name, $full_symbol_name, $filename) = @_;
|
||||
|
||||
my $sym = DynaLoader::dl_find_symbol($LIB, $full_symbol_name);
|
||||
die "failed to locate '$full_symbol_name'\n" if !defined $sym;
|
||||
DynaLoader::dl_install_xsub($perl_func_name, $sym, $filename);
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
__load_shared_lib(__PACKAGE__);
|
||||
"#;
|
||||
|
||||
const MODULE_TAIL: &str = "}\n";
|
||||
|
||||
struct ModuleArgs {
|
||||
package_name: String,
|
||||
file_name: String,
|
||||
lib_name: Option<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<AttributeArgs> for ModuleArgs {
|
||||
type Error = syn::Error;
|
||||
|
||||
fn try_from(args: AttributeArgs) -> Result<Self, Self::Error> {
|
||||
let mut package_name = None;
|
||||
let mut file_name = None;
|
||||
let mut lib_name = None;
|
||||
|
||||
for arg in args {
|
||||
match arg {
|
||||
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
|
||||
path,
|
||||
eq_token: _,
|
||||
lit: syn::Lit::Str(litstr),
|
||||
})) => {
|
||||
if path.is_ident("name") {
|
||||
package_name = Some(litstr.value());
|
||||
} else if path.is_ident("file") {
|
||||
file_name = Some(litstr.value());
|
||||
} else if path.is_ident("lib") {
|
||||
lib_name = Some(litstr.value());
|
||||
} else {
|
||||
bail!(path => "unknown argument");
|
||||
}
|
||||
}
|
||||
_ => bail!(Span::call_site(), "unexpected attribute argument"),
|
||||
}
|
||||
}
|
||||
|
||||
let package_name = package_name
|
||||
.ok_or_else(|| format_err!(Span::call_site(), "missing 'package' argument"))?;
|
||||
|
||||
let file_name =
|
||||
file_name.unwrap_or_else(|| format!("{}.pm", package_name.replace("::", "/")));
|
||||
|
||||
Ok(Self {
|
||||
package_name,
|
||||
file_name,
|
||||
lib_name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_module(attr: AttributeArgs, mut module: syn::ItemMod) -> Result<TokenStream, Error> {
|
||||
let args = ModuleArgs::try_from(attr)?;
|
||||
|
||||
let mut module_source = format!("package {};\n{}", args.package_name, MODULE_HEAD);
|
||||
|
||||
if let Some((_brace, ref mut items)) = module.content {
|
||||
for item in items.iter_mut() {
|
||||
match core::mem::replace(item, syn::Item::Verbatim(TokenStream::new())) {
|
||||
syn::Item::Fn(mut func) => {
|
||||
let count = func.attrs.len();
|
||||
func.attrs.retain(|attr| !attr.path.is_ident("export"));
|
||||
// if we removed an #[export] macro this is an exported function:
|
||||
if count != func.attrs.len() {
|
||||
let func = handle_function(func)?;
|
||||
*item = syn::Item::Verbatim(func.tokens);
|
||||
module_source = format!(
|
||||
"{} newXS('{}', '{}', 'src/FIXME.rs');\n",
|
||||
module_source, func.rust_name, func.xs_name,
|
||||
);
|
||||
} else {
|
||||
*item = syn::Item::Fn(func);
|
||||
}
|
||||
}
|
||||
other => *item = other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module_source.push_str(MODULE_TAIL);
|
||||
|
||||
if let Some(lib) = args.lib_name {
|
||||
module_source = module_source.replace("{{LIB_NAME}}", &format!("('{}')", lib));
|
||||
} else {
|
||||
module_source = module_source.replace("{{LIB_NAME}}", LIB_NAME_DEFAULT);
|
||||
}
|
||||
|
||||
let path = std::path::Path::new(&args.file_name);
|
||||
if let Some(parent) = path.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
std::fs::write(path, module_source.as_bytes())?;
|
||||
|
||||
Ok(quote! { #module })
|
||||
}
|
12
perlmod-test/Cargo.toml
Normal file
12
perlmod-test/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "perlmod-test"
|
||||
version = "0.1.0"
|
||||
authors = ["Wolfgang Bumiller <w.bumiller@proxmox.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = [ "cdylib" ]
|
||||
|
||||
[dependencies]
|
||||
failure = "0.1"
|
||||
perlmod = { path = "../perlmod", features = [ "exporter" ] }
|
13
perlmod-test/src/lib.rs
Normal file
13
perlmod-test/src/lib.rs
Normal file
@ -0,0 +1,13 @@
|
||||
#[perlmod::package(name = "RSPM::Foo", lib = "perlmod_test")]
|
||||
mod export {
|
||||
use failure::{bail, Error};
|
||||
|
||||
#[export]
|
||||
fn foo(a: u32, b: u32) -> Result<u32, Error> {
|
||||
if a == 42 {
|
||||
bail!("dying on magic number");
|
||||
}
|
||||
|
||||
Ok(a + b)
|
||||
}
|
||||
}
|
20
perlmod/Cargo.toml
Normal file
20
perlmod/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "perlmod"
|
||||
version = "0.1.0"
|
||||
authors = ["Wolfgang Bumiller <w.bumiller@proxmox.com>"]
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.2.1"
|
||||
failure = "0.1"
|
||||
libc = "0.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
perlmod-macro = { path = "../perlmod-macro", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["exporter"]
|
||||
exporter = ["perlmod-macro"]
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.46"
|
71
perlmod/build.rs
Normal file
71
perlmod/build.rs
Normal file
@ -0,0 +1,71 @@
|
||||
extern crate cc;
|
||||
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::{env, fs, io};
|
||||
|
||||
fn main() {
|
||||
let out_dir = env::var("OUT_DIR").expect("expected OUT_DIR to be set by cargo");
|
||||
|
||||
let include_dir = format!("{}/include", out_dir);
|
||||
let ppport_h_file = format!("{}/ppport.h", include_dir);
|
||||
// quoted, without exterial double qoutes
|
||||
let ppport_h_file_string_inner = ppport_h_file.replace('"', "\\\"");
|
||||
|
||||
if let Err(err) = fs::create_dir(Path::new(&include_dir)) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
panic!("failed to make include dir in OUT_DIR");
|
||||
}
|
||||
}
|
||||
|
||||
// perl -MDevel::PPPort -e 'Devel::PPPort::WriteFile("include/ppport.h");'
|
||||
Command::new("perl")
|
||||
.arg("-MDevel::PPPort")
|
||||
.arg("-e")
|
||||
.arg(&format!(
|
||||
r#"Devel::PPPort::WriteFile("{}");"#,
|
||||
ppport_h_file_string_inner
|
||||
))
|
||||
.output()
|
||||
.expect("failed to create ppport.h file using perl's Devel::PPPort");
|
||||
|
||||
// get include path:
|
||||
// perl -MConfig -e 'print $Config{archlib}'
|
||||
let perl_archlib = Command::new("perl")
|
||||
.arg("-MConfig")
|
||||
.arg("-e")
|
||||
.arg("print $Config{archlib}")
|
||||
.output()
|
||||
.expect("failed to get perl arch include directory");
|
||||
// technically not a true Path, but we expect a system path which should be utf8-compatible
|
||||
let archlib_include_path = format!(
|
||||
"{}/CORE",
|
||||
std::str::from_utf8(&perl_archlib.stdout).expect("expected perl include path to be utf8")
|
||||
);
|
||||
|
||||
// get perl cflags:
|
||||
// perl -MConfig -e 'print $Config{ccflags}'
|
||||
let perl_ccflags = Command::new("perl")
|
||||
.arg("-MConfig")
|
||||
.arg("-e")
|
||||
.arg("print $Config{ccflags}")
|
||||
.output()
|
||||
.expect("failed to get perl cflags");
|
||||
// technically garbage as it may contain paths, but since this should only contain system
|
||||
// paths and otherwise also might contain quotes and what not let's just not really care:
|
||||
let ccflags = std::str::from_utf8(&perl_ccflags.stdout).expect("expected cflags to be utf8");
|
||||
|
||||
let mut cc = cc::Build::new();
|
||||
|
||||
cc.pic(true)
|
||||
.shared_flag(false)
|
||||
.include(include_dir)
|
||||
.include(archlib_include_path);
|
||||
|
||||
for flag in ccflags.split_ascii_whitespace() {
|
||||
cc.flag(flag);
|
||||
}
|
||||
|
||||
// now build the static library:
|
||||
cc.file("src/glue.c").compile("libglue.a");
|
||||
}
|
223
perlmod/src/array.rs
Normal file
223
perlmod/src/array.rs
Normal file
@ -0,0 +1,223 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::error::CastError;
|
||||
use crate::ffi::{self, AV, SV};
|
||||
use crate::scalar::{Scalar, ScalarRef};
|
||||
use crate::Value;
|
||||
|
||||
/// An owned reference to a perl array value (AV).
|
||||
///
|
||||
/// This keeps a reference to a value which lives in the perl interpreter.
|
||||
#[repr(transparent)]
|
||||
pub struct Array(Scalar);
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl Array {
|
||||
/// Create a new array value.
|
||||
pub fn new() -> Self {
|
||||
unsafe { Self::from_raw_move(ffi::RSPL_newAV()) }
|
||||
}
|
||||
|
||||
/// Turn this into a `Scalar`. The underlying perl value does not change, this is a pure type
|
||||
/// cast down to a less specific "pointer" type.
|
||||
pub fn into_scalar(self) -> Scalar {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Get the internal perl value as a low-level `AV` pointer.
|
||||
pub fn av(&self) -> *mut AV {
|
||||
self.0.sv() as *mut AV
|
||||
}
|
||||
|
||||
/// "Downcast" a `Scalar` into an `Array`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must verify that this is legal.
|
||||
pub unsafe fn from_scalar(scalar: Scalar) -> Self {
|
||||
Self(scalar)
|
||||
}
|
||||
|
||||
/// Take over a raw `AV` value, assuming that we then own a reference to it.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not change the value's reference count, it is assumed that we're taking ownership
|
||||
/// of one reference.
|
||||
///
|
||||
/// The caller must ensure that it is safe to decrease the reference count later on, or use
|
||||
/// `into_raw()` instead of letting the `Array` get dropped.
|
||||
pub unsafe fn from_raw_move(ptr: *mut AV) -> Self {
|
||||
Self(Scalar::from_raw_move(ptr as *mut SV))
|
||||
}
|
||||
|
||||
/// Create a new reference to an existing `AV` value. This will increase the value's reference
|
||||
/// count.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller may still need to decrease the reference count for the `ptr` source value.
|
||||
pub unsafe fn from_raw_ref(ptr: *mut AV) -> Self {
|
||||
Self(Scalar::from_raw_ref(ptr as *mut SV))
|
||||
}
|
||||
|
||||
/// Create a new reference to this value.
|
||||
pub fn clone_ref(&self) -> Self {
|
||||
Self(self.0.clone_ref())
|
||||
}
|
||||
|
||||
/// Get the length of the array.
|
||||
pub fn len(&self) -> usize {
|
||||
// perl returns the highest index, not the length!
|
||||
unsafe { ffi::RSPL_av_len(self.av()).wrapping_add(1) }
|
||||
}
|
||||
|
||||
/// Check if this is an empty array.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Get a value from the array.
|
||||
pub fn get(&self, index: usize) -> Option<Value> {
|
||||
let index = index as libc::ssize_t;
|
||||
let sv: *mut *mut SV = unsafe { ffi::RSPL_av_fetch(self.av(), index, 0) };
|
||||
if sv.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { Value::from_raw_ref(*sv) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an iterator over this array's values.
|
||||
pub fn iter(&self) -> Iter {
|
||||
Iter {
|
||||
array: self.clone_ref(),
|
||||
at: 0,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Pre-extend the array to up to the specified length..
|
||||
pub fn reserve(&self, more: usize) {
|
||||
let idx = self.len() + more - 1;
|
||||
unsafe {
|
||||
ffi::RSPL_av_extend(self.av(), idx as libc::ssize_t);
|
||||
}
|
||||
}
|
||||
|
||||
/// Push a value onto the array.
|
||||
pub fn push(&self, value: Value) {
|
||||
unsafe {
|
||||
ffi::RSPL_av_push(self.av(), value.into_raw());
|
||||
}
|
||||
}
|
||||
|
||||
/// Pop a value off of the array's end.
|
||||
pub fn pop(&self) -> Option<Value> {
|
||||
if self.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { Value::from_raw_move(ffi::RSPL_av_pop(self.av())) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Deref for Array {
|
||||
type Target = ScalarRef;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::DerefMut for Array {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut *self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Scalar> for Array {
|
||||
type Error = CastError;
|
||||
|
||||
fn try_from(scalar: Scalar) -> Result<Self, CastError> {
|
||||
if unsafe { ffi::RSPL_is_array(scalar.sv()) } {
|
||||
Ok(Self(scalar))
|
||||
} else {
|
||||
Err(CastError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Array {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "[")?;
|
||||
let mut comma = false;
|
||||
for i in self {
|
||||
if comma {
|
||||
write!(f, ", {:?}", i)?;
|
||||
} else {
|
||||
comma = true;
|
||||
write!(f, "{:?}", i)?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Iter<'a> {
|
||||
array: Array,
|
||||
at: usize,
|
||||
_phantom: PhantomData<&'a Array>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = Value;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let at = self.at;
|
||||
if at < self.array.len() {
|
||||
self.at += 1;
|
||||
self.array.get(at)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Array {
|
||||
type Item = Value;
|
||||
type IntoIter = Iter<'static>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Iter {
|
||||
array: self,
|
||||
at: 0,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Array {
|
||||
type Item = Value;
|
||||
type IntoIter = Iter<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for Array {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
use serde::ser::SerializeSeq;
|
||||
let mut seq = serializer.serialize_seq(Some(self.len()))?;
|
||||
for i in self {
|
||||
seq.serialize_element(&i)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
}
|
551
perlmod/src/de.rs
Normal file
551
perlmod/src/de.rs
Normal file
@ -0,0 +1,551 @@
|
||||
use serde::de::{self, DeserializeOwned, DeserializeSeed, MapAccess, SeqAccess, Visitor};
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::scalar::Type;
|
||||
use crate::Value;
|
||||
use crate::{array, ffi, hash};
|
||||
|
||||
pub struct Deserializer {
|
||||
input: Value,
|
||||
option_allowed: bool,
|
||||
}
|
||||
|
||||
pub fn from_value<T>(input: Value) -> Result<T, Error>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let mut deserializer = Deserializer::from_value(input);
|
||||
let out = T::deserialize(&mut deserializer)?;
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
impl Deserializer {
|
||||
pub fn from_value(input: Value) -> Self {
|
||||
Deserializer {
|
||||
input,
|
||||
option_allowed: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_some_value(input: Value) -> Self {
|
||||
Deserializer {
|
||||
input,
|
||||
option_allowed: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn deref_current(&mut self) -> Result<(), Error> {
|
||||
while let Value::Reference(_) = &self.input {
|
||||
self.input = self.input.dereference().ok_or_else(|| {
|
||||
Error::new("failed to dereference a reference while deserializing")
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sanity_check(&mut self) -> Result<(), Error> {
|
||||
if let Value::Scalar(value) = &self.input {
|
||||
match value.ty() {
|
||||
Type::Scalar(_) => Ok(()),
|
||||
Type::Other(_) => Error::fail("cannot deserialize weird magic perl values"),
|
||||
// These are impossible as they are all handled by different Value enum types:
|
||||
Type::Reference => Error::fail("Value::Scalar: containing a reference"),
|
||||
Type::Array => Error::fail("Value::Scalar: containing an array"),
|
||||
Type::Hash => Error::fail("Value::Scalar: containing a hash"),
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&mut self) -> Result<&Value, Error> {
|
||||
self.deref_current()?;
|
||||
self.sanity_check()?;
|
||||
Ok(&self.input)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserializer {
|
||||
/// deserialize_any, preferring a string value
|
||||
fn deserialize_any_string<'de, V>(&mut self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.get()? {
|
||||
Value::Scalar(value) => match value.ty() {
|
||||
Type::Scalar(flags) => {
|
||||
use crate::scalar::Flags;
|
||||
|
||||
if flags.contains(Flags::STRING) {
|
||||
visitor.visit_str(value.pv_utf8())
|
||||
} else if flags.contains(Flags::DOUBLE) {
|
||||
visitor.visit_f64(value.nv())
|
||||
} else if flags.contains(Flags::INTEGER) {
|
||||
visitor.visit_i64(value.iv() as i64)
|
||||
} else {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Value::Hash(value) => visitor.visit_map(HashAccess::new(value)),
|
||||
Value::Array(value) => visitor.visit_seq(ArrayAccess::new(value)),
|
||||
Value::Reference(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// deserialize_any, preferring an integer value
|
||||
fn deserialize_any_iv<'de, V>(&mut self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.get()? {
|
||||
Value::Scalar(value) => match value.ty() {
|
||||
Type::Scalar(flags) => {
|
||||
use crate::scalar::Flags;
|
||||
|
||||
if flags.contains(Flags::INTEGER) {
|
||||
visitor.visit_i64(value.iv() as i64)
|
||||
} else if flags.contains(Flags::DOUBLE) {
|
||||
visitor.visit_f64(value.nv())
|
||||
} else if flags.contains(Flags::STRING) {
|
||||
visitor.visit_str(value.pv_utf8())
|
||||
} else {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Value::Hash(value) => visitor.visit_map(HashAccess::new(value)),
|
||||
Value::Array(value) => visitor.visit_seq(ArrayAccess::new(value)),
|
||||
Value::Reference(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// deserialize_any, preferring a float value
|
||||
fn deserialize_any_nv<'de, V>(&mut self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.get()? {
|
||||
Value::Scalar(value) => match value.ty() {
|
||||
Type::Scalar(flags) => {
|
||||
use crate::scalar::Flags;
|
||||
|
||||
if flags.contains(Flags::DOUBLE) {
|
||||
visitor.visit_f64(value.nv())
|
||||
} else if flags.contains(Flags::INTEGER) {
|
||||
visitor.visit_i64(value.iv() as i64)
|
||||
} else if flags.contains(Flags::STRING) {
|
||||
visitor.visit_str(value.pv_utf8())
|
||||
} else {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Value::Hash(value) => visitor.visit_map(HashAccess::new(value)),
|
||||
Value::Array(value) => visitor.visit_seq(ArrayAccess::new(value)),
|
||||
Value::Reference(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer {
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any_string(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.get()? {
|
||||
Value::Scalar(value) => match value.ty() {
|
||||
Type::Scalar(flags) => {
|
||||
use crate::scalar::Flags;
|
||||
|
||||
if flags.is_empty() || flags.intersects(Flags::INTEGER | Flags::DOUBLE) {
|
||||
visitor.visit_bool(unsafe { ffi::RSPL_SvTRUE(value.sv()) })
|
||||
} else {
|
||||
Error::fail("expected bool value")
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Value::Hash(value) => visitor.visit_map(HashAccess::new(value)),
|
||||
Value::Array(value) => visitor.visit_seq(ArrayAccess::new(value)),
|
||||
Value::Reference(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any_iv(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any_iv(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any_iv(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any_iv(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any_iv(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any_iv(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any_iv(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any_iv(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any_nv(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any_nv(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.get()? {
|
||||
Value::Scalar(value) => match value.ty() {
|
||||
Type::Scalar(flags) => {
|
||||
use crate::scalar::Flags;
|
||||
|
||||
if flags.contains(Flags::INTEGER) {
|
||||
let c = value.iv();
|
||||
if c < 0x100 {
|
||||
visitor.visit_char(c as u8 as char)
|
||||
} else {
|
||||
visitor.visit_i64(c as i64)
|
||||
}
|
||||
} else if flags.contains(Flags::DOUBLE) {
|
||||
visitor.visit_f64(value.nv())
|
||||
} else if flags.contains(Flags::STRING) {
|
||||
let s = value.pv_utf8();
|
||||
let mut chars = s.chars();
|
||||
match chars.next() {
|
||||
Some(ch) if chars.next().is_none() => visitor.visit_char(ch),
|
||||
_ => visitor.visit_str(value.pv_utf8()),
|
||||
}
|
||||
} else {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Value::Hash(value) => visitor.visit_map(HashAccess::new(value)),
|
||||
Value::Array(value) => visitor.visit_seq(ArrayAccess::new(value)),
|
||||
Value::Reference(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.get()? {
|
||||
Value::Scalar(value) => match value.ty() {
|
||||
Type::Scalar(flags) => {
|
||||
use crate::scalar::Flags;
|
||||
|
||||
if flags.contains(Flags::STRING) {
|
||||
visitor.visit_bytes(value.pv_bytes())
|
||||
} else if flags.contains(Flags::DOUBLE) {
|
||||
visitor.visit_f64(value.nv())
|
||||
} else if flags.contains(Flags::INTEGER) {
|
||||
visitor.visit_i64(value.iv() as i64)
|
||||
} else {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Value::Hash(value) => visitor.visit_map(HashAccess::new(value)),
|
||||
Value::Array(value) => visitor.visit_seq(ArrayAccess::new(value)),
|
||||
Value::Reference(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_bytes(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
if self.option_allowed {
|
||||
if let Value::Scalar(value) = self.get()? {
|
||||
if let Type::Scalar(flags) = value.ty() {
|
||||
if flags.is_empty() {
|
||||
return visitor.visit_none();
|
||||
}
|
||||
}
|
||||
}
|
||||
self.option_allowed = false;
|
||||
let res = visitor.visit_some(&mut *self);
|
||||
self.option_allowed = true;
|
||||
res
|
||||
} else {
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_tuple_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_map(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HashAccess<'a> {
|
||||
hash: &'a hash::Hash,
|
||||
entry: *mut ffi::HE,
|
||||
finished: bool,
|
||||
at_value: bool,
|
||||
}
|
||||
|
||||
impl<'a> HashAccess<'a> {
|
||||
pub fn new(value: &'a hash::Hash) -> Self {
|
||||
drop(value.shared_iter()); // reset iterator
|
||||
Self {
|
||||
hash: &value,
|
||||
entry: std::ptr::null_mut(),
|
||||
finished: false,
|
||||
at_value: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'a> MapAccess<'de> for HashAccess<'a> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
|
||||
where
|
||||
K: DeserializeSeed<'de>,
|
||||
{
|
||||
if self.finished {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if self.entry.is_null() {
|
||||
self.entry = unsafe { ffi::RSPL_hv_iternext(self.hash.hv()) };
|
||||
if self.entry.is_null() {
|
||||
self.finished = true;
|
||||
return Ok(None);
|
||||
}
|
||||
} else if self.at_value {
|
||||
return Error::fail("map access value skipped");
|
||||
}
|
||||
|
||||
self.at_value = true;
|
||||
|
||||
let key = unsafe { Value::from_raw_ref(ffi::RSPL_hv_iterkeysv(self.entry)) };
|
||||
seed.deserialize(&mut Deserializer::from_value(key))
|
||||
.map(Some)
|
||||
}
|
||||
|
||||
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: DeserializeSeed<'de>,
|
||||
{
|
||||
if self.finished {
|
||||
return Error::fail("map access value requested after end");
|
||||
}
|
||||
|
||||
if self.entry.is_null() || !self.at_value {
|
||||
return Error::fail("map access key skipped");
|
||||
}
|
||||
|
||||
self.at_value = false;
|
||||
|
||||
let value =
|
||||
unsafe { Value::from_raw_ref(ffi::RSPL_hv_iterval(self.hash.hv(), self.entry)) };
|
||||
self.entry = std::ptr::null_mut();
|
||||
|
||||
seed.deserialize(&mut Deserializer::from_value(value))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArrayAccess<'a> {
|
||||
iter: array::Iter<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ArrayAccess<'a> {
|
||||
pub fn new(value: &'a array::Array) -> Self {
|
||||
Self { iter: value.iter() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'a> SeqAccess<'de> for ArrayAccess<'a> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_element_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
|
||||
where
|
||||
K: DeserializeSeed<'de>,
|
||||
{
|
||||
self.iter
|
||||
.next()
|
||||
.map(move |value| seed.deserialize(&mut Deserializer::from_value(value)))
|
||||
.transpose()
|
||||
}
|
||||
}
|
45
perlmod/src/error.rs
Normal file
45
perlmod/src/error.rs
Normal file
@ -0,0 +1,45 @@
|
||||
#[derive(Debug)]
|
||||
pub struct CastError;
|
||||
|
||||
impl std::fmt::Display for CastError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "wrong type")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CastError {}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Error(String);
|
||||
|
||||
impl Error {
|
||||
#[inline]
|
||||
pub fn new(s: &str) -> Self {
|
||||
Self(s.to_string())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fail<T>(s: &str) -> Result<T, Self> {
|
||||
Err(Self(s.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "error: {}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl serde::de::Error for Error {
|
||||
fn custom<T: std::fmt::Display>(msg: T) -> Self {
|
||||
Self(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::Error for Error {
|
||||
fn custom<T: std::fmt::Display>(msg: T) -> Self {
|
||||
Self(msg.to_string())
|
||||
}
|
||||
}
|
158
perlmod/src/ffi.rs
Normal file
158
perlmod/src/ffi.rs
Normal file
@ -0,0 +1,158 @@
|
||||
//! Unsafe ffi code we use.
|
||||
//!
|
||||
//! You should not use this code directly. This is used by the binding generator to implement xsubs
|
||||
//! for exported functions.
|
||||
|
||||
#[repr(C)]
|
||||
pub struct CV {
|
||||
_ffi: usize,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct SV {
|
||||
_ffi: usize,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct AV {
|
||||
_ffi: usize,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct HV {
|
||||
_ffi: usize,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct HE {
|
||||
_ffi: usize,
|
||||
}
|
||||
|
||||
// in our glue:
|
||||
#[link(name = "glue", kind = "static")]
|
||||
extern "C" {
|
||||
pub fn RSPL_StackMark_count(this: usize) -> usize;
|
||||
|
||||
pub fn RSPL_stack_get(offset: usize) -> *mut SV;
|
||||
|
||||
pub fn RSPL_croak_sv(sv: *mut SV) -> !;
|
||||
pub fn RSPL_SvNV(sv: *mut SV) -> f64;
|
||||
pub fn RSPL_SvIV(sv: *mut SV) -> isize;
|
||||
pub fn RSPL_SvPVutf8(sv: *mut SV, len: *mut libc::size_t) -> *const libc::c_char;
|
||||
pub fn RSPL_SvPV(sv: *mut SV, len: *mut libc::size_t) -> *const libc::c_char;
|
||||
pub fn RSPL_SvPVbyte(sv: *mut SV, len: *mut libc::size_t) -> *const libc::c_char;
|
||||
pub fn RSPL_sv_2mortal(sv: *mut SV) -> *mut SV;
|
||||
pub fn RSPL_get_undef() -> *mut SV;
|
||||
pub fn RSPL_get_yes() -> *mut SV;
|
||||
pub fn RSPL_get_no() -> *mut SV;
|
||||
pub fn RSPL_pop_markstack_ptr() -> usize;
|
||||
pub fn RSPL_stack_resize_by(count: isize);
|
||||
pub fn RSPL_stack_shrink_to(count: usize);
|
||||
pub fn RSPL_stack_sp() -> *mut *mut SV;
|
||||
pub fn RSPL_newRV_inc(sv: *mut SV) -> *mut SV;
|
||||
pub fn RSPL_newSViv(v: isize) -> *mut SV;
|
||||
pub fn RSPL_newSVuv(v: usize) -> *mut SV;
|
||||
pub fn RSPL_newSVnv(v: f64) -> *mut SV;
|
||||
pub fn RSPL_newSVpvn(v: *const libc::c_char, len: libc::size_t) -> *mut SV;
|
||||
pub fn RSPL_SvREFCNT_inc(sv: *mut SV) -> *mut SV;
|
||||
pub fn RSPL_SvREFCNT_dec(sv: *mut SV);
|
||||
pub fn RSPL_is_reference(sv: *mut SV) -> bool;
|
||||
pub fn RSPL_dereference(sv: *mut SV) -> *mut SV;
|
||||
pub fn RSPL_is_array(sv: *mut SV) -> bool;
|
||||
pub fn RSPL_is_hash(sv: *mut SV) -> bool;
|
||||
pub fn RSPL_type_flags(sv: *mut SV) -> u32;
|
||||
pub fn RSPL_svtype(sv: *mut SV) -> u32;
|
||||
pub fn RSPL_SvTRUE(sv: *mut SV) -> bool;
|
||||
|
||||
pub fn RSPL_newAV() -> *mut AV;
|
||||
pub fn RSPL_av_extend(av: *mut AV, len: libc::ssize_t);
|
||||
pub fn RSPL_av_push(av: *mut AV, sv: *mut SV);
|
||||
pub fn RSPL_av_pop(av: *mut AV) -> *mut SV;
|
||||
pub fn RSPL_av_len(av: *mut AV) -> usize;
|
||||
pub fn RSPL_av_fetch(av: *mut AV, index: libc::ssize_t, lval: i32) -> *mut *mut SV;
|
||||
|
||||
pub fn RSPL_newHV() -> *mut HV;
|
||||
pub fn RSPL_HvTOTALKEYS(hv: *mut HV) -> usize;
|
||||
pub fn RSPL_hv_fetch(
|
||||
hv: *mut HV,
|
||||
key: *const libc::c_char,
|
||||
klen: i32,
|
||||
lval: i32,
|
||||
) -> *mut *mut SV;
|
||||
/// Always consumes ownership of `value`.
|
||||
pub fn RSPL_hv_store(hv: *mut HV, key: *const libc::c_char, klen: i32, value: *mut SV) -> bool;
|
||||
pub fn RSPL_hv_store_ent(hv: *mut HV, key: *mut SV, value: *mut SV) -> bool;
|
||||
pub fn RSPL_hv_iterinit(hv: *mut HV);
|
||||
pub fn RSPL_hv_iternextsv(
|
||||
hv: *mut HV,
|
||||
key: *mut *mut libc::c_char,
|
||||
retlen: *mut i32,
|
||||
) -> *mut SV;
|
||||
pub fn RSPL_hv_iternext(hv: *mut HV) -> *mut HE;
|
||||
pub fn RSPL_hv_iterkeysv(he: *mut HE) -> *mut SV;
|
||||
pub fn RSPL_hv_iterval(hv: *mut HV, he: *mut HE) -> *mut SV;
|
||||
}
|
||||
|
||||
/// Argument marker for the stack.
|
||||
pub struct StackMark(usize);
|
||||
|
||||
impl StackMark {
|
||||
pub fn count(&self) -> usize {
|
||||
unsafe { RSPL_StackMark_count(self.0) }
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> StackIter {
|
||||
StackIter {
|
||||
at: self.0 + 1,
|
||||
end: self.0 + 1 + self.count(),
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn set_stack(self) {
|
||||
RSPL_stack_shrink_to(self.0);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StackIter {
|
||||
at: usize,
|
||||
end: usize,
|
||||
}
|
||||
|
||||
impl Iterator for StackIter {
|
||||
type Item = crate::Scalar;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let at = self.at;
|
||||
if at == self.end {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
let ptr = RSPL_stack_get(self.at);
|
||||
self.at += 1;
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(crate::Scalar::from_raw_ref(ptr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn pop_arg_mark() -> StackMark {
|
||||
StackMark(RSPL_pop_markstack_ptr())
|
||||
}
|
||||
|
||||
pub unsafe fn stack_push_raw(value: *mut SV) {
|
||||
RSPL_stack_resize_by(1);
|
||||
*RSPL_stack_sp() = value;
|
||||
}
|
||||
|
||||
pub fn stack_push(value: crate::Mortal) {
|
||||
unsafe {
|
||||
stack_push_raw(value.into_raw());
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn croak(sv: *mut SV) {
|
||||
RSPL_croak_sv(sv);
|
||||
}
|
295
perlmod/src/glue.c
Normal file
295
perlmod/src/glue.c
Normal file
@ -0,0 +1,295 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "EXTERN.h"
|
||||
#include "perl.h"
|
||||
#include "XSUB.h"
|
||||
#include "ppport.h"
|
||||
|
||||
typedef uintptr_t usize;
|
||||
typedef intptr_t isize;
|
||||
|
||||
extern usize RSPL_StackMark_count(usize self) {
|
||||
SV **ptr = PL_stack_base + self;
|
||||
if (ptr > PL_stack_sp) {
|
||||
return 0;
|
||||
}
|
||||
return PL_stack_sp - ptr;
|
||||
}
|
||||
|
||||
extern SV* RSPL_stack_get(usize offset) {
|
||||
SV **ptr = PL_stack_base + offset;
|
||||
if (ptr > PL_stack_sp) {
|
||||
return NULL;
|
||||
}
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
extern void RSPL_croak_sv(SV *sv) {
|
||||
croak_sv(sv);
|
||||
}
|
||||
|
||||
extern double RSPL_SvNV(SV *sv) {
|
||||
return SvNV(sv);
|
||||
}
|
||||
|
||||
extern isize RSPL_SvIV(SV *sv) {
|
||||
return SvIV(sv);
|
||||
}
|
||||
|
||||
extern const char* RSPL_SvPVutf8(SV *sv, size_t *out_len) {
|
||||
size_t length;
|
||||
const char *out = SvPVutf8(sv, length);
|
||||
*out_len = length;
|
||||
return out;
|
||||
}
|
||||
|
||||
extern const char* RSPL_SvPV(SV *sv, size_t *out_len) {
|
||||
size_t length;
|
||||
const char *out = SvPV(sv, length);
|
||||
*out_len = length;
|
||||
return out;
|
||||
}
|
||||
|
||||
extern const char* RSPL_SvPVbyte(SV *sv, size_t *out_len) {
|
||||
size_t length;
|
||||
const char *out = SvPVbyte(sv, length);
|
||||
*out_len = length;
|
||||
return out;
|
||||
}
|
||||
|
||||
extern SV* RSPL_sv_2mortal(SV *sv) {
|
||||
return sv_2mortal(sv);
|
||||
}
|
||||
|
||||
extern SV* RSPL_get_undef() {
|
||||
return &PL_sv_undef;
|
||||
}
|
||||
|
||||
extern SV* RSPL_get_yes() {
|
||||
return &PL_sv_yes;
|
||||
}
|
||||
|
||||
extern SV* RSPL_get_no() {
|
||||
return &PL_sv_no;
|
||||
}
|
||||
|
||||
extern usize RSPL_PL_markstack_ptr() {
|
||||
return *PL_markstack_ptr;
|
||||
}
|
||||
|
||||
extern usize RSPL_pop_markstack_ptr() {
|
||||
return *PL_markstack_ptr--;
|
||||
}
|
||||
|
||||
extern void RSPL_stack_shrink_to(usize count) {
|
||||
PL_stack_sp = PL_stack_base + count;
|
||||
}
|
||||
|
||||
extern void RSPL_stack_resize_by(isize count) {
|
||||
if (count > 0) {
|
||||
isize space = PL_stack_max - PL_stack_sp;
|
||||
if (space < count) {
|
||||
Perl_stack_grow(aTHX_ PL_stack_sp, PL_stack_sp, count - space);
|
||||
}
|
||||
}
|
||||
PL_stack_sp += count;
|
||||
}
|
||||
|
||||
extern SV** RSPL_stack_sp() {
|
||||
return PL_stack_sp;
|
||||
}
|
||||
|
||||
extern SV* RSPL_newRV_inc(SV *rv) {
|
||||
return newRV_inc(rv);
|
||||
}
|
||||
|
||||
extern SV* RSPL_newSViv(isize v) {
|
||||
return newSViv(v);
|
||||
}
|
||||
|
||||
extern SV* RSPL_newSVuv(usize v) {
|
||||
return newSVuv(v);
|
||||
}
|
||||
|
||||
extern SV* RSPL_newSVnv(double v) {
|
||||
return newSVnv(v);
|
||||
}
|
||||
|
||||
extern SV* RSPL_newSVpvn(const char *v, size_t len) {
|
||||
return newSVpvn(v, len);
|
||||
}
|
||||
|
||||
extern SV* RSPL_SvREFCNT_inc(SV *sv) {
|
||||
return SvREFCNT_inc(sv);
|
||||
}
|
||||
|
||||
extern void RSPL_SvREFCNT_dec(SV *sv) {
|
||||
return SvREFCNT_dec(sv);
|
||||
}
|
||||
|
||||
extern bool RSPL_is_scalar(SV *sv) {
|
||||
return SvTYPE(sv) < SVt_PVAV;
|
||||
}
|
||||
|
||||
extern bool RSPL_SvTRUE(SV *sv) {
|
||||
return SvTRUE(sv);
|
||||
}
|
||||
|
||||
// This must be the same as in rust!
|
||||
#define TYPE_FLAG_INT 1
|
||||
#define TYPE_FLAG_DOUBLE 2
|
||||
#define TYPE_FLAG_STRING 4
|
||||
|
||||
static const uint32_t type_flags[16] = {
|
||||
[SVt_NULL] = 0,
|
||||
[SVt_IV] = TYPE_FLAG_INT,
|
||||
[SVt_NV] = TYPE_FLAG_INT | TYPE_FLAG_DOUBLE,
|
||||
[SVt_PV] = TYPE_FLAG_STRING,
|
||||
[SVt_PVIV] = TYPE_FLAG_STRING | TYPE_FLAG_INT,
|
||||
[SVt_PVNV] = TYPE_FLAG_STRING | TYPE_FLAG_INT | TYPE_FLAG_DOUBLE,
|
||||
[SVt_PVMG] = ~0,
|
||||
};
|
||||
|
||||
extern uint32_t RSPL_svtype(SV *sv) {
|
||||
return SvTYPE(sv);
|
||||
}
|
||||
|
||||
extern uint32_t RSPL_type_flags(SV *sv) {
|
||||
return type_flags[SvTYPE(sv)];
|
||||
}
|
||||
|
||||
extern bool RSPL_has_integer(SV *sv) {
|
||||
return 0 != (type_flags[SvTYPE(sv)] & TYPE_FLAG_INT);
|
||||
}
|
||||
|
||||
extern bool RSPL_has_double(SV *sv) {
|
||||
return 0 != (type_flags[SvTYPE(sv)] & TYPE_FLAG_DOUBLE);
|
||||
}
|
||||
|
||||
extern bool RSPL_has_string(SV *sv) {
|
||||
return 0 != (type_flags[SvTYPE(sv)] & TYPE_FLAG_STRING);
|
||||
}
|
||||
|
||||
extern SV* RSPL_SvRV(SV *sv) {
|
||||
return SvRV(sv);
|
||||
}
|
||||
|
||||
extern SV* RSPL_dereference(SV *sv) {
|
||||
return SvROK(sv) ? SvRV(sv) : NULL;
|
||||
}
|
||||
|
||||
extern bool RSPL_is_reference(SV *sv) {
|
||||
return SvROK(sv);
|
||||
}
|
||||
|
||||
extern bool RSPL_is_array(SV *sv) {
|
||||
return SvTYPE(sv) == SVt_PVAV;
|
||||
}
|
||||
|
||||
extern bool RSPL_is_hash(SV *sv) {
|
||||
return SvTYPE(sv) == SVt_PVHV;
|
||||
}
|
||||
|
||||
extern AV* RSPL_newAV() {
|
||||
return newAV();
|
||||
}
|
||||
|
||||
extern usize RSPL_av_len(AV *av) {
|
||||
return av_len(av);
|
||||
}
|
||||
|
||||
extern void RSPL_av_extend(AV *av, ssize_t len) {
|
||||
av_extend(av, len);
|
||||
}
|
||||
|
||||
extern void RSPL_av_push(AV *av, SV *sv) {
|
||||
av_push(av, sv);
|
||||
}
|
||||
|
||||
extern SV* RSPL_av_pop(AV *av) {
|
||||
return av_pop(av);
|
||||
}
|
||||
|
||||
extern SV** RSPL_av_fetch(AV *av, ssize_t index, int32_t lval) {
|
||||
return av_fetch(av, index, lval);
|
||||
}
|
||||
|
||||
extern HV* RSPL_newHV() {
|
||||
return newHV();
|
||||
}
|
||||
|
||||
extern usize RSPL_HvTOTALKEYS(HV *hv) {
|
||||
return HvTOTALKEYS(hv);
|
||||
}
|
||||
|
||||
extern SV** RSPL_hv_fetch(HV *hv, const char *key, int32_t klen, int32_t lval) {
|
||||
return hv_fetch(hv, key, klen, lval);
|
||||
}
|
||||
|
||||
/// ALWAYS takes ownership of 'value'.
|
||||
extern bool RSPL_hv_store(HV *hv, const char *key, int32_t klen, SV *value) {
|
||||
if (hv_store(hv, key, klen, value, 0) == NULL) {
|
||||
SvREFCNT_dec(value);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extern bool RSPL_hv_store_ent(HV *hv, SV *key, SV *value) {
|
||||
if (hv_store_ent(hv, key, value, 0) == NULL) {
|
||||
SvREFCNT_dec(value);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extern void RSPL_hv_iterinit(HV *hv) {
|
||||
hv_iterinit(hv);
|
||||
}
|
||||
|
||||
extern SV* RSPL_hv_iternextsv(HV *hv, char **key, int32_t *retlen) {
|
||||
return hv_iternextsv(hv, key, retlen);
|
||||
}
|
||||
|
||||
extern HE* RSPL_hv_iternext(HV *hv) {
|
||||
return hv_iternext(hv);
|
||||
}
|
||||
|
||||
extern SV* RSPL_hv_iterkeysv(HE *he) {
|
||||
return hv_iterkeysv(he);
|
||||
}
|
||||
|
||||
extern SV* RSPL_hv_iterval(HV *hv, HE *he) {
|
||||
return hv_iterval(hv, he);
|
||||
}
|
||||
|
||||
/*
|
||||
These make are convoluted brainfarts:
|
||||
SVt_NULL undef
|
||||
SVt_IV all the above or int
|
||||
SVt_NV all the above or a double
|
||||
SVt_PV undef or a string
|
||||
SVt_PVIV PV or IV
|
||||
SVt_PVNV PV or NV
|
||||
SVt_PVMG all of the above with tentacles, 2 heads and unicorn poop on top
|
||||
|
||||
These make some sense
|
||||
SVt_INVLIST Bleeding smelly perl guts
|
||||
SVt_REGEXP Sandpaper
|
||||
SVt_PVGV Typeglob
|
||||
SVt_PVLV C++ style reference to another scalar (implicit deref)
|
||||
|
||||
These make sense
|
||||
SVt_PVAV Arrays
|
||||
SVt_PVHV Hashes
|
||||
SVt_PVCV Subroutine
|
||||
SVt_PVFM Formats
|
||||
SVt_PVIO I/O objects
|
||||
*/
|
208
perlmod/src/hash.rs
Normal file
208
perlmod/src/hash.rs
Normal file
@ -0,0 +1,208 @@
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::error::CastError;
|
||||
use crate::ffi::{self, HV, SV};
|
||||
use crate::scalar::{Scalar, ScalarRef};
|
||||
use crate::Value;
|
||||
|
||||
/// An owned reference to a perl hash value (HV).
|
||||
///
|
||||
/// This keeps a reference to a value which lives in the perl interpreter.
|
||||
#[repr(transparent)]
|
||||
pub struct Hash(Scalar);
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl Hash {
|
||||
/// Create a new array value.
|
||||
pub fn new() -> Self {
|
||||
unsafe { Self::from_raw_move(ffi::RSPL_newHV()) }
|
||||
}
|
||||
|
||||
/// Turn this into a `Scalar`. The underlying perl value does not change, this is a pure type
|
||||
/// cast down to a less specific "pointer" type.
|
||||
pub fn into_scalar(self) -> Scalar {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Get the internal perl value as a low-level `HV` pointer.
|
||||
pub fn hv(&self) -> *mut HV {
|
||||
self.0.sv() as *mut HV
|
||||
}
|
||||
|
||||
/// "Downcast" a `Scalar` into a `Hash`. The caller must verify that this is legal.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must verify that this is legal.
|
||||
pub unsafe fn from_scalar(scalar: Scalar) -> Self {
|
||||
Self(scalar)
|
||||
}
|
||||
|
||||
/// Take over a raw `HV` value, assuming that we then own a reference to it.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not change the value's reference count, it is assumed that we're taking ownership
|
||||
/// of one reference.
|
||||
///
|
||||
/// The caller must ensure that it is safe to decrease the reference count later on, or use
|
||||
/// `into_raw()` instead of letting the `Hash` get dropped.
|
||||
pub unsafe fn from_raw_move(ptr: *mut HV) -> Self {
|
||||
Self(Scalar::from_raw_move(ptr as *mut SV))
|
||||
}
|
||||
|
||||
/// Create a new reference to an existing `HV` value. This will increase the value's reference
|
||||
/// count.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller may still need to decrease the reference count for the `ptr` source value.
|
||||
pub unsafe fn from_raw_ref(ptr: *mut HV) -> Self {
|
||||
Self(Scalar::from_raw_ref(ptr as *mut SV))
|
||||
}
|
||||
|
||||
/// Create a new reference to this value.
|
||||
pub fn clone_ref(&self) -> Self {
|
||||
Self(self.0.clone_ref())
|
||||
}
|
||||
|
||||
/// Get the number of keys in this hash.
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { ffi::RSPL_HvTOTALKEYS(self.hv()) }
|
||||
}
|
||||
|
||||
/// Check if this is an empty hash.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Get a value from the hash. Note that this only uses utf8 strings. For a more generic method
|
||||
/// see `get_by_bytes`.
|
||||
pub fn get(&self, key: &str) -> Option<Value> {
|
||||
self.get_by_bytes(key.as_bytes())
|
||||
}
|
||||
|
||||
/// Get a value from the hash, but with a raw byte string as index.
|
||||
pub fn get_by_bytes(&self, key: &[u8]) -> Option<Value> {
|
||||
let sv: *mut *mut SV = unsafe {
|
||||
ffi::RSPL_hv_fetch(
|
||||
self.hv(),
|
||||
key.as_ptr() as *const libc::c_char,
|
||||
key.len() as i32,
|
||||
0,
|
||||
)
|
||||
};
|
||||
if sv.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { Value::from_raw_ref(*sv) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a value into the hash.
|
||||
pub fn insert(&self, key: &str, value: Value) {
|
||||
self.insert_by_bytes(key.as_bytes(), value);
|
||||
}
|
||||
|
||||
/// Insert a value into the hash with a byte string as key.
|
||||
pub fn insert_by_bytes(&self, key: &[u8], value: Value) {
|
||||
unsafe {
|
||||
ffi::RSPL_hv_store(
|
||||
self.hv(),
|
||||
key.as_ptr() as *const u8 as *const libc::c_char,
|
||||
key.len() as i32,
|
||||
value.into_raw(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a value using an existin value as a key.
|
||||
pub fn insert_by_value(&self, key: &Value, value: Value) {
|
||||
unsafe {
|
||||
ffi::RSPL_hv_store_ent(self.hv(), key.sv(), value.sv());
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the *shared* iterator over this hash's elements.
|
||||
///
|
||||
/// Note that this uses the hash's internal iterator, so any other iterator as well as `each`
|
||||
/// statement within perl code is affected by it, and it is usually a bad idea to have multiple
|
||||
/// iterators over the same hash simultaneously.
|
||||
pub fn shared_iter(&self) -> Iter {
|
||||
unsafe {
|
||||
ffi::RSPL_hv_iterinit(self.hv());
|
||||
}
|
||||
Iter { hash: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Deref for Hash {
|
||||
type Target = ScalarRef;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::DerefMut for Hash {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut *self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Scalar> for Hash {
|
||||
type Error = CastError;
|
||||
|
||||
fn try_from(scalar: Scalar) -> Result<Self, CastError> {
|
||||
if unsafe { ffi::RSPL_is_hash(scalar.sv()) } {
|
||||
Ok(Self(scalar))
|
||||
} else {
|
||||
Err(CastError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Hash {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{{HASH}}")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Iter<'a> {
|
||||
hash: &'a Hash,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = (&'a [u8], Value);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut key: *mut libc::c_char = std::ptr::null_mut();
|
||||
let mut keylen: i32 = 0;
|
||||
let value = unsafe { ffi::RSPL_hv_iternextsv(self.hash.hv(), &mut key, &mut keylen) };
|
||||
if value.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
Some((
|
||||
std::slice::from_raw_parts(key as *mut u8, keylen as usize),
|
||||
Value::from_raw_ref(value),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for Hash {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
use serde::ser::SerializeMap;
|
||||
let mut map = serializer.serialize_map(Some(self.len()))?;
|
||||
for (k, v) in self.shared_iter() {
|
||||
map.serialize_key(&k)?;
|
||||
map.serialize_value(&v)?;
|
||||
}
|
||||
map.end()
|
||||
}
|
||||
}
|
29
perlmod/src/lib.rs
Normal file
29
perlmod/src/lib.rs
Normal file
@ -0,0 +1,29 @@
|
||||
pub(crate) mod error;
|
||||
|
||||
pub mod de;
|
||||
pub mod ffi;
|
||||
pub mod ser;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use de::from_value;
|
||||
#[doc(inline)]
|
||||
pub use ser::to_value;
|
||||
|
||||
pub mod scalar;
|
||||
#[doc(inline)]
|
||||
pub use scalar::{Mortal, Scalar};
|
||||
|
||||
pub mod array;
|
||||
#[doc(inline)]
|
||||
pub use array::Array;
|
||||
|
||||
pub mod hash;
|
||||
#[doc(inline)]
|
||||
pub use hash::Hash;
|
||||
|
||||
pub mod value;
|
||||
#[doc(inline)]
|
||||
pub use value::Value;
|
||||
|
||||
#[cfg(feature = "exporter")]
|
||||
pub use perlmod_macro::package;
|
335
perlmod/src/scalar.rs
Normal file
335
perlmod/src/scalar.rs
Normal file
@ -0,0 +1,335 @@
|
||||
use bitflags::bitflags;
|
||||
|
||||
use crate::ffi::{self, SV};
|
||||
use crate::Value;
|
||||
|
||||
/// An owned reference to a perl value.
|
||||
///
|
||||
/// This keeps a reference to a value which lives in the perl interpreter.
|
||||
/// This derefs to a `ScalarRef` which implements most of the basic functionality common to all
|
||||
/// `SV` related types.
|
||||
///
|
||||
/// This implements `Send` but not `Sync`, because it is effectively the same as an `Arc` to a
|
||||
/// value which is inherently `Send` but not `Sync`.
|
||||
#[repr(transparent)]
|
||||
pub struct Scalar(*mut SV);
|
||||
|
||||
unsafe impl Send for Scalar {}
|
||||
|
||||
impl Scalar {
|
||||
/// Turn this into a "mortal" value. This will move this value's owned reference onto the
|
||||
/// mortal stack to be cleaned up after the next perl statement if no more references exist.
|
||||
///
|
||||
/// (To be garbage collected after this perl-statement.)
|
||||
pub fn into_mortal(self) -> Mortal {
|
||||
Mortal(unsafe { ffi::RSPL_sv_2mortal(self.into_raw()) })
|
||||
}
|
||||
|
||||
/// Turn this into a raw `SV` transferring control of one reference count.
|
||||
pub fn into_raw(self) -> *mut SV {
|
||||
let ptr = self.0;
|
||||
core::mem::forget(self);
|
||||
ptr
|
||||
}
|
||||
|
||||
/// Create a wrapping `Scalar` from an `SV` pointer. The `Scalar` takes over the owned
|
||||
/// reference from the passed `SV`, which means it must not be a mortal reference.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not change the value's reference count, it is assumed that we're taking ownership
|
||||
/// of one reference.
|
||||
///
|
||||
/// The caller must ensure that it is safe to decrease the reference count later on, or use
|
||||
/// `into_raw()` instead of letting the `Scalar` get dropped.
|
||||
pub unsafe fn from_raw_move(ptr: *mut SV) -> Self {
|
||||
Self(ptr)
|
||||
}
|
||||
|
||||
/// Increase the reference count on an `SV` pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller may still need to decrease the reference count for the `ptr` source value.
|
||||
pub unsafe fn from_raw_ref(ptr: *mut SV) -> Self {
|
||||
Self::from_raw_move(ffi::RSPL_SvREFCNT_inc(ptr))
|
||||
}
|
||||
|
||||
/// Create a reference to `PL_sv_undef`.
|
||||
pub fn new_undef() -> Self {
|
||||
unsafe { Self::from_raw_ref(ffi::RSPL_get_undef()) }
|
||||
}
|
||||
|
||||
/// Create a reference to `PL_sv_yes`.
|
||||
pub fn new_yes() -> Self {
|
||||
unsafe { Self::from_raw_ref(ffi::RSPL_get_yes()) }
|
||||
}
|
||||
|
||||
/// Create a reference to `PL_sv_no`.
|
||||
pub fn new_no() -> Self {
|
||||
unsafe { Self::from_raw_ref(ffi::RSPL_get_no()) }
|
||||
}
|
||||
|
||||
/// Create a new integer value:
|
||||
pub fn new_int(v: isize) -> Self {
|
||||
unsafe { Self::from_raw_move(ffi::RSPL_newSViv(v)) }
|
||||
}
|
||||
|
||||
/// Create a new unsigned integer value:
|
||||
pub fn new_uint(v: usize) -> Self {
|
||||
unsafe { Self::from_raw_move(ffi::RSPL_newSVuv(v)) }
|
||||
}
|
||||
|
||||
/// Create a new floating point value.
|
||||
pub fn new_float(v: f64) -> Self {
|
||||
unsafe { Self::from_raw_move(ffi::RSPL_newSVnv(v)) }
|
||||
}
|
||||
|
||||
/// Create a new string value.
|
||||
pub fn new_string(s: &str) -> Self {
|
||||
Self::new_bytes(s.as_bytes())
|
||||
}
|
||||
|
||||
/// Create a new byte string.
|
||||
pub fn new_bytes(s: &[u8]) -> Self {
|
||||
unsafe {
|
||||
Self::from_raw_move(ffi::RSPL_newSVpvn(
|
||||
s.as_ptr() as *const libc::c_char,
|
||||
s.len() as libc::size_t,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Scalar {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
ffi::RSPL_SvREFCNT_dec(self.sv());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Deref for Scalar {
|
||||
type Target = ScalarRef;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*(self.0 as *mut ScalarRef) }
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::DerefMut for Scalar {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *(self.0 as *mut ScalarRef) }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Mortal(*mut SV);
|
||||
|
||||
impl Mortal {
|
||||
/// Get the inner value.
|
||||
pub fn into_raw(self) -> *mut SV {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Deref for Mortal {
|
||||
type Target = ScalarRef;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*(self.0 as *mut ScalarRef) }
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::DerefMut for Mortal {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *(self.0 as *mut ScalarRef) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ScalarRef;
|
||||
|
||||
bitflags! {
|
||||
/// Represents the types a `Value` can contain. Values can usually contain multiple scalar types
|
||||
/// at once and it is unclear which is the "true" type, so we can only check whether a value
|
||||
/// contains something, not what it is originally meant to be!
|
||||
///
|
||||
/// NOTE: The values must be the same as in our c glue code!
|
||||
pub struct Flags: u8 {
|
||||
const INTEGER = 1;
|
||||
const DOUBLE = 2;
|
||||
const STRING = 4;
|
||||
}
|
||||
}
|
||||
|
||||
/// While scalar types aren't clearly different from another, complex types are, so we do
|
||||
/// distinguish between these:
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Type {
|
||||
Scalar(Flags),
|
||||
Reference,
|
||||
Array,
|
||||
Hash,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
impl ScalarRef {
|
||||
pub(crate) fn sv(&self) -> *mut SV {
|
||||
self as *const ScalarRef as *const SV as *mut SV
|
||||
}
|
||||
|
||||
/// Get some information about the value's type.
|
||||
pub fn ty(&self) -> Type {
|
||||
unsafe {
|
||||
if ffi::RSPL_is_reference(self.sv()) {
|
||||
Type::Reference
|
||||
} else {
|
||||
let flags = ffi::RSPL_type_flags(self.sv());
|
||||
if flags > 0xff {
|
||||
panic!("bad C type returned");
|
||||
} else if flags != 0 {
|
||||
Type::Scalar(Flags::from_bits(flags as u8).unwrap())
|
||||
} else if ffi::RSPL_is_array(self.sv()) {
|
||||
Type::Array
|
||||
} else if ffi::RSPL_is_hash(self.sv()) {
|
||||
Type::Hash
|
||||
} else {
|
||||
Type::Other(ffi::RSPL_svtype(self.sv()) as u8)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dereference this reference.
|
||||
pub fn dereference(&self) -> Option<Scalar> {
|
||||
let ptr = unsafe { ffi::RSPL_dereference(self.sv()) };
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { Scalar::from_raw_ref(ptr) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Coerce to a double value. (perlxs SvNV).
|
||||
pub fn nv(&self) -> f64 {
|
||||
unsafe { ffi::RSPL_SvNV(self.sv()) }
|
||||
}
|
||||
|
||||
/// Coerce to an integer value. (perlxs SvIV).
|
||||
pub fn iv(&self) -> isize {
|
||||
unsafe { ffi::RSPL_SvIV(self.sv()) }
|
||||
}
|
||||
|
||||
/// Coerce to an utf8 string value. (perlxs SvPVutf8)
|
||||
pub fn pv_utf8(&self) -> &str {
|
||||
unsafe {
|
||||
let mut len: libc::size_t = 0;
|
||||
let ptr = ffi::RSPL_SvPVutf8(self.sv(), &mut len) as *const u8;
|
||||
std::str::from_utf8_unchecked(std::slice::from_raw_parts(ptr, len))
|
||||
}
|
||||
}
|
||||
|
||||
/// Coerce to a string without utf8 encoding. (perlxs SvPV)
|
||||
pub fn pv_string_bytes(&self) -> &[u8] {
|
||||
unsafe {
|
||||
let mut len: libc::size_t = 0;
|
||||
let ptr = ffi::RSPL_SvPV(self.sv(), &mut len) as *const u8;
|
||||
std::slice::from_raw_parts(ptr, len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Coerce to a byte-string. (perlxs SvPVbyte)
|
||||
pub fn pv_bytes(&self) -> &[u8] {
|
||||
unsafe {
|
||||
let mut len: libc::size_t = 0;
|
||||
let ptr = ffi::RSPL_SvPVbyte(self.sv(), &mut len) as *const u8;
|
||||
std::slice::from_raw_parts(ptr, len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create another owned reference to this value.
|
||||
pub fn clone_ref(&self) -> Scalar {
|
||||
unsafe { Scalar::from_raw_ref(self.sv()) }
|
||||
}
|
||||
|
||||
/// Convenience check for SVt_NULL
|
||||
pub fn is_undef(&self) -> bool {
|
||||
0 == unsafe { ffi::RSPL_type_flags(self.sv()) }
|
||||
}
|
||||
|
||||
/// Turn this into a `Value`.
|
||||
pub fn into_value(self) -> Value {
|
||||
Value::from_scalar(self.clone_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Scalar {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let this: &ScalarRef = &self;
|
||||
std::fmt::Debug::fmt(this, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ScalarRef {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
use std::fmt::Debug;
|
||||
match self.ty() {
|
||||
Type::Scalar(flags) => {
|
||||
if flags.intersects(Flags::STRING) {
|
||||
Debug::fmt(self.pv_utf8(), f)
|
||||
} else if flags.intersects(Flags::INTEGER) {
|
||||
write!(f, "{}", self.iv())
|
||||
} else if flags.intersects(Flags::DOUBLE) {
|
||||
write!(f, "{}", self.nv())
|
||||
} else {
|
||||
write!(f, "<unhandled scalar>")
|
||||
}
|
||||
}
|
||||
Type::Reference => write!(f, "<*REFERENCE>"),
|
||||
Type::Array => write!(f, "<*ARRAY>"),
|
||||
Type::Hash => write!(f, "<*HASH>"),
|
||||
Type::Other(_) => write!(f, "<*PERLTYPE>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for Scalar {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
use serde::ser::Error;
|
||||
|
||||
match self.ty() {
|
||||
Type::Scalar(flags) => {
|
||||
if flags.contains(Flags::STRING) {
|
||||
serializer.serialize_str(self.pv_utf8())
|
||||
} else if flags.contains(Flags::DOUBLE) {
|
||||
serializer.serialize_f64(self.nv())
|
||||
} else if flags.contains(Flags::INTEGER) {
|
||||
serializer.serialize_i64(self.iv() as i64)
|
||||
} else {
|
||||
serializer.serialize_unit()
|
||||
}
|
||||
}
|
||||
Type::Other(_) => Err(S::Error::custom(
|
||||
"cannot deserialize weird magic perl values",
|
||||
)),
|
||||
|
||||
// These are impossible as they are all handled by different Value enum types:
|
||||
Type::Reference => Value::from(
|
||||
self.dereference()
|
||||
.ok_or_else(|| S::Error::custom("failed to dereference perl value"))?,
|
||||
)
|
||||
.serialize(serializer),
|
||||
Type::Array => {
|
||||
let this = unsafe { crate::Array::from_raw_ref(self.sv() as *mut ffi::AV) };
|
||||
this.serialize(serializer)
|
||||
}
|
||||
Type::Hash => {
|
||||
let this = unsafe { crate::Hash::from_raw_ref(self.sv() as *mut ffi::HV) };
|
||||
this.serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
376
perlmod/src/ser.rs
Normal file
376
perlmod/src/ser.rs
Normal file
@ -0,0 +1,376 @@
|
||||
use serde::{ser, Serialize};
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::Value;
|
||||
use crate::{array, hash};
|
||||
|
||||
pub struct Serializer;
|
||||
|
||||
pub fn to_value<T>(value: &T) -> Result<Value, Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
value.serialize(&mut Serializer)
|
||||
}
|
||||
|
||||
pub struct SerHash {
|
||||
hash: hash::Hash,
|
||||
key: Option<Value>,
|
||||
}
|
||||
|
||||
pub struct SerArray {
|
||||
array: array::Array,
|
||||
}
|
||||
|
||||
pub struct SerVariant<T> {
|
||||
hash: hash::Hash,
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<'a> ser::Serializer for &'a mut Serializer {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = SerArray;
|
||||
type SerializeTuple = SerArray;
|
||||
type SerializeTupleStruct = SerArray;
|
||||
type SerializeTupleVariant = SerVariant<SerArray>;
|
||||
type SerializeMap = SerHash;
|
||||
type SerializeStruct = SerHash;
|
||||
type SerializeStructVariant = SerVariant<SerHash>;
|
||||
|
||||
fn serialize_bool(self, v: bool) -> Result<Value, Error> {
|
||||
Ok(Value::new_uint(if v { 1 } else { 0 }))
|
||||
}
|
||||
|
||||
fn serialize_i8(self, v: i8) -> Result<Value, Error> {
|
||||
self.serialize_i64(i64::from(v))
|
||||
}
|
||||
|
||||
fn serialize_i16(self, v: i16) -> Result<Value, Error> {
|
||||
self.serialize_i64(i64::from(v))
|
||||
}
|
||||
|
||||
fn serialize_i32(self, v: i32) -> Result<Value, Error> {
|
||||
self.serialize_i64(i64::from(v))
|
||||
}
|
||||
|
||||
fn serialize_i64(self, v: i64) -> Result<Value, Error> {
|
||||
Ok(Value::new_int(v as isize))
|
||||
}
|
||||
|
||||
fn serialize_u8(self, v: u8) -> Result<Value, Error> {
|
||||
self.serialize_u64(u64::from(v))
|
||||
}
|
||||
|
||||
fn serialize_u16(self, v: u16) -> Result<Value, Error> {
|
||||
self.serialize_u64(u64::from(v))
|
||||
}
|
||||
|
||||
fn serialize_u32(self, v: u32) -> Result<Value, Error> {
|
||||
self.serialize_u64(u64::from(v))
|
||||
}
|
||||
|
||||
fn serialize_u64(self, v: u64) -> Result<Value, Error> {
|
||||
Ok(Value::new_uint(v as usize))
|
||||
}
|
||||
|
||||
fn serialize_f32(self, v: f32) -> Result<Value, Error> {
|
||||
self.serialize_f64(f64::from(v))
|
||||
}
|
||||
|
||||
fn serialize_f64(self, v: f64) -> Result<Value, Error> {
|
||||
Ok(Value::new_float(v))
|
||||
}
|
||||
|
||||
fn serialize_char(self, v: char) -> Result<Value, Error> {
|
||||
self.serialize_str(&v.to_string())
|
||||
}
|
||||
|
||||
fn serialize_str(self, v: &str) -> Result<Value, Error> {
|
||||
Ok(Value::new_string(v))
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, v: &[u8]) -> Result<Value, Error> {
|
||||
Ok(Value::new_bytes(v))
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<Value, Error> {
|
||||
self.serialize_unit()
|
||||
}
|
||||
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Value, Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<Value, Error> {
|
||||
Ok(Value::new_undef())
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value, Error> {
|
||||
self.serialize_unit()
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
) -> Result<Value, Error> {
|
||||
self.serialize_str(variant)
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value, Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Value, Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
let value = value.serialize(&mut Serializer)?;
|
||||
let hash = hash::Hash::new();
|
||||
hash.insert(variant, value);
|
||||
Ok(Value::from(hash))
|
||||
}
|
||||
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Error> {
|
||||
Ok(SerArray::new(len))
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Error> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct, Error> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, Error> {
|
||||
Ok(SerVariant::<SerArray>::new(variant, Some(len)))
|
||||
}
|
||||
|
||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Error> {
|
||||
Ok(SerHash::new())
|
||||
}
|
||||
|
||||
fn serialize_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStruct, Error> {
|
||||
self.serialize_map(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStructVariant, Error> {
|
||||
Ok(SerVariant::<SerHash>::new(variant))
|
||||
}
|
||||
}
|
||||
|
||||
impl SerArray {
|
||||
fn new(len: Option<usize>) -> Self {
|
||||
let array = array::Array::new();
|
||||
if let Some(len) = len {
|
||||
array.reserve(len);
|
||||
}
|
||||
Self { array }
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::SerializeSeq for SerArray {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
self.array.push(value.serialize(&mut Serializer)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
Ok(Value::new_ref(&self.array))
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::SerializeTuple for SerArray {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
self.array.push(value.serialize(&mut Serializer)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
Ok(Value::new_ref(&self.array))
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::SerializeTupleStruct for SerArray {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
self.array.push(value.serialize(&mut Serializer)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
Ok(Value::new_ref(&self.array))
|
||||
}
|
||||
}
|
||||
|
||||
impl SerHash {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
hash: hash::Hash::new(),
|
||||
key: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::SerializeMap for SerHash {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_key<T>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
if self.key.is_some() {
|
||||
Error::fail("serialize_key called twice")
|
||||
} else {
|
||||
self.key = Some(value.serialize(&mut Serializer)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
match self.key.take() {
|
||||
None => Error::fail("serialize_value called without key"),
|
||||
Some(key) => {
|
||||
let value = value.serialize(&mut Serializer)?;
|
||||
self.hash.insert_by_value(&key, value);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
if self.key.is_some() {
|
||||
Error::fail("missing value for key")
|
||||
} else {
|
||||
Ok(Value::new_ref(&self.hash))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::SerializeStruct for SerHash {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, field: &'static str, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
self.hash.insert(field, value.serialize(&mut Serializer)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
Ok(Value::new_ref(&self.hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl SerVariant<SerArray> {
|
||||
fn new(variant: &str, len: Option<usize>) -> Self {
|
||||
let inner = SerArray::new(len);
|
||||
let hash = hash::Hash::new();
|
||||
hash.insert(variant, Value::new_ref(&inner.array));
|
||||
Self { hash, inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::SerializeTupleVariant for SerVariant<SerArray> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
self.inner.array.push(value.serialize(&mut Serializer)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
Ok(Value::new_ref(&self.inner.array))
|
||||
}
|
||||
}
|
||||
|
||||
impl SerVariant<SerHash> {
|
||||
fn new(variant: &str) -> Self {
|
||||
let inner = SerHash::new();
|
||||
let hash = hash::Hash::new();
|
||||
hash.insert(variant, Value::new_ref(&inner.hash));
|
||||
Self { hash, inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::SerializeStructVariant for SerVariant<SerHash> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, field: &'static str, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
self.inner
|
||||
.hash
|
||||
.insert(field, value.serialize(&mut Serializer)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
Ok(Value::new_ref(&self.hash))
|
||||
}
|
||||
}
|
355
perlmod/src/value.rs
Normal file
355
perlmod/src/value.rs
Normal file
@ -0,0 +1,355 @@
|
||||
use std::fmt;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::ffi::{self, SV};
|
||||
use crate::scalar::ScalarRef;
|
||||
use crate::{Array, Hash, Scalar};
|
||||
|
||||
/// A higher level value. This is basically an `SV` already cast to `AV` or `HV` for arrays and
|
||||
/// hashes.
|
||||
pub enum Value {
|
||||
Scalar(Scalar),
|
||||
Reference(Scalar),
|
||||
Array(Array),
|
||||
Hash(Hash),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
/// Create a new undef value:
|
||||
pub fn new_undef() -> Self {
|
||||
Value::Scalar(Scalar::new_undef())
|
||||
}
|
||||
|
||||
/// Create a new integer value:
|
||||
pub fn new_int(v: isize) -> Self {
|
||||
Value::Scalar(Scalar::new_int(v))
|
||||
}
|
||||
|
||||
/// Create a new unsigned integer value:
|
||||
pub fn new_uint(v: usize) -> Self {
|
||||
Value::Scalar(Scalar::new_uint(v))
|
||||
}
|
||||
|
||||
/// Create a new floating point value.
|
||||
pub fn new_float(v: f64) -> Self {
|
||||
Value::Scalar(Scalar::new_float(v))
|
||||
}
|
||||
|
||||
/// Create a new string value.
|
||||
pub fn new_string(s: &str) -> Self {
|
||||
Value::Scalar(Scalar::new_string(s))
|
||||
}
|
||||
|
||||
/// Create a new byte string.
|
||||
pub fn new_bytes(s: &[u8]) -> Self {
|
||||
Value::Scalar(Scalar::new_bytes(s))
|
||||
}
|
||||
|
||||
/// Create an actual perl reference to the value. (The equivalent of perl's backslash
|
||||
/// operator).
|
||||
pub fn new_ref<T>(value: &T) -> Self
|
||||
where
|
||||
T: std::ops::Deref<Target = ScalarRef>,
|
||||
{
|
||||
Value::Reference(unsafe { Scalar::from_raw_move(ffi::RSPL_newRV_inc(value.sv())) })
|
||||
}
|
||||
|
||||
/// Take over a raw `SV` value, assuming that we then own a reference to it.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This does not change the value's reference count, it is assumed that we're taking ownership
|
||||
/// of one reference.
|
||||
///
|
||||
/// The caller must ensure that it is safe to decrease the reference count later on, or use
|
||||
/// `into_raw()` instead of letting the `Value` get dropped.
|
||||
pub unsafe fn from_raw_move(ptr: *mut SV) -> Self {
|
||||
Self::from_scalar(Scalar::from_raw_move(ptr as *mut SV))
|
||||
}
|
||||
|
||||
/// Create a new reference to an existing `SV` value. This will increase the value's reference
|
||||
/// count.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller may still need to decrease the reference count for the `ptr` source value.
|
||||
pub unsafe fn from_raw_ref(ptr: *mut SV) -> Self {
|
||||
Self::from_scalar(Scalar::from_raw_ref(ptr as *mut SV))
|
||||
}
|
||||
|
||||
pub fn from_scalar(scalar: Scalar) -> Self {
|
||||
Self::from(scalar)
|
||||
}
|
||||
|
||||
/// Create a new reference to this value.
|
||||
pub fn clone_ref(&self) -> Self {
|
||||
match self {
|
||||
Value::Scalar(v) => Value::Scalar(v.clone_ref()),
|
||||
Value::Reference(v) => Value::Reference(v.clone_ref()),
|
||||
Value::Array(v) => Value::Array(v.clone_ref()),
|
||||
Value::Hash(v) => Value::Hash(v.clone_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Dereference this reference value.
|
||||
pub fn dereference(&self) -> Option<Value> {
|
||||
match self {
|
||||
Value::Reference(v) => v.dereference().map(Value::from_scalar),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn this into a raw `SV` transferring control of one reference count.
|
||||
pub fn into_raw(self) -> *mut SV {
|
||||
match self {
|
||||
Value::Scalar(v) => v.into_raw(),
|
||||
Value::Reference(v) => v.into_raw(),
|
||||
Value::Array(v) => v.into_scalar().into_raw(),
|
||||
Value::Hash(v) => v.into_scalar().into_raw(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_mortal(self) -> crate::scalar::Mortal {
|
||||
match self {
|
||||
Value::Scalar(v) => v.into_mortal(),
|
||||
Value::Reference(v) => v.into_mortal(),
|
||||
Value::Array(v) => v.into_scalar().into_mortal(),
|
||||
Value::Hash(v) => v.into_scalar().into_mortal(),
|
||||
}
|
||||
}
|
||||
|
||||
/// If this is value is an array, get the value at the specified index.
|
||||
pub fn get(&self, index: usize) -> Option<Value> {
|
||||
if let Value::Array(a) = self {
|
||||
a.get(index)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Scalar> for Value {
|
||||
fn from(scalar: Scalar) -> Self {
|
||||
unsafe {
|
||||
if ffi::RSPL_is_array(scalar.sv()) {
|
||||
Value::Array(Array::from_scalar(scalar))
|
||||
} else if ffi::RSPL_is_hash(scalar.sv()) {
|
||||
Value::Hash(Hash::from_scalar(scalar))
|
||||
} else if ffi::RSPL_is_reference(scalar.sv()) {
|
||||
Value::Reference(scalar)
|
||||
} else {
|
||||
Value::Scalar(scalar)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Hash> for Value {
|
||||
fn from(hash: Hash) -> Self {
|
||||
Value::Hash(hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Array> for Value {
|
||||
fn from(array: Array) -> Self {
|
||||
Value::Array(array)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use fmt::Debug;
|
||||
match self {
|
||||
Value::Scalar(v) => Debug::fmt(v, f),
|
||||
Value::Reference(v) => Debug::fmt(v, f),
|
||||
Value::Array(v) => Debug::fmt(v, f),
|
||||
Value::Hash(v) => Debug::fmt(v, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Deref for Value {
|
||||
type Target = ScalarRef;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Value::Scalar(v) => &*v,
|
||||
Value::Reference(v) => &*v,
|
||||
Value::Array(v) => &*v,
|
||||
Value::Hash(v) => &*v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::DerefMut for Value {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
Value::Scalar(v) => &mut *v,
|
||||
Value::Reference(v) => &mut *v,
|
||||
Value::Array(v) => &mut *v,
|
||||
Value::Hash(v) => &mut *v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Value {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
use serde::ser::Error;
|
||||
|
||||
match self {
|
||||
Value::Scalar(this) => this.serialize(serializer),
|
||||
Value::Reference(this) => Value::from(
|
||||
this.dereference()
|
||||
.ok_or_else(|| S::Error::custom("failed to dereference perl value"))?,
|
||||
)
|
||||
.serialize(serializer),
|
||||
Value::Array(value) => value.serialize(serializer),
|
||||
Value::Hash(value) => value.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Value {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Visitor;
|
||||
|
||||
struct ValueVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for ValueVisitor {
|
||||
type Value = Value;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("any valid PERL value")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_bool<E>(self, value: bool) -> Result<Value, E> {
|
||||
Ok(Value::new_int(if value { 1 } else { 0 }))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_i64<E>(self, value: i64) -> Result<Value, E> {
|
||||
Ok(Value::new_int(value as isize))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_u64<E>(self, value: u64) -> Result<Value, E> {
|
||||
Ok(Value::new_uint(value as usize))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_f64<E>(self, value: f64) -> Result<Value, E> {
|
||||
Ok(Value::new_float(value))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_str<E>(self, value: &str) -> Result<Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(Value::new_string(value))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_string<E>(self, value: String) -> Result<Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_str(&value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_none<E>(self) -> Result<Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(Value::new_undef())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_some<D>(self, deserializer: D) -> Result<Value, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Deserialize::deserialize(deserializer)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_unit<E>(self) -> Result<Value, E> {
|
||||
Ok(Value::new_undef())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_seq<V>(self, mut visitor: V) -> Result<Value, V::Error>
|
||||
where
|
||||
V: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let array = Array::new();
|
||||
|
||||
while let Some(elem) = visitor.next_element()? {
|
||||
array.push(elem);
|
||||
}
|
||||
|
||||
Ok(Value::Array(array))
|
||||
}
|
||||
|
||||
fn visit_map<V>(self, mut visitor: V) -> Result<Value, V::Error>
|
||||
where
|
||||
V: serde::de::MapAccess<'de>,
|
||||
{
|
||||
// We use this to hint the deserializer that we're expecting a string-ish value.
|
||||
struct KeyClassifier;
|
||||
struct KeyClass(String);
|
||||
|
||||
impl<'de> serde::de::DeserializeSeed<'de> for KeyClassifier {
|
||||
type Value = KeyClass;
|
||||
|
||||
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Visitor<'de> for KeyClassifier {
|
||||
type Value = KeyClass;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a string key")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(KeyClass(s.to_owned()))
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, s: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(KeyClass(s))
|
||||
}
|
||||
}
|
||||
|
||||
let hash = Hash::new();
|
||||
while let Some(key) = visitor.next_key_seed(KeyClassifier)? {
|
||||
let value: Value = visitor.next_value()?;
|
||||
hash.insert(&key.0, value);
|
||||
}
|
||||
Ok(Value::from(hash))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(ValueVisitor)
|
||||
}
|
||||
}
|
1
rust-toolchain
Normal file
1
rust-toolchain
Normal file
@ -0,0 +1 @@
|
||||
nightly
|
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@ -0,0 +1 @@
|
||||
edition = "2018"
|
Loading…
Reference in New Issue
Block a user