feat: add rust-script
This commit is contained in:
186
external/rust-script/src/consts.rs
vendored
Normal file
186
external/rust-script/src/consts.rs
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
/*!
|
||||
This module just contains any big string literals I don't want cluttering up the rest of the code.
|
||||
*/
|
||||
|
||||
pub const PROGRAM_NAME: &str = "rust-script";
|
||||
|
||||
/*
|
||||
What follows are the templates used to wrap script input.
|
||||
*/
|
||||
|
||||
/// Substitution for the script body.
|
||||
pub const SCRIPT_BODY_SUB: &str = "script";
|
||||
|
||||
/// Substitution for the script prelude.
|
||||
pub const SCRIPT_PRELUDE_SUB: &str = "prelude";
|
||||
|
||||
/// The template used for script file inputs.
|
||||
pub const FILE_TEMPLATE: &str = r#"#{script}"#;
|
||||
|
||||
/// The template used for `--expr` input.
|
||||
pub const EXPR_TEMPLATE: &str = r#"
|
||||
#{prelude}
|
||||
use std::any::{Any, TypeId};
|
||||
|
||||
fn main() {
|
||||
let exit_code = match try_main() {
|
||||
Ok(()) => None,
|
||||
Err(e) => {
|
||||
use std::io::{self, Write};
|
||||
let _ = writeln!(io::stderr(), "Error: {}", e);
|
||||
Some(1)
|
||||
},
|
||||
};
|
||||
if let Some(exit_code) = exit_code {
|
||||
std::process::exit(exit_code);
|
||||
}
|
||||
}
|
||||
|
||||
fn try_main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn _rust_script_is_empty_tuple<T: ?Sized + Any>(_s: &T) -> bool {
|
||||
TypeId::of::<()>() == TypeId::of::<T>()
|
||||
}
|
||||
match {#{script}} {
|
||||
__rust_script_expr if !_rust_script_is_empty_tuple(&__rust_script_expr) => println!("{:?}", __rust_script_expr),
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
"#;
|
||||
|
||||
/*
|
||||
Regarding the loop templates: what I *want* is for the result of the closure to be printed to standard output *only* if it's not `()`.
|
||||
|
||||
* TODO: Merge the `LOOP_*` templates so there isn't duplicated code. It's icky.
|
||||
*/
|
||||
|
||||
/// The template used for `--loop` input, assuming no `--count` flag is also given.
|
||||
pub const LOOP_TEMPLATE: &str = r#"
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_braces)]
|
||||
#{prelude}
|
||||
use std::any::Any;
|
||||
use std::io::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let mut closure = enforce_closure(
|
||||
{#{script}}
|
||||
);
|
||||
let mut line_buffer = String::new();
|
||||
let stdin = std::io::stdin();
|
||||
loop {
|
||||
line_buffer.clear();
|
||||
let read_res = stdin.read_line(&mut line_buffer).unwrap_or(0);
|
||||
if read_res == 0 { break }
|
||||
let output = closure(&line_buffer);
|
||||
|
||||
let display = {
|
||||
let output_any: &dyn Any = &output;
|
||||
!output_any.is::<()>()
|
||||
};
|
||||
|
||||
if display {
|
||||
println!("{:?}", output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_closure<F, T>(closure: F) -> F
|
||||
where F: FnMut(&str) -> T, T: 'static {
|
||||
closure
|
||||
}
|
||||
"#;
|
||||
|
||||
/// The template used for `--count --loop` input.
|
||||
pub const LOOP_COUNT_TEMPLATE: &str = r#"
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_braces)]
|
||||
use std::any::Any;
|
||||
use std::io::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let mut closure = enforce_closure(
|
||||
{#{script}}
|
||||
);
|
||||
let mut line_buffer = String::new();
|
||||
let stdin = std::io::stdin();
|
||||
let mut count = 0;
|
||||
loop {
|
||||
line_buffer.clear();
|
||||
let read_res = stdin.read_line(&mut line_buffer).unwrap_or(0);
|
||||
if read_res == 0 { break }
|
||||
count += 1;
|
||||
let output = closure(&line_buffer, count);
|
||||
|
||||
let display = {
|
||||
let output_any: &dyn Any = &output;
|
||||
!output_any.is::<()>()
|
||||
};
|
||||
|
||||
if display {
|
||||
println!("{:?}", output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_closure<F, T>(closure: F) -> F
|
||||
where F: FnMut(&str, usize) -> T, T: 'static {
|
||||
closure
|
||||
}
|
||||
"#;
|
||||
|
||||
/// Substitution for the identifier-safe package name of the script.
|
||||
pub const MANI_NAME_SUB: &str = "name";
|
||||
|
||||
/// Substitution for the identifier-safe bin name of the script.
|
||||
pub const MANI_BIN_NAME_SUB: &str = "bin_name";
|
||||
|
||||
/// Substitution for the filesystem-safe name of the script.
|
||||
pub const MANI_FILE_SUB: &str = "file";
|
||||
|
||||
/**
|
||||
The default manifest used for packages.
|
||||
*/
|
||||
#[rustversion::before(1.59)]
|
||||
pub const DEFAULT_MANIFEST: &str = r##"
|
||||
[package]
|
||||
name = "#{name}"
|
||||
version = "0.1.0"
|
||||
authors = ["Anonymous"]
|
||||
edition = "2018"
|
||||
|
||||
[[bin]]
|
||||
name = "#{bin_name}"
|
||||
path = "#{file}.rs"
|
||||
"##;
|
||||
#[rustversion::since(1.59)]
|
||||
pub const DEFAULT_MANIFEST: &str = r##"
|
||||
[package]
|
||||
name = "#{name}"
|
||||
version = "0.1.0"
|
||||
authors = ["Anonymous"]
|
||||
edition = "2018"
|
||||
|
||||
[[bin]]
|
||||
name = "#{bin_name}"
|
||||
path = "#{file}.rs"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
"##;
|
||||
|
||||
/**
|
||||
When generating a package's unique ID, how many hex nibbles of the digest should be used *at most*?
|
||||
|
||||
The largest meaningful value is `40`.
|
||||
*/
|
||||
pub const ID_DIGEST_LEN_MAX: usize = 24;
|
||||
|
||||
/**
|
||||
How old can stuff in the cache be before we automatically clear it out?
|
||||
|
||||
Measured in milliseconds.
|
||||
*/
|
||||
// It's been *one week* since you looked at me,
|
||||
// cocked your head to the side and said "I'm angry."
|
||||
pub const MAX_CACHE_AGE_MS: u128 = 7 * 24 * 60 * 60 * 1000;
|
||||
61
external/rust-script/src/error.rs
vendored
Normal file
61
external/rust-script/src/error.rs
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
/*!
|
||||
Definition of the program's main error type.
|
||||
*/
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::result::Result;
|
||||
|
||||
/// Shorthand for the program's common result type.
|
||||
pub type MainResult<T> = Result<T, MainError>;
|
||||
|
||||
/// An error in the program.
|
||||
#[derive(Debug)]
|
||||
pub enum MainError {
|
||||
Io(io::Error),
|
||||
Tag(Cow<'static, str>, Box<MainError>),
|
||||
Other(Box<dyn Error>),
|
||||
OtherOwned(String),
|
||||
OtherBorrowed(&'static str),
|
||||
}
|
||||
|
||||
impl fmt::Display for MainError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
use self::MainError::*;
|
||||
use std::fmt::Display;
|
||||
match *self {
|
||||
Io(ref err) => Display::fmt(err, fmt),
|
||||
Tag(ref msg, ref err) => write!(fmt, "{}: {}", msg, err),
|
||||
Other(ref err) => Display::fmt(err, fmt),
|
||||
OtherOwned(ref err) => Display::fmt(err, fmt),
|
||||
OtherBorrowed(err) => Display::fmt(err, fmt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for MainError {}
|
||||
|
||||
macro_rules! from_impl {
|
||||
($src_ty:ty => $dst_ty:ty, $src:ident -> $e:expr) => {
|
||||
impl From<$src_ty> for $dst_ty {
|
||||
fn from($src: $src_ty) -> $dst_ty {
|
||||
$e
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
from_impl! { io::Error => MainError, v -> MainError::Io(v) }
|
||||
from_impl! { String => MainError, v -> MainError::OtherOwned(v) }
|
||||
from_impl! { &'static str => MainError, v -> MainError::OtherBorrowed(v) }
|
||||
|
||||
impl<T> From<Box<T>> for MainError
|
||||
where
|
||||
T: 'static + Error,
|
||||
{
|
||||
fn from(src: Box<T>) -> Self {
|
||||
MainError::Other(src)
|
||||
}
|
||||
}
|
||||
101
external/rust-script/src/file_assoc.rs
vendored
Normal file
101
external/rust-script/src/file_assoc.rs
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
/*!
|
||||
This module deals with setting up file associations on Windows
|
||||
*/
|
||||
use crate::error::MainResult;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use winreg::{enums as wre, RegKey};
|
||||
|
||||
pub fn install_file_association() -> MainResult<()> {
|
||||
let rust_script_path = env::current_exe()?.canonicalize()?;
|
||||
if !rust_script_path.exists() {
|
||||
return Err(format!("{:?} not found", rust_script_path).into());
|
||||
}
|
||||
|
||||
// We have to remove the `\\?\` prefix because, if we don't, the shell freaks out.
|
||||
let rust_script_path = rust_script_path.to_string_lossy();
|
||||
let rust_script_path = if let Some(stripped) = rust_script_path.strip_prefix(r#"\\?\"#) {
|
||||
stripped
|
||||
} else {
|
||||
&rust_script_path[..]
|
||||
};
|
||||
|
||||
let res = (|| -> io::Result<()> {
|
||||
let hlcr = RegKey::predef(wre::HKEY_CLASSES_ROOT);
|
||||
let (dot_ers, _) = hlcr.create_subkey(".ers")?;
|
||||
dot_ers.set_value("", &"RustScript.Ers")?;
|
||||
|
||||
let (cs_ers, _) = hlcr.create_subkey("RustScript.Ers")?;
|
||||
cs_ers.set_value("", &"Rust Script")?;
|
||||
|
||||
let (sh_o_c, _) = cs_ers.create_subkey(r#"shell\open\command"#)?;
|
||||
sh_o_c.set_value("", &format!(r#""{}" "%1" %*"#, rust_script_path))?;
|
||||
Ok(())
|
||||
})();
|
||||
|
||||
match res {
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::PermissionDenied {
|
||||
println!(
|
||||
"Access denied. Make sure you run this command from an administrator prompt."
|
||||
);
|
||||
}
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
|
||||
println!("Created rust-script registry entry.");
|
||||
println!("- Handler set to: {}", rust_script_path);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn uninstall_file_association() -> MainResult<()> {
|
||||
let mut ignored_missing = false;
|
||||
{
|
||||
let mut notify = || ignored_missing = true;
|
||||
|
||||
let hlcr = RegKey::predef(wre::HKEY_CLASSES_ROOT);
|
||||
hlcr.delete_subkey(r#"RustScript.Ers\shell\open\command"#)
|
||||
.ignore_missing_and(&mut notify)?;
|
||||
hlcr.delete_subkey(r#"RustScript.Ers\shell\open"#)
|
||||
.ignore_missing_and(&mut notify)?;
|
||||
hlcr.delete_subkey(r#"RustScript.Ers\shell"#)
|
||||
.ignore_missing_and(&mut notify)?;
|
||||
hlcr.delete_subkey(r#"RustScript.Ers"#)
|
||||
.ignore_missing_and(&mut notify)?;
|
||||
}
|
||||
|
||||
if ignored_missing {
|
||||
println!("Ignored some missing registry entries.");
|
||||
}
|
||||
println!("Deleted rust-script registry entry.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
trait IgnoreMissing {
|
||||
fn ignore_missing_and<F>(self, f: F) -> Self
|
||||
where
|
||||
F: FnOnce();
|
||||
}
|
||||
|
||||
impl IgnoreMissing for io::Result<()> {
|
||||
fn ignore_missing_and<F>(self, f: F) -> Self
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
match self {
|
||||
Ok(()) => Ok(()),
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::NotFound {
|
||||
f();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1230
external/rust-script/src/main.rs
vendored
Normal file
1230
external/rust-script/src/main.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1214
external/rust-script/src/manifest.rs
vendored
Normal file
1214
external/rust-script/src/manifest.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
94
external/rust-script/src/platform.rs
vendored
Normal file
94
external/rust-script/src/platform.rs
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
/*!
|
||||
This module is for platform-specific stuff.
|
||||
*/
|
||||
|
||||
pub use self::inner::force_cargo_color;
|
||||
|
||||
use crate::consts;
|
||||
use crate::error::MainError;
|
||||
use std::fs;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
// Last-modified time of a file, in milliseconds since the UNIX epoch.
|
||||
pub fn file_last_modified(file: &fs::File) -> u128 {
|
||||
file.metadata()
|
||||
.and_then(|md| {
|
||||
md.modified()
|
||||
.map(|t| t.duration_since(UNIX_EPOCH).unwrap().as_millis())
|
||||
})
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
// Current system time, in milliseconds since the UNIX epoch.
|
||||
pub fn current_time() -> u128 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis()
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub fn cache_dir() -> Result<PathBuf, MainError> {
|
||||
dirs_next::cache_dir()
|
||||
.map(|dir| dir.join(consts::PROGRAM_NAME))
|
||||
.ok_or_else(|| ("Cannot get cache directory").into())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn cache_dir() -> Result<PathBuf, MainError> {
|
||||
use lazy_static::lazy_static;
|
||||
lazy_static! {
|
||||
static ref TEMP_DIR: tempfile::TempDir = tempfile::TempDir::new().unwrap();
|
||||
}
|
||||
Ok(TEMP_DIR.path().to_path_buf())
|
||||
}
|
||||
|
||||
pub fn generated_projects_cache_path() -> Result<PathBuf, MainError> {
|
||||
cache_dir().map(|dir| dir.join("projects"))
|
||||
}
|
||||
|
||||
pub fn binary_cache_path() -> Result<PathBuf, MainError> {
|
||||
cache_dir().map(|dir| dir.join("binaries"))
|
||||
}
|
||||
|
||||
pub fn templates_dir() -> Result<PathBuf, MainError> {
|
||||
if cfg!(debug_assertions) {
|
||||
if let Ok(path) = std::env::var("RUST_SCRIPT_DEBUG_TEMPLATE_PATH") {
|
||||
return Ok(path.into());
|
||||
}
|
||||
}
|
||||
|
||||
dirs_next::data_local_dir()
|
||||
.map(|dir| dir.join(consts::PROGRAM_NAME).join("templates"))
|
||||
.ok_or_else(|| ("Cannot get cache directory").into())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
mod inner {
|
||||
pub use super::*;
|
||||
|
||||
/**
|
||||
Returns `true` if `rust-script` should force Cargo to use coloured output.
|
||||
|
||||
This depends on whether `rust-script`'s STDERR is connected to a TTY or not.
|
||||
*/
|
||||
pub fn force_cargo_color() -> bool {
|
||||
atty::is(atty::Stream::Stderr)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub mod inner {
|
||||
pub use super::*;
|
||||
|
||||
/**
|
||||
Returns `true` if `rust-script` should force Cargo to use coloured output.
|
||||
|
||||
Always returns `false` on Windows because colour is communicated over a side-channel.
|
||||
*/
|
||||
pub fn force_cargo_color() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
138
external/rust-script/src/templates.rs
vendored
Normal file
138
external/rust-script/src/templates.rs
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
/*!
|
||||
This module contains code related to template support.
|
||||
*/
|
||||
use crate::consts;
|
||||
use crate::error::{MainError, MainResult};
|
||||
use crate::platform;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
|
||||
lazy_static! {
|
||||
static ref RE_SUB: Regex = Regex::new(r#"#\{([A-Za-z_][A-Za-z0-9_]*)}"#).unwrap();
|
||||
}
|
||||
|
||||
pub fn expand(src: &str, subs: &HashMap<&str, &str>) -> MainResult<String> {
|
||||
// The estimate of final size is the sum of the size of all the input.
|
||||
let sub_size = subs.iter().map(|(_, v)| v.len()).sum::<usize>();
|
||||
let est_size = src.len() + sub_size;
|
||||
|
||||
let mut anchor = 0;
|
||||
let mut result = String::with_capacity(est_size);
|
||||
|
||||
for m in RE_SUB.captures_iter(src) {
|
||||
// Concatenate the static bit just before the match.
|
||||
let (m_start, m_end) = {
|
||||
let m_0 = m.get(0).unwrap();
|
||||
(m_0.start(), m_0.end())
|
||||
};
|
||||
let prior_slice = anchor..m_start;
|
||||
anchor = m_end;
|
||||
result.push_str(&src[prior_slice]);
|
||||
|
||||
// Concat the substitution.
|
||||
let sub_name = m.get(1).unwrap().as_str();
|
||||
match subs.get(sub_name) {
|
||||
Some(s) => result.push_str(s),
|
||||
None => {
|
||||
return Err(MainError::OtherOwned(format!(
|
||||
"substitution `{}` in template is unknown",
|
||||
sub_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push_str(&src[anchor..]);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/**
|
||||
Attempts to locate and load the contents of the specified template.
|
||||
*/
|
||||
pub fn get_template(name: &str) -> MainResult<Cow<'static, str>> {
|
||||
use std::io::Read;
|
||||
|
||||
let base = platform::templates_dir()?;
|
||||
|
||||
let file = fs::File::open(base.join(format!("{}.rs", name)))
|
||||
.map_err(MainError::from)
|
||||
.map_err(|e| {
|
||||
MainError::Tag(
|
||||
format!(
|
||||
"template file `{}.rs` does not exist in {}",
|
||||
name,
|
||||
base.display()
|
||||
)
|
||||
.into(),
|
||||
Box::new(e),
|
||||
)
|
||||
});
|
||||
|
||||
// If the template is one of the built-in ones, do fallback if it wasn't found on disk.
|
||||
if file.is_err() {
|
||||
if let Some(text) = builtin_template(name) {
|
||||
return Ok(text.into());
|
||||
}
|
||||
}
|
||||
|
||||
let mut file = file?;
|
||||
|
||||
let mut text = String::new();
|
||||
file.read_to_string(&mut text)?;
|
||||
Ok(text.into())
|
||||
}
|
||||
|
||||
fn builtin_template(name: &str) -> Option<&'static str> {
|
||||
Some(match name {
|
||||
"expr" => consts::EXPR_TEMPLATE,
|
||||
"file" => consts::FILE_TEMPLATE,
|
||||
"loop" => consts::LOOP_TEMPLATE,
|
||||
"loop-count" => consts::LOOP_COUNT_TEMPLATE,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn list() -> MainResult<()> {
|
||||
use std::ffi::OsStr;
|
||||
|
||||
let t_path = platform::templates_dir()?;
|
||||
|
||||
if !t_path.exists() {
|
||||
fs::create_dir_all(&t_path)?;
|
||||
}
|
||||
|
||||
println!("Listing templates in {}", t_path.display());
|
||||
|
||||
if !t_path.exists() {
|
||||
return Err(format!(
|
||||
"cannot list template directory `{}`: it does not exist",
|
||||
t_path.display()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if !t_path.is_dir() {
|
||||
return Err(format!(
|
||||
"cannot list template directory `{}`: it is not a directory",
|
||||
t_path.display()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
for entry in fs::read_dir(&t_path)? {
|
||||
let entry = entry?;
|
||||
if !entry.file_type()?.is_file() {
|
||||
continue;
|
||||
}
|
||||
let f_path = entry.path();
|
||||
if f_path.extension() != Some(OsStr::new("rs")) {
|
||||
continue;
|
||||
}
|
||||
if let Some(stem) = f_path.file_stem() {
|
||||
println!("{}", stem.to_string_lossy());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
54
external/rust-script/src/util.rs
vendored
Normal file
54
external/rust-script/src/util.rs
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*!
|
||||
This module just contains other random implementation stuff.
|
||||
*/
|
||||
use log::error;
|
||||
use std::error::Error;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/**
|
||||
Used to defer a closure until the value is dropped.
|
||||
|
||||
The closure *must* return a `Result<(), _>`, as a reminder to *not* panic; doing so will abort your whole program if it happens during another panic. If the closure returns an `Err`, then it is logged as an `error`.
|
||||
|
||||
A `Defer` can also be "disarmed", preventing the closure from running at all.
|
||||
*/
|
||||
#[must_use]
|
||||
pub struct Defer<'a, F, E>(Option<F>, PhantomData<&'a F>)
|
||||
where
|
||||
F: 'a + FnOnce() -> Result<(), E>,
|
||||
E: Error;
|
||||
|
||||
impl<'a, F, E> Defer<'a, F, E>
|
||||
where
|
||||
F: 'a + FnOnce() -> Result<(), E>,
|
||||
E: Error,
|
||||
{
|
||||
/**
|
||||
Create a new `Defer` with the given closure.
|
||||
*/
|
||||
pub fn new(f: F) -> Defer<'a, F, E> {
|
||||
Defer(Some(f), PhantomData)
|
||||
}
|
||||
|
||||
/**
|
||||
Consume this `Defer` *without* invoking the closure.
|
||||
*/
|
||||
pub fn disarm(mut self) {
|
||||
self.0 = None;
|
||||
drop(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F, E> ::std::ops::Drop for Defer<'a, F, E>
|
||||
where
|
||||
F: 'a + FnOnce() -> Result<(), E>,
|
||||
E: Error,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
if let Some(f) = self.0.take() {
|
||||
if let Err(err) = f() {
|
||||
error!("deferred function failed: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user