1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-26 00:59:24 -05:00

feat: Add support for passing a key to Deno.env() (#2952)

This adds a new op to get a single env var.
This commit is contained in:
Jed Fox 2019-10-02 11:55:28 -04:00 committed by Ryan Dahl
parent c920c5f62a
commit 99eec73b4b
7 changed files with 144 additions and 9 deletions

View file

@ -11,6 +11,7 @@ use hyper;
use reqwest; use reqwest;
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use std; use std;
use std::env::VarError;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
use std::io; use std::io;
@ -136,6 +137,16 @@ impl GetErrorKind for ModuleResolutionError {
} }
} }
impl GetErrorKind for VarError {
fn kind(&self) -> ErrorKind {
use VarError::*;
match self {
NotPresent => ErrorKind::NotFound,
NotUnicode(..) => ErrorKind::InvalidData,
}
}
}
impl GetErrorKind for io::Error { impl GetErrorKind for io::Error {
fn kind(&self) -> ErrorKind { fn kind(&self) -> ErrorKind {
use io::ErrorKind::*; use io::ErrorKind::*;
@ -294,6 +305,7 @@ impl GetErrorKind for dyn AnyError {
.or_else(|| self.downcast_ref::<StaticError>().map(Get::kind)) .or_else(|| self.downcast_ref::<StaticError>().map(Get::kind))
.or_else(|| self.downcast_ref::<uri::InvalidUri>().map(Get::kind)) .or_else(|| self.downcast_ref::<uri::InvalidUri>().map(Get::kind))
.or_else(|| self.downcast_ref::<url::ParseError>().map(Get::kind)) .or_else(|| self.downcast_ref::<url::ParseError>().map(Get::kind))
.or_else(|| self.downcast_ref::<VarError>().map(Get::kind))
.or_else(|| self.downcast_ref::<ReadlineError>().map(Get::kind)) .or_else(|| self.downcast_ref::<ReadlineError>().map(Get::kind))
.or_else(|| { .or_else(|| {
self self

View file

@ -102,6 +102,25 @@ pub fn op_env(
Ok(JsonOp::Sync(json!(v))) Ok(JsonOp::Sync(json!(v)))
} }
#[derive(Deserialize)]
struct GetEnv {
key: String,
}
pub fn op_get_env(
state: &ThreadSafeState,
args: Value,
_zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> {
let args: GetEnv = serde_json::from_value(args)?;
state.check_env()?;
let r = match env::var(args.key) {
Err(env::VarError::NotPresent) => json!([]),
v => json!([v?]),
};
Ok(JsonOp::Sync(r))
}
#[derive(Deserialize)] #[derive(Deserialize)]
struct Exit { struct Exit {
code: i32, code: i32,

View file

@ -63,6 +63,10 @@ impl Worker {
"set_env", "set_env",
state_.cli_op(json_op(state_.stateful_op(os::op_set_env))), state_.cli_op(json_op(state_.stateful_op(os::op_set_env))),
); );
i.register_op(
"get_env",
state_.cli_op(json_op(state_.stateful_op(os::op_get_env))),
);
i.register_op( i.register_op(
"home_dir", "home_dir",
state_.cli_op(json_op(state_.stateful_op(os::op_home_dir))), state_.cli_op(json_op(state_.stateful_op(os::op_home_dir))),

View file

@ -11,6 +11,7 @@ export let OP_ENV: number;
export let OP_EXEC_PATH: number; export let OP_EXEC_PATH: number;
export let OP_UTIME: number; export let OP_UTIME: number;
export let OP_SET_ENV: number; export let OP_SET_ENV: number;
export let OP_GET_ENV: number;
export let OP_HOME_DIR: number; export let OP_HOME_DIR: number;
export let OP_START: number; export let OP_START: number;
export let OP_APPLY_SOURCE_MAP: number; export let OP_APPLY_SOURCE_MAP: number;

View file

@ -44,6 +44,16 @@ declare namespace Deno {
export function env(): { export function env(): {
[index: string]: string; [index: string]: string;
}; };
/** Returns the value of an environment variable at invocation.
* If the variable is not present, `undefined` will be returned.
*
* const myEnv = Deno.env();
* console.log(myEnv.SHELL);
* myEnv.TEST_VAR = "HELLO";
* const newEnv = Deno.env();
* console.log(myEnv.TEST_VAR == newEnv.TEST_VAR);
*/
export function env(key: string): string | undefined;
/** /**
* Returns the current user's home directory. * Returns the current user's home directory.
* Requires the `--allow-env` flag. * Requires the `--allow-env` flag.

View file

@ -37,18 +37,30 @@ function setEnv(key: string, value: string): void {
sendSync(dispatch.OP_SET_ENV, { key, value }); sendSync(dispatch.OP_SET_ENV, { key, value });
} }
function getEnv(key: string): string | undefined {
return sendSync(dispatch.OP_GET_ENV, { key })[0];
}
/** Returns a snapshot of the environment variables at invocation. Mutating a /** Returns a snapshot of the environment variables at invocation. Mutating a
* property in the object will set that variable in the environment for * property in the object will set that variable in the environment for
* the process. The environment object will only accept `string`s * the process. The environment object will only accept `string`s
* as values. * as values.
* *
* console.log(Deno.env("SHELL"));
* const myEnv = Deno.env(); * const myEnv = Deno.env();
* console.log(myEnv.SHELL); * console.log(myEnv.SHELL);
* myEnv.TEST_VAR = "HELLO"; * myEnv.TEST_VAR = "HELLO";
* const newEnv = Deno.env(); * const newEnv = Deno.env();
* console.log(myEnv.TEST_VAR == newEnv.TEST_VAR); * console.log(myEnv.TEST_VAR == newEnv.TEST_VAR);
*/ */
export function env(): { [index: string]: string } { export function env(): { [index: string]: string };
export function env(key: string): string | undefined;
export function env(
key?: string
): { [index: string]: string } | string | undefined {
if (key) {
return getEnv(key);
}
const env = sendSync(dispatch.OP_ENV); const env = sendSync(dispatch.OP_ENV);
return new Proxy(env, { return new Proxy(env, {
set(obj, prop: string, value: string): boolean { set(obj, prop: string, value: string): boolean {

View file

@ -14,21 +14,98 @@ testPerm({ env: true }, function envSuccess(): void {
env.test_var = "Hello World"; env.test_var = "Hello World";
const newEnv = Deno.env(); const newEnv = Deno.env();
assertEquals(env.test_var, newEnv.test_var); assertEquals(env.test_var, newEnv.test_var);
assertEquals(Deno.env("test_var"), env.test_var);
}); });
test(function envFailure(): void { testPerm({ env: true }, function envNotFound(): void {
let caughtError = false; const r = Deno.env("env_var_does_not_exist!");
assertEquals(r, undefined);
});
test(function envPermissionDenied1(): void {
let err;
try { try {
Deno.env(); Deno.env();
} catch (err) { } catch (e) {
caughtError = true; err = e;
assertEquals(err.kind, Deno.ErrorKind.PermissionDenied);
assertEquals(err.name, "PermissionDenied");
} }
assertNotEquals(err, undefined);
assert(caughtError); assertEquals(err.kind, Deno.ErrorKind.PermissionDenied);
assertEquals(err.name, "PermissionDenied");
}); });
test(function envPermissionDenied2(): void {
let err;
try {
Deno.env("PATH");
} catch (e) {
err = e;
}
assertNotEquals(err, undefined);
assertEquals(err.kind, Deno.ErrorKind.PermissionDenied);
assertEquals(err.name, "PermissionDenied");
});
if (Deno.build.os === "win") {
// This test verifies that on Windows, environment variables are
// case-insensitive. Case normalization needs be done using the collation
// that Windows uses, rather than naively using String.toLowerCase().
testPerm({ env: true, run: true }, async function envCaseInsensitive() {
// Utility function that runs a Deno subprocess with the environment
// specified in `inputEnv`. The subprocess reads the environment variables
// which are in the keys of `expectedEnv` and writes them to stdout as JSON.
// It is then verified that these match with the values of `expectedEnv`.
const checkChildEnv = async (inputEnv, expectedEnv): Promise<void> => {
const src = `
console.log(
${JSON.stringify(Object.keys(expectedEnv))}.map(k => Deno.env(k))
)`;
const proc = Deno.run({
args: [Deno.execPath(), "eval", src],
env: inputEnv,
stdout: "piped"
});
const status = await proc.status();
assertEquals(status.success, true);
const expectedValues = Object.values(expectedEnv);
const actualValues = JSON.parse(
new TextDecoder().decode(await proc.output())
);
assertEquals(actualValues, expectedValues);
};
assertEquals(Deno.env("path"), Deno.env("PATH"));
assertEquals(Deno.env("Path"), Deno.env("PATH"));
// Check 'foo', 'Foo' and 'Foo' are case folded.
await checkChildEnv({ foo: "X" }, { foo: "X", Foo: "X", FOO: "X" });
// Check that 'µ' and 'Μ' are not case folded.
const lc1 = "µ";
const uc1 = lc1.toUpperCase();
assertNotEquals(lc1, uc1);
await checkChildEnv(
{ [lc1]: "mu", [uc1]: "MU" },
{ [lc1]: "mu", [uc1]: "MU" }
);
// Check that 'dž' and 'DŽ' are folded, but 'Dž' is preserved.
const c2 = "Dž";
const lc2 = c2.toLowerCase();
const uc2 = c2.toUpperCase();
assertNotEquals(c2, lc2);
assertNotEquals(c2, uc2);
await checkChildEnv(
{ [c2]: "Dz", [lc2]: "dz" },
{ [c2]: "Dz", [lc2]: "dz", [uc2]: "dz" }
);
await checkChildEnv(
{ [c2]: "Dz", [uc2]: "DZ" },
{ [c2]: "Dz", [uc2]: "DZ", [lc2]: "DZ" }
);
});
}
test(function osPid(): void { test(function osPid(): void {
console.log("pid", Deno.pid); console.log("pid", Deno.pid);
assert(Deno.pid > 0); assert(Deno.pid > 0);