mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
perf: move Deno.writeTextFile and like functions to Rust (#14221)
Co-authored-by: Luca Casonato <hello@lcas.dev>
This commit is contained in:
parent
ca3b20df3c
commit
a64e63c361
6 changed files with 222 additions and 138 deletions
|
@ -17,7 +17,7 @@ Deno.test(
|
|||
const dataRead = Deno.readFileSync(filename);
|
||||
const dec = new TextDecoder("utf-8");
|
||||
const actual = dec.decode(dataRead);
|
||||
assertEquals("Hello", actual);
|
||||
assertEquals(actual, "Hello");
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -34,7 +34,7 @@ Deno.test(
|
|||
const dataRead = Deno.readFileSync(fileUrl);
|
||||
const dec = new TextDecoder("utf-8");
|
||||
const actual = dec.decode(dataRead);
|
||||
assertEquals("Hello", actual);
|
||||
assertEquals(actual, "Hello");
|
||||
|
||||
Deno.removeSync(tempDir, { recursive: true });
|
||||
},
|
||||
|
@ -92,7 +92,7 @@ Deno.test(
|
|||
const dataRead = Deno.readFileSync(filename);
|
||||
const dec = new TextDecoder("utf-8");
|
||||
const actual = dec.decode(dataRead);
|
||||
assertEquals("Hello", actual);
|
||||
assertEquals(actual, "Hello");
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -107,17 +107,17 @@ Deno.test(
|
|||
let dataRead = Deno.readFileSync(filename);
|
||||
const dec = new TextDecoder("utf-8");
|
||||
let actual = dec.decode(dataRead);
|
||||
assertEquals("HelloHello", actual);
|
||||
assertEquals(actual, "HelloHello");
|
||||
// Now attempt overwrite
|
||||
Deno.writeFileSync(filename, data, { append: false });
|
||||
dataRead = Deno.readFileSync(filename);
|
||||
actual = dec.decode(dataRead);
|
||||
assertEquals("Hello", actual);
|
||||
assertEquals(actual, "Hello");
|
||||
// append not set should also overwrite
|
||||
Deno.writeFileSync(filename, data);
|
||||
dataRead = Deno.readFileSync(filename);
|
||||
actual = dec.decode(dataRead);
|
||||
assertEquals("Hello", actual);
|
||||
assertEquals(actual, "Hello");
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -131,7 +131,7 @@ Deno.test(
|
|||
const dataRead = Deno.readFileSync(filename);
|
||||
const dec = new TextDecoder("utf-8");
|
||||
const actual = dec.decode(dataRead);
|
||||
assertEquals("Hello", actual);
|
||||
assertEquals(actual, "Hello");
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -148,7 +148,7 @@ Deno.test(
|
|||
const dataRead = Deno.readFileSync(fileUrl);
|
||||
const dec = new TextDecoder("utf-8");
|
||||
const actual = dec.decode(dataRead);
|
||||
assertEquals("Hello", actual);
|
||||
assertEquals(actual, "Hello");
|
||||
|
||||
Deno.removeSync(tempDir, { recursive: true });
|
||||
},
|
||||
|
@ -212,7 +212,7 @@ Deno.test(
|
|||
const dataRead = Deno.readFileSync(filename);
|
||||
const dec = new TextDecoder("utf-8");
|
||||
const actual = dec.decode(dataRead);
|
||||
assertEquals("Hello", actual);
|
||||
assertEquals(actual, "Hello");
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -227,17 +227,17 @@ Deno.test(
|
|||
let dataRead = Deno.readFileSync(filename);
|
||||
const dec = new TextDecoder("utf-8");
|
||||
let actual = dec.decode(dataRead);
|
||||
assertEquals("HelloHello", actual);
|
||||
assertEquals(actual, "HelloHello");
|
||||
// Now attempt overwrite
|
||||
await Deno.writeFile(filename, data, { append: false });
|
||||
dataRead = Deno.readFileSync(filename);
|
||||
actual = dec.decode(dataRead);
|
||||
assertEquals("Hello", actual);
|
||||
assertEquals(actual, "Hello");
|
||||
// append not set should also overwrite
|
||||
await Deno.writeFile(filename, data);
|
||||
dataRead = Deno.readFileSync(filename);
|
||||
actual = dec.decode(dataRead);
|
||||
assertEquals("Hello", actual);
|
||||
assertEquals(actual, "Hello");
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -256,8 +256,6 @@ Deno.test(
|
|||
assert(e instanceof Error);
|
||||
assertEquals(e.name, "AbortError");
|
||||
}
|
||||
const stat = Deno.statSync(filename);
|
||||
assertEquals(stat.size, 0);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -276,8 +274,6 @@ Deno.test(
|
|||
} catch (e) {
|
||||
assertEquals(e, abortReason);
|
||||
}
|
||||
const stat = Deno.statSync(filename);
|
||||
assertEquals(stat.size, 0);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -295,8 +291,6 @@ Deno.test(
|
|||
} catch (e) {
|
||||
assertEquals(e, "Some string");
|
||||
}
|
||||
const stat = Deno.statSync(filename);
|
||||
assertEquals(stat.size, 0);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -315,8 +309,7 @@ Deno.test(
|
|||
assert(e instanceof Error);
|
||||
assertEquals(e.name, "AbortError");
|
||||
}
|
||||
const stat = Deno.statSync(filename);
|
||||
assertEquals(stat.size, 0);
|
||||
assertNotExists(filename);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -335,8 +328,7 @@ Deno.test(
|
|||
} catch (e) {
|
||||
assertEquals(e, abortReason);
|
||||
}
|
||||
const stat = Deno.statSync(filename);
|
||||
assertEquals(stat.size, 0);
|
||||
assertNotExists(filename);
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -356,7 +348,21 @@ Deno.test(
|
|||
} catch (e) {
|
||||
assertEquals(e, "Some string");
|
||||
}
|
||||
const stat = Deno.statSync(filename);
|
||||
assertEquals(stat.size, 0);
|
||||
assertNotExists(filename);
|
||||
},
|
||||
);
|
||||
|
||||
function assertNotExists(filename: string | URL) {
|
||||
if (pathExists(filename)) {
|
||||
throw new Error(`The file ${filename} exists.`);
|
||||
}
|
||||
}
|
||||
|
||||
function pathExists(path: string | URL) {
|
||||
try {
|
||||
Deno.statSync(path);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ Deno.test(
|
|||
const filename = Deno.makeTempDirSync() + "/test.txt";
|
||||
Deno.writeTextFileSync(filename, "Hello");
|
||||
const dataRead = Deno.readTextFileSync(filename);
|
||||
assertEquals("Hello", dataRead);
|
||||
assertEquals(dataRead, "Hello");
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -24,7 +24,7 @@ Deno.test(
|
|||
);
|
||||
Deno.writeTextFileSync(fileUrl, "Hello");
|
||||
const dataRead = Deno.readTextFileSync(fileUrl);
|
||||
assertEquals("Hello", dataRead);
|
||||
assertEquals(dataRead, "Hello");
|
||||
|
||||
Deno.removeSync(fileUrl, { recursive: true });
|
||||
},
|
||||
|
@ -78,7 +78,7 @@ Deno.test(
|
|||
// Turn on create, should have no error
|
||||
Deno.writeTextFileSync(filename, data, { create: true });
|
||||
Deno.writeTextFileSync(filename, data, { create: false });
|
||||
assertEquals("Hello", Deno.readTextFileSync(filename));
|
||||
assertEquals(Deno.readTextFileSync(filename), "Hello");
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -89,13 +89,13 @@ Deno.test(
|
|||
const filename = Deno.makeTempDirSync() + "/test.txt";
|
||||
Deno.writeTextFileSync(filename, data);
|
||||
Deno.writeTextFileSync(filename, data, { append: true });
|
||||
assertEquals("HelloHello", Deno.readTextFileSync(filename));
|
||||
assertEquals(Deno.readTextFileSync(filename), "HelloHello");
|
||||
// Now attempt overwrite
|
||||
Deno.writeTextFileSync(filename, data, { append: false });
|
||||
assertEquals("Hello", Deno.readTextFileSync(filename));
|
||||
assertEquals(Deno.readTextFileSync(filename), "Hello");
|
||||
// append not set should also overwrite
|
||||
Deno.writeTextFileSync(filename, data);
|
||||
assertEquals("Hello", Deno.readTextFileSync(filename));
|
||||
assertEquals(Deno.readTextFileSync(filename), "Hello");
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -105,7 +105,7 @@ Deno.test(
|
|||
const filename = Deno.makeTempDirSync() + "/test.txt";
|
||||
await Deno.writeTextFile(filename, "Hello");
|
||||
const dataRead = Deno.readTextFileSync(filename);
|
||||
assertEquals("Hello", dataRead);
|
||||
assertEquals(dataRead, "Hello");
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -118,7 +118,7 @@ Deno.test(
|
|||
);
|
||||
await Deno.writeTextFile(fileUrl, "Hello");
|
||||
const dataRead = Deno.readTextFileSync(fileUrl);
|
||||
assertEquals("Hello", dataRead);
|
||||
assertEquals(dataRead, "Hello");
|
||||
|
||||
Deno.removeSync(fileUrl, { recursive: true });
|
||||
},
|
||||
|
@ -178,7 +178,7 @@ Deno.test(
|
|||
// Turn on create, should have no error
|
||||
await Deno.writeTextFile(filename, data, { create: true });
|
||||
await Deno.writeTextFile(filename, data, { create: false });
|
||||
assertEquals("Hello", Deno.readTextFileSync(filename));
|
||||
assertEquals(Deno.readTextFileSync(filename), "Hello");
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -189,12 +189,12 @@ Deno.test(
|
|||
const filename = Deno.makeTempDirSync() + "/test.txt";
|
||||
await Deno.writeTextFile(filename, data);
|
||||
await Deno.writeTextFile(filename, data, { append: true });
|
||||
assertEquals("HelloHello", Deno.readTextFileSync(filename));
|
||||
assertEquals(Deno.readTextFileSync(filename), "HelloHello");
|
||||
// Now attempt overwrite
|
||||
await Deno.writeTextFile(filename, data, { append: false });
|
||||
assertEquals("Hello", Deno.readTextFileSync(filename));
|
||||
assertEquals(Deno.readTextFileSync(filename), "Hello");
|
||||
// append not set should also overwrite
|
||||
await Deno.writeTextFile(filename, data);
|
||||
assertEquals("Hello", Deno.readTextFileSync(filename));
|
||||
assertEquals(Deno.readTextFileSync(filename), "Hello");
|
||||
},
|
||||
);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::RcLike;
|
||||
use crate::Resource;
|
||||
use futures::future::FusedFuture;
|
||||
use futures::future::Future;
|
||||
use futures::future::TryFuture;
|
||||
|
@ -8,6 +9,7 @@ use futures::task::Context;
|
|||
use futures::task::Poll;
|
||||
use pin_project::pin_project;
|
||||
use std::any::type_name;
|
||||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::fmt::Display;
|
||||
|
@ -84,6 +86,16 @@ impl<F: Future> FusedFuture for Cancelable<F> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Resource for CancelHandle {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"cancellation".into()
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project(project = TryCancelableProjection)]
|
||||
#[derive(Debug)]
|
||||
pub struct TryCancelable<F> {
|
||||
|
|
|
@ -12,6 +12,7 @@ use deno_core::include_js_files;
|
|||
use deno_core::op;
|
||||
use deno_core::url::Url;
|
||||
use deno_core::ByteString;
|
||||
use deno_core::CancelHandle;
|
||||
use deno_core::Extension;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
|
@ -107,6 +108,7 @@ pub fn init<P: TimersPermission + 'static>(
|
|||
compression::op_compression_finish::decl(),
|
||||
op_now::decl::<P>(),
|
||||
op_timer_handle::decl(),
|
||||
op_cancel_handle::decl(),
|
||||
op_sleep::decl(),
|
||||
op_sleep_sync::decl::<P>(),
|
||||
])
|
||||
|
@ -352,6 +354,13 @@ fn op_encoding_encode_into(
|
|||
})
|
||||
}
|
||||
|
||||
/// Creates a [`CancelHandle`] resource that can be used to cancel invocations of certain ops.
|
||||
#[op]
|
||||
pub fn op_cancel_handle(state: &mut OpState) -> Result<ResourceId, AnyError> {
|
||||
let rid = state.resource_table.add(CancelHandle::new());
|
||||
Ok(rid)
|
||||
}
|
||||
|
||||
pub fn get_declaration() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_web.d.ts")
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
"use strict";
|
||||
((window) => {
|
||||
const { stat, statSync, chmod, chmodSync } = window.__bootstrap.fs;
|
||||
const { open, openSync } = window.__bootstrap.files;
|
||||
const { build } = window.__bootstrap.build;
|
||||
const {
|
||||
TypedArrayPrototypeSubarray,
|
||||
} = window.__bootstrap.primordials;
|
||||
const core = window.__bootstrap.core;
|
||||
const { abortSignal } = window.__bootstrap;
|
||||
const { pathFromURL } = window.__bootstrap.util;
|
||||
|
||||
function writeFileSync(
|
||||
path,
|
||||
|
@ -14,33 +11,13 @@
|
|||
options = {},
|
||||
) {
|
||||
options.signal?.throwIfAborted();
|
||||
if (options.create !== undefined) {
|
||||
const create = !!options.create;
|
||||
if (!create) {
|
||||
// verify that file exists
|
||||
statSync(path);
|
||||
}
|
||||
}
|
||||
|
||||
const openOptions = options.append
|
||||
? { write: true, create: true, append: true }
|
||||
: { write: true, create: true, truncate: true };
|
||||
const file = openSync(path, openOptions);
|
||||
|
||||
if (
|
||||
options.mode !== undefined &&
|
||||
options.mode !== null &&
|
||||
build.os !== "windows"
|
||||
) {
|
||||
chmodSync(path, options.mode);
|
||||
}
|
||||
|
||||
let nwritten = 0;
|
||||
while (nwritten < data.length) {
|
||||
nwritten += file.writeSync(TypedArrayPrototypeSubarray(data, nwritten));
|
||||
}
|
||||
|
||||
file.close();
|
||||
core.opSync("op_write_file_sync", {
|
||||
path: pathFromURL(path),
|
||||
data,
|
||||
mode: options.mode,
|
||||
append: options.append ?? false,
|
||||
create: options.create ?? true,
|
||||
});
|
||||
}
|
||||
|
||||
async function writeFile(
|
||||
|
@ -48,38 +25,30 @@
|
|||
data,
|
||||
options = {},
|
||||
) {
|
||||
if (options.create !== undefined) {
|
||||
const create = !!options.create;
|
||||
if (!create) {
|
||||
// verify that file exists
|
||||
await stat(path);
|
||||
}
|
||||
let cancelRid;
|
||||
let abortHandler;
|
||||
if (options.signal) {
|
||||
options.signal.throwIfAborted();
|
||||
cancelRid = core.opSync("op_cancel_handle");
|
||||
abortHandler = () => core.tryClose(cancelRid);
|
||||
options.signal[abortSignal.add](abortHandler);
|
||||
}
|
||||
|
||||
const openOptions = options.append
|
||||
? { write: true, create: true, append: true }
|
||||
: { write: true, create: true, truncate: true };
|
||||
const file = await open(path, openOptions);
|
||||
|
||||
if (
|
||||
options.mode !== undefined &&
|
||||
options.mode !== null &&
|
||||
build.os !== "windows"
|
||||
) {
|
||||
await chmod(path, options.mode);
|
||||
}
|
||||
|
||||
const signal = options?.signal ?? null;
|
||||
let nwritten = 0;
|
||||
try {
|
||||
while (nwritten < data.length) {
|
||||
signal?.throwIfAborted();
|
||||
nwritten += await file.write(
|
||||
TypedArrayPrototypeSubarray(data, nwritten),
|
||||
);
|
||||
}
|
||||
await core.opAsync("op_write_file_async", {
|
||||
path: pathFromURL(path),
|
||||
data,
|
||||
mode: options.mode,
|
||||
append: options.append ?? false,
|
||||
create: options.create ?? true,
|
||||
cancelRid,
|
||||
});
|
||||
} finally {
|
||||
file.close();
|
||||
if (options.signal) {
|
||||
options.signal[abortSignal.remove](abortHandler);
|
||||
|
||||
// always throw the abort error when aborted
|
||||
options.signal.throwIfAborted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,9 @@ use deno_core::error::custom_error;
|
|||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op;
|
||||
use deno_core::CancelFuture;
|
||||
use deno_core::CancelHandle;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
|
||||
use deno_core::Extension;
|
||||
use deno_core::OpState;
|
||||
|
@ -23,6 +26,7 @@ use std::cell::RefCell;
|
|||
use std::convert::From;
|
||||
use std::env::{current_dir, set_current_dir, temp_dir};
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::io::{Error, Seek, SeekFrom};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
|
@ -40,6 +44,8 @@ pub fn init() -> Extension {
|
|||
.ops(vec![
|
||||
op_open_sync::decl(),
|
||||
op_open_async::decl(),
|
||||
op_write_file_sync::decl(),
|
||||
op_write_file_async::decl(),
|
||||
op_seek_sync::decl(),
|
||||
op_seek_async::decl(),
|
||||
op_fdatasync_sync::decl(),
|
||||
|
@ -117,7 +123,7 @@ pub struct OpenOptions {
|
|||
|
||||
fn open_helper(
|
||||
state: &mut OpState,
|
||||
args: OpenArgs,
|
||||
args: &OpenArgs,
|
||||
) -> Result<(PathBuf, std::fs::OpenOptions), AnyError> {
|
||||
let path = Path::new(&args.path).to_path_buf();
|
||||
|
||||
|
@ -136,7 +142,7 @@ fn open_helper(
|
|||
}
|
||||
|
||||
let permissions = state.borrow_mut::<Permissions>();
|
||||
let options = args.options;
|
||||
let options = &args.options;
|
||||
|
||||
if options.read {
|
||||
permissions.read.check(&path)?;
|
||||
|
@ -162,7 +168,7 @@ fn op_open_sync(
|
|||
state: &mut OpState,
|
||||
args: OpenArgs,
|
||||
) -> Result<ResourceId, AnyError> {
|
||||
let (path, open_options) = open_helper(state, args)?;
|
||||
let (path, open_options) = open_helper(state, &args)?;
|
||||
let std_file = open_options.open(&path).map_err(|err| {
|
||||
Error::new(err.kind(), format!("{}, open '{}'", err, path.display()))
|
||||
})?;
|
||||
|
@ -177,18 +183,112 @@ async fn op_open_async(
|
|||
state: Rc<RefCell<OpState>>,
|
||||
args: OpenArgs,
|
||||
) -> Result<ResourceId, AnyError> {
|
||||
let (path, open_options) = open_helper(&mut state.borrow_mut(), args)?;
|
||||
let (path, open_options) = open_helper(&mut state.borrow_mut(), &args)?;
|
||||
let tokio_file = tokio::fs::OpenOptions::from(open_options)
|
||||
.open(&path)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
Error::new(err.kind(), format!("{}, open '{}'", err, path.display()))
|
||||
})?;
|
||||
|
||||
let resource = StdFileResource::fs_file(tokio_file);
|
||||
let rid = state.borrow_mut().resource_table.add(resource);
|
||||
Ok(rid)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WriteFileArgs {
|
||||
path: String,
|
||||
mode: Option<u32>,
|
||||
append: bool,
|
||||
create: bool,
|
||||
data: ZeroCopyBuf,
|
||||
cancel_rid: Option<ResourceId>,
|
||||
}
|
||||
|
||||
impl WriteFileArgs {
|
||||
fn into_open_args_and_data(self) -> (OpenArgs, ZeroCopyBuf) {
|
||||
(
|
||||
OpenArgs {
|
||||
path: self.path,
|
||||
mode: self.mode,
|
||||
options: OpenOptions {
|
||||
read: false,
|
||||
write: true,
|
||||
create: self.create,
|
||||
truncate: !self.append,
|
||||
append: self.append,
|
||||
create_new: false,
|
||||
},
|
||||
},
|
||||
self.data,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_write_file_sync(
|
||||
state: &mut OpState,
|
||||
args: WriteFileArgs,
|
||||
) -> Result<(), AnyError> {
|
||||
let (open_args, data) = args.into_open_args_and_data();
|
||||
let (path, open_options) = open_helper(state, &open_args)?;
|
||||
write_file(&path, open_options, &open_args, data)
|
||||
}
|
||||
|
||||
#[op]
|
||||
async fn op_write_file_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
args: WriteFileArgs,
|
||||
) -> Result<(), AnyError> {
|
||||
let cancel_handle = match args.cancel_rid {
|
||||
Some(cancel_rid) => state
|
||||
.borrow_mut()
|
||||
.resource_table
|
||||
.get::<CancelHandle>(cancel_rid)
|
||||
.ok(),
|
||||
None => None,
|
||||
};
|
||||
let (open_args, data) = args.into_open_args_and_data();
|
||||
let (path, open_options) = open_helper(&mut *state.borrow_mut(), &open_args)?;
|
||||
let write_future = tokio::task::spawn_blocking(move || {
|
||||
write_file(&path, open_options, &open_args, data)
|
||||
});
|
||||
if let Some(cancel_handle) = cancel_handle {
|
||||
write_future.or_cancel(cancel_handle).await???;
|
||||
} else {
|
||||
write_future.await??;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_file(
|
||||
path: &Path,
|
||||
open_options: std::fs::OpenOptions,
|
||||
_open_args: &OpenArgs,
|
||||
data: ZeroCopyBuf,
|
||||
) -> Result<(), AnyError> {
|
||||
let mut std_file = open_options.open(path).map_err(|err| {
|
||||
Error::new(err.kind(), format!("{}, open '{}'", err, path.display()))
|
||||
})?;
|
||||
|
||||
// need to chmod the file if it already exists and a mode is specified
|
||||
#[cfg(unix)]
|
||||
if let Some(mode) = &_open_args.mode {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let permissions = PermissionsExt::from_mode(mode & 0o777);
|
||||
std_file
|
||||
.set_permissions(permissions)
|
||||
.map_err(|err: Error| {
|
||||
Error::new(err.kind(), format!("{}, chmod '{}'", err, path.display()))
|
||||
})?;
|
||||
}
|
||||
|
||||
std_file.write_all(&data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SeekArgs {
|
||||
|
@ -571,28 +671,12 @@ pub struct ChmodArgs {
|
|||
|
||||
#[op]
|
||||
fn op_chmod_sync(state: &mut OpState, args: ChmodArgs) -> Result<(), AnyError> {
|
||||
let path = Path::new(&args.path).to_path_buf();
|
||||
let path = Path::new(&args.path);
|
||||
let mode = args.mode & 0o777;
|
||||
let err_mapper = |err: Error| {
|
||||
Error::new(err.kind(), format!("{}, chmod '{}'", err, path.display()))
|
||||
};
|
||||
|
||||
state.borrow_mut::<Permissions>().write.check(&path)?;
|
||||
state.borrow_mut::<Permissions>().write.check(path)?;
|
||||
debug!("op_chmod_sync {} {:o}", path.display(), mode);
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let permissions = PermissionsExt::from_mode(mode);
|
||||
std::fs::set_permissions(&path, permissions).map_err(err_mapper)?;
|
||||
Ok(())
|
||||
}
|
||||
// TODO Implement chmod for Windows (#4357)
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
// Still check file/dir exists on Windows
|
||||
let _metadata = std::fs::metadata(&path).map_err(err_mapper)?;
|
||||
Err(generic_error("Not implemented"))
|
||||
}
|
||||
raw_chmod(path, mode)
|
||||
}
|
||||
|
||||
#[op]
|
||||
|
@ -610,28 +694,32 @@ async fn op_chmod_async(
|
|||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
debug!("op_chmod_async {} {:o}", path.display(), mode);
|
||||
let err_mapper = |err: Error| {
|
||||
Error::new(err.kind(), format!("{}, chmod '{}'", err, path.display()))
|
||||
};
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let permissions = PermissionsExt::from_mode(mode);
|
||||
std::fs::set_permissions(&path, permissions).map_err(err_mapper)?;
|
||||
Ok(())
|
||||
}
|
||||
// TODO Implement chmod for Windows (#4357)
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
// Still check file/dir exists on Windows
|
||||
let _metadata = std::fs::metadata(&path).map_err(err_mapper)?;
|
||||
Err(not_supported())
|
||||
}
|
||||
raw_chmod(&path, mode)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn raw_chmod(path: &Path, _raw_mode: u32) -> Result<(), AnyError> {
|
||||
let err_mapper = |err: Error| {
|
||||
Error::new(err.kind(), format!("{}, chmod '{}'", err, path.display()))
|
||||
};
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let permissions = PermissionsExt::from_mode(_raw_mode);
|
||||
std::fs::set_permissions(&path, permissions).map_err(err_mapper)?;
|
||||
Ok(())
|
||||
}
|
||||
// TODO Implement chmod for Windows (#4357)
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
// Still check file/dir exists on Windows
|
||||
let _metadata = std::fs::metadata(&path).map_err(err_mapper)?;
|
||||
Err(not_supported())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ChownArgs {
|
||||
|
|
Loading…
Reference in a new issue