deno 2.9.8

Provides the deno executable
// Copyright 2018-2026 the Deno authors. MIT license.

use std::str::FromStr;

use deno_cli_parser::CliError;
use deno_cli_parser::CliErrorKind;
use deno_runtime::deno_permissions::NetDescriptor;

#[derive(Debug, PartialEq, Eq)]
pub struct ParsePortError(String);

#[derive(Debug, PartialEq, Eq)]
pub struct BarePort(u16);

impl FromStr for BarePort {
  type Err = ParsePortError;
  fn from_str(s: &str) -> Result<BarePort, ParsePortError> {
    if s.starts_with(':') {
      match s.split_at(1).1.parse::<u16>() {
        Ok(port) => Ok(BarePort(port)),
        Err(e) => Err(ParsePortError(e.to_string())),
      }
    } else {
      Err(ParsePortError(
        "Bare Port doesn't start with ':'".to_string(),
      ))
    }
  }
}

/// Expands "bare port" paths (eg. ":8080") into full paths with hosts. It
/// expands to such paths into 3 paths with following hosts: `0.0.0.0:port`,
/// `127.0.0.1:port` and `localhost:port`.
pub fn parse(paths: Vec<String>) -> Result<Vec<String>, CliError> {
  let mut out: Vec<String> = vec![];
  for host_and_port in paths.into_iter() {
    if let Ok(port) = host_and_port.parse::<BarePort>() {
      // we got bare port, let's add default hosts
      for host in ["0.0.0.0", "127.0.0.1", "localhost"].iter() {
        out.push(format!("{}:{}", host, port.0));
      }
    } else {
      NetDescriptor::parse_for_list(&host_and_port).map_err(|e| {
        CliError::new(CliErrorKind::InvalidValue, e.to_string())
      })?;
      out.push(host_and_port)
    }
  }
  Ok(out)
}

#[cfg(test)]
mod bare_port_tests {
  use super::BarePort;
  use super::ParsePortError;

  #[test]
  fn bare_port_parsed() {
    let expected = BarePort(8080);
    let actual = ":8080".parse::<BarePort>();
    assert_eq!(actual, Ok(expected));
  }

  #[test]
  fn bare_port_parse_error1() {
    let expected =
      ParsePortError("Bare Port doesn't start with ':'".to_string());
    let actual = "8080".parse::<BarePort>();
    assert_eq!(actual, Err(expected));
  }

  #[test]
  fn bare_port_parse_error2() {
    let actual = ":65536".parse::<BarePort>();
    assert!(actual.is_err());
  }

  #[test]
  fn bare_port_parse_error3() {
    let actual = ":14u16".parse::<BarePort>();
    assert!(actual.is_err());
  }

  #[test]
  fn bare_port_parse_error4() {
    let actual = "Deno".parse::<BarePort>();
    assert!(actual.is_err());
  }

  #[test]
  fn bare_port_parse_error5() {
    let actual = "deno.land:8080".parse::<BarePort>();
    assert!(actual.is_err());
  }
}

#[cfg(test)]
mod tests {
  use super::parse;

  // Creates vector of strings, Vec<String>
  macro_rules! svec {
      ($($x:expr),*) => (vec![$($x.to_string()),*]);
  }

  #[test]
  fn parse_net_args_() {
    let entries = svec![
      "deno.land",
      "deno.land:80",
      "*.deno.land",
      "[::]",
      "[::1]",
      "127.0.0.1",
      "[::1]",
      "1.2.3.4:5678",
      "0.0.0.0:5678",
      "127.0.0.1:5678",
      "[::]:5678",
      "[::1]:5678",
      "localhost:5678",
      "[::1]:8080",
      "[::]:8000",
      "[::1]:8000",
      "localhost:8000",
      "0.0.0.0:4545",
      "127.0.0.1:4545",
      "999.0.88.1:80",
      "127.0.0.0/24",
      "192.168.1.0/24",
      "10.0.0.0/8"
    ];
    let expected = svec![
      "deno.land",
      "deno.land:80",
      "*.deno.land",
      "[::]",
      "[::1]",
      "127.0.0.1",
      "[::1]",
      "1.2.3.4:5678",
      "0.0.0.0:5678",
      "127.0.0.1:5678",
      "[::]:5678",
      "[::1]:5678",
      "localhost:5678",
      "[::1]:8080",
      "[::]:8000",
      "[::1]:8000",
      "localhost:8000",
      "0.0.0.0:4545",
      "127.0.0.1:4545",
      "999.0.88.1:80",
      "127.0.0.0/24",
      "192.168.1.0/24",
      "10.0.0.0/8"
    ];
    let actual = parse(entries).unwrap();
    assert_eq!(actual, expected);
  }

  #[test]
  fn parse_net_args_expansion() {
    let entries = svec![":8080"];
    let expected = svec!["0.0.0.0:8080", "127.0.0.1:8080", "localhost:8080"];
    let actual = parse(entries).unwrap();
    assert_eq!(actual, expected);
  }

  #[test]
  fn parse_net_args_unix_socket() {
    let entries = svec!["unix:/var/run/docker.sock"];
    let expected = svec!["unix:/var/run/docker.sock"];
    let actual = parse(entries).unwrap();
    assert_eq!(actual, expected);
  }

  #[test]
  fn parse_net_args_unix_socket_relative_error() {
    assert!(parse(svec!["unix:relative.sock"]).is_err());
    assert!(parse(svec!["unix:"]).is_err());
  }

  #[cfg(windows)]
  #[test]
  fn parse_net_args_windows_named_pipe() {
    let entries = svec![r"unix:\\.\pipe\app"];
    let expected = svec![r"unix:\\.\pipe\app"];
    let actual = parse(entries).unwrap();
    assert_eq!(actual, expected);
  }

  #[test]
  fn parse_net_args_drive_letter_unix_socket() {
    let entries = svec![r"unix:C:\sockets\app.sock"];
    #[cfg(windows)]
    {
      let actual = parse(entries).unwrap();
      assert_eq!(actual, svec![r"unix:C:\sockets\app.sock"]);
    }
    #[cfg(not(windows))]
    {
      assert!(parse(entries).is_err());
    }
  }

  #[test]
  fn parse_net_args_ipv6() {
    let entries = svec!["[::1]", "[::]:5678", "[::1]:5678"];
    let expected = svec!["[::1]", "[::]:5678", "[::1]:5678"];
    let actual = parse(entries).unwrap();
    assert_eq!(actual, expected);
  }

  #[test]
  fn parse_net_args_ipv6_error1() {
    let entries = svec![":::"];
    assert!(parse(entries).is_err());
  }

  #[test]
  fn parse_net_args_ipv6_error2() {
    let entries = svec!["::1"];
    assert!(parse(entries).is_err());
  }

  #[test]
  fn parse_net_args_ipv6_error3() {
    let entries = svec!["::"];
    assert!(parse(entries).is_err());
  }

  #[test]
  fn parse_net_args_ipv6_error4() {
    let entries = svec!["::cafe"];
    assert!(parse(entries).is_err());
  }

  #[test]
  fn parse_net_args_ipv6_error5() {
    let entries = svec!["1::1"];
    assert!(parse(entries).is_err());
  }

  #[test]
  fn parse_net_args_ipv6_error6() {
    let entries = svec!["0123:4567:890a:bcde:fg::"];
    assert!(parse(entries).is_err());
  }

  #[test]
  fn parse_net_args_ipv6_error7() {
    let entries = svec!["[::q]:8080"];
    assert!(parse(entries).is_err());
  }
}