summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/deno_error.rs12
-rw-r--r--cli/ops/os.rs19
-rw-r--r--cli/worker.rs4
-rw-r--r--js/dispatch.ts1
-rw-r--r--js/lib.deno_runtime.d.ts10
-rw-r--r--js/os.ts14
-rw-r--r--js/os_test.ts91
7 files changed, 143 insertions, 8 deletions
diff --git a/cli/deno_error.rs b/cli/deno_error.rs
index eb885adb8..551547e26 100644
--- a/cli/deno_error.rs
+++ b/cli/deno_error.rs
@@ -11,6 +11,7 @@ use hyper;
use reqwest;
use rustyline::error::ReadlineError;
use std;
+use std::env::VarError;
use std::error::Error;
use std::fmt;
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 {
fn kind(&self) -> 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::<uri::InvalidUri>().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
diff --git a/cli/ops/os.rs b/cli/ops/os.rs
index 770af404c..b35b76c2a 100644
--- a/cli/ops/os.rs
+++ b/cli/ops/os.rs
@@ -103,6 +103,25 @@ pub fn op_env(
}
#[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)]
struct Exit {
code: i32,
}
diff --git a/cli/worker.rs b/cli/worker.rs
index 6f5e6af41..9bdf2ae08 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -64,6 +64,10 @@ impl Worker {
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(
"home_dir",
state_.cli_op(json_op(state_.stateful_op(os::op_home_dir))),
);
diff --git a/js/dispatch.ts b/js/dispatch.ts
index 67c831e09..7f30707a7 100644
--- a/js/dispatch.ts
+++ b/js/dispatch.ts
@@ -11,6 +11,7 @@ export let OP_ENV: number;
export let OP_EXEC_PATH: number;
export let OP_UTIME: number;
export let OP_SET_ENV: number;
+export let OP_GET_ENV: number;
export let OP_HOME_DIR: number;
export let OP_START: number;
export let OP_APPLY_SOURCE_MAP: number;
diff --git a/js/lib.deno_runtime.d.ts b/js/lib.deno_runtime.d.ts
index d0a720769..780503106 100644
--- a/js/lib.deno_runtime.d.ts
+++ b/js/lib.deno_runtime.d.ts
@@ -44,6 +44,16 @@ declare namespace Deno {
export function env(): {
[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.
* Requires the `--allow-env` flag.
diff --git a/js/os.ts b/js/os.ts
index df30b3906..2fc06434a 100644
--- a/js/os.ts
+++ b/js/os.ts
@@ -37,18 +37,30 @@ function setEnv(key: string, value: string): void {
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
* property in the object will set that variable in the environment for
* the process. The environment object will only accept `string`s
* as values.
*
+ * console.log(Deno.env("SHELL"));
* 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(): { [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);
return new Proxy(env, {
set(obj, prop: string, value: string): boolean {
diff --git a/js/os_test.ts b/js/os_test.ts
index ad3772631..0d07df1b4 100644
--- a/js/os_test.ts
+++ b/js/os_test.ts
@@ -14,21 +14,98 @@ testPerm({ env: true }, function envSuccess(): void {
env.test_var = "Hello World";
const newEnv = Deno.env();
assertEquals(env.test_var, newEnv.test_var);
+ assertEquals(Deno.env("test_var"), env.test_var);
});
-test(function envFailure(): void {
- let caughtError = false;
+testPerm({ env: true }, function envNotFound(): void {
+ const r = Deno.env("env_var_does_not_exist!");
+ assertEquals(r, undefined);
+});
+
+test(function envPermissionDenied1(): void {
+ let err;
try {
Deno.env();
- } catch (err) {
- caughtError = true;
- assertEquals(err.kind, Deno.ErrorKind.PermissionDenied);
- assertEquals(err.name, "PermissionDenied");
+ } catch (e) {
+ err = e;
}
+ assertNotEquals(err, undefined);
+ assertEquals(err.kind, Deno.ErrorKind.PermissionDenied);
+ assertEquals(err.name, "PermissionDenied");
+});
- assert(caughtError);
+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 {
console.log("pid", Deno.pid);
assert(Deno.pid > 0);