From c0e93de2b7a73d01cabf68def4a2b1ebbe0cd43d Mon Sep 17 00:00:00 2001 From: Jessie Chatham Spencer <--global> Date: Sat, 18 Feb 2023 12:07:52 +0100 Subject: [PATCH] Move `ame projectsrc` to separate file Issue: #121 This is a part of the CLI refactor to move subcommands to separate files. This commit creates a separate module for the projectsrc subcommand. Extra: Write doc comments for projetsrc and secret sub commands. remove web/tree --- .github/workflows/action.yaml | 2 +- Cargo.lock | 1 + cli/Cargo.toml | 1 + cli/src/lib.rs | 12 +- cli/src/main.rs | 268 +-------- cli/src/projectsrc.rs | 284 +++++++++ cli/src/secrets.rs | 26 +- cli/tests/cli.rs | 114 ++++ cli/tests/snapshots/cli__ame_run_task-2.snap | 2 +- ..., \"-s\", \"org-secret\"]::false.snap.new" | 6 + ...m__TeaInSpace__ame-demo.git\"]::true.snap" | 4 +- .../snapshots/cli__can_list_project_srcs.snap | 8 + lib/src/lib.rs | 8 + web/tree | 559 ------------------ 14 files changed, 458 insertions(+), 837 deletions(-) create mode 100644 cli/src/projectsrc.rs create mode 100644 "cli/tests/snapshots/cli__can_create_project_source::case::[\"https:____github.com__TeaInSpace__ame-demo-private.git\", \"-s\", \"org-secret\"]::false.snap.new" create mode 100644 cli/tests/snapshots/cli__can_list_project_srcs.snap delete mode 100644 web/tree diff --git a/.github/workflows/action.yaml b/.github/workflows/action.yaml index aad93070..67e82e76 100644 --- a/.github/workflows/action.yaml +++ b/.github/workflows/action.yaml @@ -7,7 +7,7 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly + toolchain: nightly - uses: Swatinem/rust-cache@v2 with: shared-key: ame-ci-cache diff --git a/Cargo.lock b/Cargo.lock index 36ffd004..ec9f4f61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,6 +575,7 @@ dependencies = [ name = "cli" version = "0.1.0" dependencies = [ + "ame", "ame_client", "assert_cmd", "assert_fs", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 93c15dac..82f5fb64 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -51,3 +51,4 @@ serial_test = "0.9.0" temp-env = { version = "0.3.1", features = ["async_closure"] } rstest = "0.16.0" k8s-openapi = { version = "0.17.0", features = ["v1_23", "schemars" ] } +ame = { path = "../lib" } diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 5597a630..eb999d0c 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -1,6 +1,6 @@ pub mod project; -use ame_client::client_builder::AmeServiceClientCfg; +use ame_client::client_builder::{build_ame_client, AmeClient, AmeServiceClientCfg}; use envconfig::Envconfig; use http::uri::InvalidUri; @@ -55,6 +55,7 @@ pub enum Error { pub type Result = std::result::Result; +pub mod projectsrc; pub mod secrets; #[derive(Clone, Default, Deserialize, Serialize, Envconfig, PartialEq, Debug)] @@ -103,6 +104,15 @@ impl CliConfiguration { ..CliConfiguration::default() } } + + pub async fn ame_client(&self) -> Result { + Ok(build_ame_client(AmeServiceClientCfg { + disable_tls_cert_check: true, // TODO: the CLI needs some configuration for this. + endpoint: self.endpoint.parse().unwrap(), + id_token: self.id_token.clone(), + }) + .await?) + } } impl TryFrom for AmeServiceClientCfg { diff --git a/cli/src/main.rs b/cli/src/main.rs index 9a33a385..b01fdae4 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,21 +1,18 @@ -use std::time::Duration; - use ame_client::{ auth::browser_login, client_builder::{build_ame_client, AmeServiceClientCfg}, - GitProjectSource, ProjectSourceCfg, ProjectSourceListParams, ProjectSourceState, - ProjectSrcIdRequest, ProjectSrcPatchRequest, TaskIdentifier, + TaskIdentifier, }; use clap::{Parser, Subcommand}; use cli::{ project::Project, + projectsrc::ProjectSrcCommands, secrets::{exec_secret_command, SecretCommand}, CliConfiguration, Result, }; -use colored::Colorize; -use futures_util::StreamExt; + use http::StatusCode; -use spinners::Spinner; + use tonic::Request; #[derive(Parser)] @@ -47,31 +44,6 @@ enum Commands { Secret(SecretCommand), } -#[derive(Subcommand)] -enum ProjectSrcCommands { - Create { - repository: String, - #[arg(short, long)] - secret: Option, - #[arg(short, long)] - user: Option, - }, - - Delete { - repository: Option, - }, - - Edit { - repository: String, - #[arg(short, long)] - secret: Option, - #[arg(short, long)] - user: Option, - }, - - List, -} - #[tokio::main] async fn main() -> Result<()> { let cli = Cli::parse(); @@ -160,237 +132,7 @@ async fn main() -> Result<()> { .await?; Ok(()) } - Commands::Projectsrc(ProjectSrcCommands::Delete { repository }) => { - let mut client = build_ame_client(AmeServiceClientCfg { - disable_tls_cert_check: true, - endpoint: config.endpoint.parse().unwrap(), - id_token: config.id_token, - }) - .await?; - - let repository = repository.as_ref().unwrap(); - - let id = client - .get_project_src_id(Request::new(ProjectSrcIdRequest { - repo: repository.to_string(), - })) - .await? - .into_inner(); - - client.delete_project_src(Request::new(id)).await?; - - Ok(()) - } - - Commands::Projectsrc(ProjectSrcCommands::List) => { - let mut client = build_ame_client(AmeServiceClientCfg { - disable_tls_cert_check: true, - endpoint: config.endpoint.parse().unwrap(), - id_token: config.id_token, - }) - .await?; - - let srcs = client - .list_project_srcs(Request::new(ProjectSourceListParams {})) - .await? - .into_inner(); - - println!("{}", "Project Sources:".bright_white().bold()); - - for cfg in srcs.cfgs { - if let ProjectSourceCfg { - git: - Some(GitProjectSource { - repository, - username, - secret, - .. - }), - } = cfg - { - println!( - "{} {} {}", - repository, - username.unwrap_or("".to_string()), - secret.unwrap_or("".to_string()) - ); - } - } - - Ok(()) - } - Commands::Projectsrc(ProjectSrcCommands::Edit { - repository, - secret, - user, - }) => { - let mut client = build_ame_client(AmeServiceClientCfg { - disable_tls_cert_check: true, - endpoint: config.endpoint.parse().unwrap(), - id_token: config.id_token, - }) - .await?; - - let id = client - .get_project_src_id(Request::new(ProjectSrcIdRequest { - repo: repository.to_string(), - })) - .await? - .into_inner(); - - client - .update_project_src(Request::new(ProjectSrcPatchRequest { - id: Some(id.clone()), - cfg: Some(ame_client::ProjectSourceCfg { - git: Some(ame_client::GitProjectSource { - repository: repository.to_string(), - sync_interval: Some("10s".to_string()), - secret: secret.clone(), - username: user.clone(), - }), - }), - })) - .await? - .into_inner(); - - let mut sp = Spinner::new( - spinners::Spinners::Dots, - format!("{} project source", "Updating".bold().cyan()), - ); - - let mut strm = client - .watch_project_src(Request::new(id)) - .await? - .into_inner(); - - while let Some(entry) = strm.next().await { - if let Ok(ame_client::ProjectSourceStatus { state, reason, .. }) = entry { - match ProjectSourceState::from_i32(state) { - Some(ProjectSourceState::Synchronized) => { - sp.stop_and_persist( - " ", - format!("{} project source", "Updated".bold().green()), - ); - break; - } - Some(ProjectSourceState::Pending) => (), - Some(ProjectSourceState::Error) => { - sp.stop_and_persist( - " ", - format!( - "{} to synchronize project source, reason: {}", - "Failed".bold().red(), - reason.unwrap_or("no reason :(".to_string()), - ), - ); - std::process::exit(1); - } - _ => { - sp.stop_and_persist( - " ", - format!("{} to update project source", "Failed".bold().red()), - ); - std::process::exit(1); - } - } - } - } - - sp = Spinner::new( - spinners::Spinners::Dots, - format!("{} projects from source", "Creating".bold().cyan()), - ); - - tokio::time::sleep(Duration::from_secs(5)).await; - - sp.stop_and_persist( - " ", - format!("{} projects from source", "Created".bold().green()), - ); - - Ok(()) - } - Commands::Projectsrc(ProjectSrcCommands::Create { - repository, - secret, - user, - }) => { - let mut client = build_ame_client(AmeServiceClientCfg { - disable_tls_cert_check: true, - endpoint: config.endpoint.parse().unwrap(), - id_token: config.id_token, - }) - .await?; - - let id = client - .create_project_src(Request::new(ame_client::ProjectSourceCfg { - git: Some(ame_client::GitProjectSource { - repository: repository.to_string(), - sync_interval: Some("10s".to_string()), - secret: secret.clone(), - username: user.clone(), - }), - })) - .await? - .into_inner(); - - let mut sp = Spinner::new( - spinners::Spinners::Dots, - format!("{} project source", "Creating".bold().cyan()), - ); - - let mut strm = client - .watch_project_src(Request::new(id)) - .await? - .into_inner(); - - tokio::time::sleep(Duration::from_secs(2)).await; - - loop { - let entry = strm.next().await; - let Some(entry) = entry else { - sp.stop_and_persist( - " ", - format!("{} to create project source", "Failed".bold().red()), - ); - std::process::exit(1); - }; - if let Ok(ame_client::ProjectSourceStatus { state, reason, .. }) = entry { - match ProjectSourceState::from_i32(state) { - Some(ProjectSourceState::Synchronized) => { - sp.stop_and_persist( - " ", - format!("{} project source", "Created".bold().green()), - ); - break; - } - Some(ProjectSourceState::Pending) => (), - Some(ProjectSourceState::Error) => { - sp.stop_and_persist( - " ", - format!( - "{} to synchronize project source, reason: {}", - "Failed".bold().red(), - reason.unwrap_or("no reason :(".to_string()), - ), - ); - std::process::exit(1); - } - _ => { - sp.stop_and_persist( - " ", - format!("{} to create project source", "Failed".bold().red()), - ); - std::process::exit(1); - } - } - } - } - - sp.stop_and_persist(" ", format!("{} project source", "Created".bold().green())); - - Ok(()) - } + Commands::Projectsrc(cmd) => cmd.run(&config).await, Commands::Secret(cmd) => exec_secret_command(config, cmd).await, } } diff --git a/cli/src/projectsrc.rs b/cli/src/projectsrc.rs new file mode 100644 index 00000000..495bd80c --- /dev/null +++ b/cli/src/projectsrc.rs @@ -0,0 +1,284 @@ +use std::time::Duration; + +use ame_client::GitProjectSource; +use ame_client::ProjectSourceCfg; +use ame_client::ProjectSourceListParams; +use ame_client::ProjectSourceState; +use ame_client::ProjectSrcIdRequest; +use ame_client::ProjectSrcPatchRequest; +use clap::Subcommand; +use colored::Colorize; +use futures_util::StreamExt; +use spinners::Spinner; +use tonic::Request; + +use crate::CliConfiguration; +use crate::Result; + +/// handles all operations on Project Sources. +/// +/// uses project sources to track projects in a Git repositories. +#[derive(Subcommand)] +pub enum ProjectSrcCommands { + /// Create a new project source pointing to a Git repository. + /// + /// For private repositories provide a secret name and associated Git user name. + /// will look for the secret in the builtin secret store and any external integrated + /// stores such as Vault. + /// + /// Use `ame secret list` to view the available secrets. + Create { + /// A Git repository. + repository: String, + + /// The name of the secret. + #[arg(short, long)] + secret: Option, + + /// A Git user name that will work with the secret. + #[arg(short, long)] + user: Option, + }, + + /// Delete a project source. + Delete { + /// Git repository used to identify the Project Source. + repository: String, + }, + + /// Edit a Project Source pointing to a Git repository + /// + /// Any supplied arguments other then the repository will overwrite existing values. + /// + /// Example: edit my..git --secretrepostiory + /// + /// This update the secret used for my..git. + Edit { + /// Git repository + repository: String, + + /// The name of the secret. + #[arg(short, long)] + secret: Option, + + /// A Git user name that will work with the secret. + #[arg(short, long)] + user: Option, + }, + + /// List all Project Sources + List, +} + +impl ProjectSrcCommands { + pub async fn run(&self, cfg: &CliConfiguration) -> Result<()> { + let mut client = cfg.ame_client().await?; + + match self { + ProjectSrcCommands::Delete { repository } => { + let id = client + .get_project_src_id(Request::new(ProjectSrcIdRequest { + repo: repository.to_string(), + })) + .await? + .into_inner(); + + client.delete_project_src(Request::new(id)).await?; + } + + ProjectSrcCommands::List => { + let srcs = client + .list_project_srcs(Request::new(ProjectSourceListParams {})) + .await? + .into_inner(); + + println!("{}", "Project Sources:".bright_white().bold()); + + for cfg in srcs.cfgs { + if let ProjectSourceCfg { + git: + Some(GitProjectSource { + repository, + username, + secret, + .. + }), + } = cfg + { + println!( + "{} {} {}", + repository, + username.unwrap_or("".to_string()), + secret.unwrap_or("".to_string()) + ); + } + } + } + + ProjectSrcCommands::Create { + repository, + secret, + user, + } => { + let id = client + .create_project_src(Request::new(ame_client::ProjectSourceCfg { + git: Some(ame_client::GitProjectSource { + repository: repository.to_string(), + sync_interval: Some("10s".to_string()), + secret: secret.clone(), + username: user.clone(), + }), + })) + .await? + .into_inner(); + + let mut sp = Spinner::new( + spinners::Spinners::Dots, + format!("{} project source", "Creating".bold().cyan()), + ); + + // wait for initial status. + let mut retries = 100; + while (client + .get_project_src_status(Request::new(id.clone())) + .await) + .is_err() + { + if retries <= 0 { + todo!("get actual stream working"); + } + + retries -= 1; + + tokio::time::sleep(Duration::from_millis(100)).await; + } + + loop { + let status = client + .get_project_src_status(Request::new(id.clone())) + .await? + .into_inner(); + + match ProjectSourceState::from_i32(status.state) { + Some(ProjectSourceState::Synchronized) => { + sp.stop_and_persist( + " ", + format!("{} project source", "Created".bold().green()), + ); + break; + } + Some(ProjectSourceState::Pending) => (), + Some(ProjectSourceState::Error) => { + sp.stop_and_persist( + " ", + format!( + "{} to synchronize project source, reason: {}", + "Failed".bold().red(), + status.reason.unwrap_or("no reason :(".to_string()), + ), + ); + std::process::exit(1); + } + _ => { + sp.stop_and_persist( + " ", + format!("{} to create project source", "Failed".bold().red()), + ); + std::process::exit(1); + } + } + + tokio::time::sleep(Duration::from_millis(100)).await; + } + + // TODO: figure out why this panics + //sp.stop_and_persist(" ", format!("{} project source", "Created".bold().green())); + } + + ProjectSrcCommands::Edit { + repository, + secret, + user, + } => { + let id = client + .get_project_src_id(Request::new(ProjectSrcIdRequest { + repo: repository.to_string(), + })) + .await? + .into_inner(); + + client + .update_project_src(Request::new(ProjectSrcPatchRequest { + id: Some(id.clone()), + cfg: Some(ame_client::ProjectSourceCfg { + git: Some(ame_client::GitProjectSource { + repository: repository.to_string(), + sync_interval: Some("10s".to_string()), + secret: secret.clone(), + username: user.clone(), + }), + }), + })) + .await? + .into_inner(); + + let mut sp = Spinner::new( + spinners::Spinners::Dots, + format!("{} project source", "Updating".bold().cyan()), + ); + + let mut strm = client + .watch_project_src(Request::new(id)) + .await? + .into_inner(); + + while let Some(entry) = strm.next().await { + if let Ok(ame_client::ProjectSourceStatus { state, reason, .. }) = entry { + match ProjectSourceState::from_i32(state) { + Some(ProjectSourceState::Synchronized) => { + sp.stop_and_persist( + " ", + format!("{} project source", "Updated".bold().green()), + ); + break; + } + Some(ProjectSourceState::Pending) => (), + Some(ProjectSourceState::Error) => { + sp.stop_and_persist( + " ", + format!( + "{} to synchronize project source, reason: {}", + "Failed".bold().red(), + reason.unwrap_or("no reason :(".to_string()), + ), + ); + std::process::exit(1); + } + _ => { + sp.stop_and_persist( + " ", + format!("{} to update project source", "Failed".bold().red()), + ); + std::process::exit(1); + } + } + } + } + + sp = Spinner::new( + spinners::Spinners::Dots, + format!("{} projects from source", "Creating".bold().cyan()), + ); + + tokio::time::sleep(Duration::from_secs(5)).await; + + sp.stop_and_persist( + " ", + format!("{} projects from source", "Created".bold().green()), + ); + } + } + + Ok(()) + } +} diff --git a/cli/src/secrets.rs b/cli/src/secrets.rs index 80f6cbde..39f24e24 100644 --- a/cli/src/secrets.rs +++ b/cli/src/secrets.rs @@ -11,18 +11,28 @@ use tonic::Request; use crate::{CliConfiguration, Result}; +/// Manage secrets in secret store. #[derive(Subcommand)] pub enum SecretCommand { + /// Create a secret + /// + /// The key will be used to identify the secret, when used with other + /// resources. + /// + /// You will be prompted to input the secret value, how ever the value will not be + /// displayed for security. Create { + /// Key used to identify the secret. key: Option, - #[arg(short, long)] - value: Option, }, + /// Delete a secret Delete { + /// Key used to identify the secret. key: Option, }, + /// List all secrets List, } @@ -35,7 +45,7 @@ pub async fn exec_secret_command(cfg: CliConfiguration, cmd: &SecretCommand) -> .await?; match cmd { - SecretCommand::Create { key, value } => { + SecretCommand::Create { key } => { let secret = AmeSecret { key: key.clone().unwrap_or_else(|| { Input::new() @@ -43,12 +53,10 @@ pub async fn exec_secret_command(cfg: CliConfiguration, cmd: &SecretCommand) -> .interact() .unwrap() }), - value: value.clone().unwrap_or_else(|| { - Password::new() - .with_prompt("Please provide the secret value") - .interact() - .unwrap() - }), + value: Password::new() + .with_prompt("Please provide the secret value") + .interact() + .unwrap(), }; let mut spinner = Spinner::new( diff --git a/cli/tests/cli.rs b/cli/tests/cli.rs index 04a0db75..2dbb7efa 100644 --- a/cli/tests/cli.rs +++ b/cli/tests/cli.rs @@ -417,3 +417,117 @@ async fn can_train_validate_and_deploy_model() -> Result<(), Box Result<(), Box> { + let repo = "https://github.com/TeaInSpace/ame-demo.git"; + let project_src_ctrl = ProjectSrcCtrl::new(kube_client().await?, AME_NAMESPACE); + + project_src_ctrl + .create_project_src(&ame::grpc::ProjectSourceCfg::from_git_repo( + repo.to_string(), + )) + .await?; + + let mut cmd = Command::cargo_bin("cli")?; + cmd.arg("projectsrc") + .arg("delete") + .arg(repo) + .assert() + .success(); + + assert_eq!(project_src_ctrl.list_project_src().await?.len(), 0); + + Ok(()) +} + +#[ignore] +#[tokio::test] +#[serial] +async fn can_list_project_srcs() -> Result<(), Box> { + let repos = vec![ + "https://github.com/TeaInSpace/ame-demo.git", + "https://github.com/TeaInSpace/ame-template-demo.git", + ]; + let project_src_ctrl = ProjectSrcCtrl::new(kube_client().await?, AME_NAMESPACE); + + for repo in &repos { + project_src_ctrl + .create_project_src(&ame::grpc::ProjectSourceCfg::from_git_repo( + repo.to_string(), + )) + .await?; + } + + let mut cmd = Command::cargo_bin("cli")?; + let output = cmd.arg("projectsrc").arg("list").assert().success(); + + assert_snapshot!(&String::from_utf8(output.get_output().stdout.clone())?); + + for repo in &repos { + project_src_ctrl.delete_project_src_for_repo(repo).await?; + } + + Ok(()) +} + +#[ignore] +#[tokio::test] +#[serial] +async fn can_edit_project_src() -> Result<(), Box> { + let repo = "https://github.com/TeaInSpace/ame-demo.git"; + let project_src_ctrl = ProjectSrcCtrl::new(kube_client().await?, AME_NAMESPACE); + + let secret = "somesecret"; + let username = "myuser"; + + project_src_ctrl + .create_project_src(&ame::grpc::ProjectSourceCfg::from_git_repo( + repo.to_string(), + )) + .await?; + + let mut cmd = Command::cargo_bin("cli")?; + cmd.arg("projectsrc") + .arg("edit") + .arg(repo) + .arg("--secret") + .arg(secret) + .arg("--user") + .arg(username) + .assert() + .success(); + + assert_eq!(project_src_ctrl.list_project_src().await?.len(), 1); + + let project_src = project_src_ctrl.get_project_src_for_repo(repo).await?; + + assert_eq!( + project_src + .spec + .cfg + .git + .as_ref() + .unwrap() + .secret + .as_ref() + .expect("expect secret to exist"), + secret + ); + assert_eq!( + project_src + .spec + .cfg + .git + .unwrap() + .username + .expect("expect username to exist"), + username + ); + + project_src_ctrl.delete_project_src_for_repo(repo).await?; + + Ok(()) +} diff --git a/cli/tests/snapshots/cli__ame_run_task-2.snap b/cli/tests/snapshots/cli__ame_run_task-2.snap index b19ea1f0..d496d256 100644 --- a/cli/tests/snapshots/cli__ame_run_task-2.snap +++ b/cli/tests/snapshots/cli__ame_run_task-2.snap @@ -11,7 +11,7 @@ patching file Misc/NEWS.d/next/Build/2021-10-11-16-27-38.bpo-45405.iSfdW5.rst patching file configure patching file configure.ac Installed Python-3.8.10 to /home/ame/.pyenv/versions/3.8.10 -"redacted timestamp" INFO mlflow.utils.virtualenv: Creating a new environment /home/ame/.mlflow/envs/mlflow-ba49d5c683fae3c4cdbe6b1fb8ca971330b218f4 +"redacted timestamp" INFO mlflow.utils.virtualenv: Creating a new environment in /home/ame/.mlflow/envs/"redacted MLflow env ID"python created virtual environment "redacted" creator "redacted" seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/ame/.local/share/virtualenv) diff --git "a/cli/tests/snapshots/cli__can_create_project_source::case::[\"https:____github.com__TeaInSpace__ame-demo-private.git\", \"-s\", \"org-secret\"]::false.snap.new" "b/cli/tests/snapshots/cli__can_create_project_source::case::[\"https:____github.com__TeaInSpace__ame-demo-private.git\", \"-s\", \"org-secret\"]::false.snap.new" new file mode 100644 index 00000000..e4d09f09 --- /dev/null +++ "b/cli/tests/snapshots/cli__can_create_project_source::case::[\"https:____github.com__TeaInSpace__ame-demo-private.git\", \"-s\", \"org-secret\"]::false.snap.new" @@ -0,0 +1,6 @@ +--- +source: cli/tests/cli.rs +assertion_line: 242 +expression: "&String::from_utf8(output.get_output().stdout.clone())?" +--- + diff --git "a/cli/tests/snapshots/cli__can_create_project_source::case::[\"https:____github.com__TeaInSpace__ame-demo.git\"]::true.snap" "b/cli/tests/snapshots/cli__can_create_project_source::case::[\"https:____github.com__TeaInSpace__ame-demo.git\"]::true.snap" index 96b8eea6..8da0a773 100644 --- "a/cli/tests/snapshots/cli__can_create_project_source::case::[\"https:____github.com__TeaInSpace__ame-demo.git\"]::true.snap" +++ "b/cli/tests/snapshots/cli__can_create_project_source::case::[\"https:____github.com__TeaInSpace__ame-demo.git\"]::true.snap" @@ -2,6 +2,4 @@ source: cli/tests/cli.rs expression: "&String::from_utf8(output.get_output().stdout.clone())?" --- - ⠏ Creating project source ⠏ Creating project source ⠏ Creating project source ⠏ Creating project source ⠏ Creating project source ⠏ Creating project source ⠏ Creating project source Created project source - ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source ⠏ Creating projects from source Created projects from source - + diff --git a/cli/tests/snapshots/cli__can_list_project_srcs.snap b/cli/tests/snapshots/cli__can_list_project_srcs.snap new file mode 100644 index 00000000..f11ac69d --- /dev/null +++ b/cli/tests/snapshots/cli__can_list_project_srcs.snap @@ -0,0 +1,8 @@ +--- +source: cli/tests/cli.rs +expression: "&String::from_utf8(output.get_output().stdout.clone())?" +--- +Project Sources: +https://github.com/TeaInSpace/ame-demo.git +https://github.com/TeaInSpace/ame-template-demo.git + diff --git a/lib/src/lib.rs b/lib/src/lib.rs index f32f72cc..85f0b5c6 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -94,6 +94,14 @@ pub mod grpc { }), } } + pub fn from_git_repo(repo: String) -> Self { + Self { + git: Some(GitProjectSource { + repository: repo, + ..GitProjectSource::default() + }), + } + } } } diff --git a/web/tree b/web/tree deleted file mode 100644 index c15bfb99..00000000 --- a/web/tree +++ /dev/null @@ -1,559 +0,0 @@ -start-axum v0.1.0 (/home/jmintb/Sync/lab/tea-in-space/test) -├── cfg-if v1.0.0 -├── console_error_panic_hook v0.1.7 -│ ├── cfg-if v1.0.0 -│ └── wasm-bindgen v0.2.84 -│ ├── cfg-if v1.0.0 -│ └── wasm-bindgen-macro v0.2.84 (proc-macro) -│ ├── quote v1.0.26 -│ │ └── proc-macro2 v1.0.52 -│ │ └── unicode-ident v1.0.8 -│ └── wasm-bindgen-macro-support v0.2.84 -│ ├── proc-macro2 v1.0.52 (*) -│ ├── quote v1.0.26 (*) -│ ├── syn v1.0.109 -│ │ ├── proc-macro2 v1.0.52 (*) -│ │ ├── quote v1.0.26 (*) -│ │ └── unicode-ident v1.0.8 -│ ├── wasm-bindgen-backend v0.2.84 -│ │ ├── bumpalo v3.12.0 -│ │ ├── log v0.4.17 -│ │ │ └── cfg-if v1.0.0 -│ │ ├── once_cell v1.17.1 -│ │ ├── proc-macro2 v1.0.52 (*) -│ │ ├── quote v1.0.26 (*) -│ │ ├── syn v1.0.109 (*) -│ │ └── wasm-bindgen-shared v0.2.84 -│ └── wasm-bindgen-shared v0.2.84 -├── console_log v0.2.2 -│ ├── log v0.4.17 -│ │ └── cfg-if v1.0.0 -│ └── web-sys v0.3.61 -│ ├── js-sys v0.3.61 -│ │ └── wasm-bindgen v0.2.84 (*) -│ └── wasm-bindgen v0.2.84 (*) -├── http v0.2.9 -│ ├── bytes v1.4.0 -│ ├── fnv v1.0.7 -│ └── itoa v1.0.6 -├── leptos v0.2.3 -│ ├── cfg-if v1.0.0 -│ ├── leptos_config v0.2.3 -│ │ ├── config v0.13.3 -│ │ │ ├── async-trait v0.1.66 (proc-macro) -│ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ └── syn v1.0.109 (*) -│ │ │ ├── json5 v0.4.1 -│ │ │ │ ├── pest v2.5.6 -│ │ │ │ │ ├── thiserror v1.0.39 -│ │ │ │ │ │ └── thiserror-impl v1.0.39 (proc-macro) -│ │ │ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ │ │ └── syn v1.0.109 (*) -│ │ │ │ │ └── ucd-trie v0.1.5 -│ │ │ │ ├── pest_derive v2.5.6 (proc-macro) -│ │ │ │ │ ├── pest v2.5.6 (*) -│ │ │ │ │ └── pest_generator v2.5.6 -│ │ │ │ │ ├── pest v2.5.6 (*) -│ │ │ │ │ ├── pest_meta v2.5.6 -│ │ │ │ │ │ ├── once_cell v1.17.1 -│ │ │ │ │ │ └── pest v2.5.6 (*) -│ │ │ │ │ │ [build-dependencies] -│ │ │ │ │ │ └── sha2 v0.10.6 -│ │ │ │ │ │ ├── cfg-if v1.0.0 -│ │ │ │ │ │ ├── cpufeatures v0.2.5 -│ │ │ │ │ │ └── digest v0.10.6 -│ │ │ │ │ │ ├── block-buffer v0.10.4 -│ │ │ │ │ │ │ └── generic-array v0.14.6 -│ │ │ │ │ │ │ └── typenum v1.16.0 -│ │ │ │ │ │ │ [build-dependencies] -│ │ │ │ │ │ │ └── version_check v0.9.4 -│ │ │ │ │ │ └── crypto-common v0.1.6 -│ │ │ │ │ │ ├── generic-array v0.14.6 (*) -│ │ │ │ │ │ └── typenum v1.16.0 -│ │ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ │ └── syn v1.0.109 (*) -│ │ │ │ └── serde v1.0.156 -│ │ │ │ └── serde_derive v1.0.156 (proc-macro) -│ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ └── syn v1.0.109 (*) -│ │ │ ├── lazy_static v1.4.0 -│ │ │ ├── nom v7.1.3 -│ │ │ │ ├── memchr v2.5.0 -│ │ │ │ └── minimal-lexical v0.2.1 -│ │ │ ├── pathdiff v0.2.1 -│ │ │ ├── ron v0.7.1 -│ │ │ │ ├── base64 v0.13.1 -│ │ │ │ ├── bitflags v1.3.2 -│ │ │ │ └── serde v1.0.156 (*) -│ │ │ ├── rust-ini v0.18.0 -│ │ │ │ ├── cfg-if v1.0.0 -│ │ │ │ └── ordered-multimap v0.4.3 -│ │ │ │ ├── dlv-list v0.3.0 -│ │ │ │ └── hashbrown v0.12.3 -│ │ │ │ └── ahash v0.7.6 -│ │ │ │ ├── getrandom v0.2.8 -│ │ │ │ │ ├── cfg-if v1.0.0 -│ │ │ │ │ └── libc v0.2.140 -│ │ │ │ └── once_cell v1.17.1 -│ │ │ │ [build-dependencies] -│ │ │ │ └── version_check v0.9.4 -│ │ │ ├── serde v1.0.156 (*) -│ │ │ ├── serde_json v1.0.94 -│ │ │ │ ├── itoa v1.0.6 -│ │ │ │ ├── ryu v1.0.13 -│ │ │ │ └── serde v1.0.156 (*) -│ │ │ ├── toml v0.5.11 -│ │ │ │ └── serde v1.0.156 (*) -│ │ │ └── yaml-rust v0.4.5 -│ │ │ └── linked-hash-map v0.5.6 -│ │ ├── fs v0.0.5 -│ │ │ ├── bytes v0.4.12 -│ │ │ │ ├── byteorder v1.4.3 -│ │ │ │ └── iovec v0.1.4 -│ │ │ │ └── libc v0.2.140 -│ │ │ ├── futures v0.1.31 -│ │ │ └── futures-cpupool v0.1.8 -│ │ │ ├── futures v0.1.31 -│ │ │ └── num_cpus v1.15.0 -│ │ │ └── libc v0.2.140 -│ │ ├── regex v1.7.1 -│ │ │ ├── aho-corasick v0.7.20 -│ │ │ │ └── memchr v2.5.0 -│ │ │ ├── memchr v2.5.0 -│ │ │ └── regex-syntax v0.6.28 -│ │ ├── serde v1.0.156 (*) -│ │ ├── thiserror v1.0.39 (*) -│ │ └── typed-builder v0.14.0 (proc-macro) -│ │ ├── proc-macro2 v1.0.52 (*) -│ │ ├── quote v1.0.26 (*) -│ │ └── syn v1.0.109 (*) -│ ├── leptos_dom v0.2.3 -│ │ ├── async-recursion v1.0.2 (proc-macro) -│ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ ├── quote v1.0.26 (*) -│ │ │ └── syn v1.0.109 (*) -│ │ ├── cfg-if v1.0.0 -│ │ ├── drain_filter_polyfill v0.1.3 -│ │ ├── educe v0.4.20 (proc-macro) -│ │ │ ├── enum-ordinalize v3.1.12 (proc-macro) -│ │ │ │ ├── num-bigint v0.4.3 -│ │ │ │ │ ├── num-integer v0.1.45 -│ │ │ │ │ │ └── num-traits v0.2.15 -│ │ │ │ │ │ [build-dependencies] -│ │ │ │ │ │ └── autocfg v1.1.0 -│ │ │ │ │ │ [build-dependencies] -│ │ │ │ │ │ └── autocfg v1.1.0 -│ │ │ │ │ └── num-traits v0.2.15 (*) -│ │ │ │ │ [build-dependencies] -│ │ │ │ │ └── autocfg v1.1.0 -│ │ │ │ ├── num-traits v0.2.15 (*) -│ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ └── syn v1.0.109 (*) -│ │ │ │ [build-dependencies] -│ │ │ │ └── rustc_version v0.4.0 -│ │ │ │ └── semver v1.0.17 -│ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ ├── quote v1.0.26 (*) -│ │ │ └── syn v1.0.109 (*) -│ │ ├── futures v0.3.27 -│ │ │ ├── futures-channel v0.3.27 -│ │ │ │ ├── futures-core v0.3.27 -│ │ │ │ └── futures-sink v0.3.27 -│ │ │ ├── futures-core v0.3.27 -│ │ │ ├── futures-executor v0.3.27 -│ │ │ │ ├── futures-core v0.3.27 -│ │ │ │ ├── futures-task v0.3.27 -│ │ │ │ └── futures-util v0.3.27 -│ │ │ │ ├── futures-channel v0.3.27 (*) -│ │ │ │ ├── futures-core v0.3.27 -│ │ │ │ ├── futures-io v0.3.27 -│ │ │ │ ├── futures-macro v0.3.27 (proc-macro) -│ │ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ │ └── syn v1.0.109 (*) -│ │ │ │ ├── futures-sink v0.3.27 -│ │ │ │ ├── futures-task v0.3.27 -│ │ │ │ ├── memchr v2.5.0 -│ │ │ │ ├── pin-project-lite v0.2.9 -│ │ │ │ ├── pin-utils v0.1.0 -│ │ │ │ └── slab v0.4.8 -│ │ │ │ [build-dependencies] -│ │ │ │ └── autocfg v1.1.0 -│ │ │ ├── futures-io v0.3.27 -│ │ │ ├── futures-sink v0.3.27 -│ │ │ ├── futures-task v0.3.27 -│ │ │ └── futures-util v0.3.27 (*) -│ │ ├── html-escape v0.2.13 -│ │ │ └── utf8-width v0.1.6 -│ │ ├── indexmap v1.9.2 -│ │ │ └── hashbrown v0.12.3 (*) -│ │ │ [build-dependencies] -│ │ │ └── autocfg v1.1.0 -│ │ ├── itertools v0.10.5 -│ │ │ └── either v1.8.1 -│ │ ├── js-sys v0.3.61 (*) -│ │ ├── leptos_reactive v0.2.3 -│ │ │ ├── base64 v0.21.0 -│ │ │ ├── cfg-if v1.0.0 -│ │ │ ├── futures v0.3.27 (*) -│ │ │ ├── js-sys v0.3.61 (*) -│ │ │ ├── serde v1.0.156 (*) -│ │ │ ├── serde-wasm-bindgen v0.5.0 -│ │ │ │ ├── js-sys v0.3.61 (*) -│ │ │ │ ├── serde v1.0.156 (*) -│ │ │ │ └── wasm-bindgen v0.2.84 (*) -│ │ │ ├── serde_json v1.0.94 (*) -│ │ │ ├── slotmap v1.0.6 -│ │ │ │ └── serde v1.0.156 (*) -│ │ │ │ [build-dependencies] -│ │ │ │ └── version_check v0.9.4 -│ │ │ ├── thiserror v1.0.39 (*) -│ │ │ ├── tracing v0.1.37 -│ │ │ │ ├── cfg-if v1.0.0 -│ │ │ │ ├── pin-project-lite v0.2.9 -│ │ │ │ ├── tracing-attributes v0.1.23 (proc-macro) -│ │ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ │ └── syn v1.0.109 (*) -│ │ │ │ └── tracing-core v0.1.30 -│ │ │ │ └── once_cell v1.17.1 -│ │ │ ├── wasm-bindgen v0.2.84 (*) -│ │ │ ├── wasm-bindgen-futures v0.4.34 -│ │ │ │ ├── cfg-if v1.0.0 -│ │ │ │ ├── js-sys v0.3.61 (*) -│ │ │ │ └── wasm-bindgen v0.2.84 (*) -│ │ │ └── web-sys v0.3.61 (*) -│ │ ├── once_cell v1.17.1 -│ │ ├── pad-adapter v0.1.1 -│ │ ├── paste v1.0.12 (proc-macro) -│ │ ├── rustc-hash v1.1.0 -│ │ ├── serde_json v1.0.94 (*) -│ │ ├── smallvec v1.10.0 -│ │ ├── tracing v0.1.37 (*) -│ │ ├── wasm-bindgen v0.2.84 (*) -│ │ ├── wasm-bindgen-futures v0.4.34 (*) -│ │ └── web-sys v0.3.61 (*) -│ ├── leptos_macro v0.2.3 (proc-macro) -│ │ ├── attribute-derive v0.5.0 -│ │ │ ├── attribute-derive-macro v0.5.0 (proc-macro) -│ │ │ │ ├── collection_literals v1.0.1 -│ │ │ │ ├── interpolator v0.4.0 -│ │ │ │ ├── proc-macro-error v1.0.4 -│ │ │ │ │ ├── proc-macro-error-attr v1.0.4 (proc-macro) -│ │ │ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ │ │ └── quote v1.0.26 (*) -│ │ │ │ │ │ [build-dependencies] -│ │ │ │ │ │ └── version_check v0.9.4 -│ │ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ │ └── syn v1.0.109 (*) -│ │ │ │ │ [build-dependencies] -│ │ │ │ │ └── version_check v0.9.4 -│ │ │ │ ├── proc-macro-utils v0.5.1 -│ │ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ │ └── smallvec v1.10.0 -│ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ ├── quote-use v0.6.0 (proc-macro) -│ │ │ │ │ ├── derive-where v1.1.0 (proc-macro) -│ │ │ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ │ │ └── syn v1.0.109 (*) -│ │ │ │ │ ├── proc-macro-error v1.0.4 (*) -│ │ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ │ └── syn v1.0.109 (*) -│ │ │ │ └── syn v1.0.109 (*) -│ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ ├── quote v1.0.26 (*) -│ │ │ └── syn v1.0.109 (*) -│ │ ├── cfg-if v1.0.0 -│ │ ├── convert_case v0.6.0 -│ │ │ └── unicode-segmentation v1.10.1 -│ │ ├── html-escape v0.2.13 (*) -│ │ ├── itertools v0.10.5 (*) -│ │ ├── leptos_dom v0.2.3 (*) -│ │ ├── leptos_hot_reload v0.2.3 -│ │ │ ├── anyhow v1.0.69 -│ │ │ ├── camino v1.1.4 -│ │ │ ├── indexmap v1.9.2 -│ │ │ │ └── hashbrown v0.12.3 -│ │ │ │ [build-dependencies] -│ │ │ │ └── autocfg v1.1.0 -│ │ │ ├── parking_lot v0.12.1 -│ │ │ │ ├── lock_api v0.4.9 -│ │ │ │ │ └── scopeguard v1.1.0 -│ │ │ │ │ [build-dependencies] -│ │ │ │ │ └── autocfg v1.1.0 -│ │ │ │ └── parking_lot_core v0.9.7 -│ │ │ │ ├── cfg-if v1.0.0 -│ │ │ │ ├── libc v0.2.140 -│ │ │ │ └── smallvec v1.10.0 -│ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ ├── quote v1.0.26 (*) -│ │ │ ├── serde v1.0.156 (*) -│ │ │ ├── syn v1.0.109 (*) -│ │ │ ├── syn-rsx v0.9.0 -│ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ ├── syn v1.0.109 (*) -│ │ │ │ └── thiserror v1.0.39 (*) -│ │ │ └── walkdir v2.3.3 -│ │ │ └── same-file v1.0.6 -│ │ ├── leptos_reactive v0.2.3 -│ │ │ ├── base64 v0.21.0 -│ │ │ ├── cfg-if v1.0.0 -│ │ │ ├── futures v0.3.27 (*) -│ │ │ ├── js-sys v0.3.61 (*) -│ │ │ ├── serde v1.0.156 (*) -│ │ │ ├── serde-wasm-bindgen v0.5.0 (*) -│ │ │ ├── serde_json v1.0.94 (*) -│ │ │ ├── slotmap v1.0.6 (*) -│ │ │ ├── thiserror v1.0.39 (*) -│ │ │ ├── tracing v0.1.37 (*) -│ │ │ ├── wasm-bindgen v0.2.84 (*) -│ │ │ ├── wasm-bindgen-futures v0.4.34 (*) -│ │ │ └── web-sys v0.3.61 -│ │ │ ├── js-sys v0.3.61 (*) -│ │ │ └── wasm-bindgen v0.2.84 (*) -│ │ ├── prettyplease v0.1.25 -│ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ └── syn v1.0.109 (*) -│ │ ├── proc-macro-error v1.0.4 (*) -│ │ ├── proc-macro2 v1.0.52 (*) -│ │ ├── quote v1.0.26 (*) -│ │ ├── server_fn_macro v0.2.3 -│ │ │ ├── const_format v0.2.30 -│ │ │ │ └── const_format_proc_macros v0.2.29 (proc-macro) -│ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ └── unicode-xid v0.2.4 -│ │ │ ├── proc-macro-error v1.0.4 (*) -│ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ ├── quote v1.0.26 (*) -│ │ │ ├── serde v1.0.156 (*) -│ │ │ ├── serde_json v1.0.94 (*) -│ │ │ ├── serde_urlencoded v0.7.1 -│ │ │ │ ├── form_urlencoded v1.1.0 -│ │ │ │ │ └── percent-encoding v2.2.0 -│ │ │ │ ├── itoa v1.0.6 -│ │ │ │ ├── ryu v1.0.13 -│ │ │ │ └── serde v1.0.156 (*) -│ │ │ ├── syn v1.0.109 (*) -│ │ │ └── xxhash-rust v0.8.6 -│ │ ├── syn v1.0.109 (*) -│ │ ├── syn-rsx v0.9.0 (*) -│ │ └── uuid v1.3.0 -│ │ └── getrandom v0.2.8 (*) -│ ├── leptos_reactive v0.2.3 (*) -│ ├── leptos_server v0.2.3 -│ │ ├── lazy_static v1.4.0 -│ │ ├── leptos_dom v0.2.3 (*) -│ │ ├── leptos_reactive v0.2.3 (*) -│ │ ├── serde v1.0.156 (*) -│ │ ├── serde_urlencoded v0.7.1 (*) -│ │ ├── server_fn v0.2.3 -│ │ │ ├── cfg-if v1.0.0 -│ │ │ ├── ciborium v0.2.0 -│ │ │ │ ├── ciborium-io v0.2.0 -│ │ │ │ ├── ciborium-ll v0.2.0 -│ │ │ │ │ ├── ciborium-io v0.2.0 -│ │ │ │ │ └── half v1.8.2 -│ │ │ │ └── serde v1.0.156 (*) -│ │ │ ├── const_format v0.2.30 (*) -│ │ │ ├── form_urlencoded v1.1.0 (*) -│ │ │ ├── once_cell v1.17.1 -│ │ │ ├── proc-macro2 v1.0.52 -│ │ │ │ └── unicode-ident v1.0.8 -│ │ │ ├── quote v1.0.26 (*) -│ │ │ ├── reqwest v0.11.14 -│ │ │ │ ├── base64 v0.21.0 -│ │ │ │ ├── bytes v1.4.0 -│ │ │ │ ├── encoding_rs v0.8.32 -│ │ │ │ │ └── cfg-if v1.0.0 -│ │ │ │ ├── futures-core v0.3.27 -│ │ │ │ ├── futures-util v0.3.27 (*) -│ │ │ │ ├── h2 v0.3.16 -│ │ │ │ │ ├── bytes v1.4.0 -│ │ │ │ │ ├── fnv v1.0.7 -│ │ │ │ │ ├── futures-core v0.3.27 -│ │ │ │ │ ├── futures-sink v0.3.27 -│ │ │ │ │ ├── futures-util v0.3.27 (*) -│ │ │ │ │ ├── http v0.2.9 (*) -│ │ │ │ │ ├── indexmap v1.9.2 (*) -│ │ │ │ │ ├── slab v0.4.8 (*) -│ │ │ │ │ ├── tokio v1.26.0 -│ │ │ │ │ │ ├── bytes v1.4.0 -│ │ │ │ │ │ ├── libc v0.2.140 -│ │ │ │ │ │ ├── memchr v2.5.0 -│ │ │ │ │ │ ├── mio v0.8.6 -│ │ │ │ │ │ │ ├── libc v0.2.140 -│ │ │ │ │ │ │ └── log v0.4.17 (*) -│ │ │ │ │ │ ├── pin-project-lite v0.2.9 -│ │ │ │ │ │ └── socket2 v0.4.9 -│ │ │ │ │ │ └── libc v0.2.140 -│ │ │ │ │ │ [build-dependencies] -│ │ │ │ │ │ └── autocfg v1.1.0 -│ │ │ │ │ ├── tokio-util v0.7.7 -│ │ │ │ │ │ ├── bytes v1.4.0 -│ │ │ │ │ │ ├── futures-core v0.3.27 -│ │ │ │ │ │ ├── futures-sink v0.3.27 -│ │ │ │ │ │ ├── pin-project-lite v0.2.9 -│ │ │ │ │ │ ├── tokio v1.26.0 (*) -│ │ │ │ │ │ └── tracing v0.1.37 (*) -│ │ │ │ │ └── tracing v0.1.37 (*) -│ │ │ │ ├── http v0.2.9 (*) -│ │ │ │ ├── http-body v0.4.5 -│ │ │ │ │ ├── bytes v1.4.0 -│ │ │ │ │ ├── http v0.2.9 (*) -│ │ │ │ │ └── pin-project-lite v0.2.9 -│ │ │ │ ├── hyper v0.14.25 -│ │ │ │ │ ├── bytes v1.4.0 -│ │ │ │ │ ├── futures-channel v0.3.27 (*) -│ │ │ │ │ ├── futures-core v0.3.27 -│ │ │ │ │ ├── futures-util v0.3.27 (*) -│ │ │ │ │ ├── h2 v0.3.16 (*) -│ │ │ │ │ ├── http v0.2.9 (*) -│ │ │ │ │ ├── http-body v0.4.5 (*) -│ │ │ │ │ ├── httparse v1.8.0 -│ │ │ │ │ ├── httpdate v1.0.2 -│ │ │ │ │ ├── itoa v1.0.6 -│ │ │ │ │ ├── pin-project-lite v0.2.9 -│ │ │ │ │ ├── socket2 v0.4.9 (*) -│ │ │ │ │ ├── tokio v1.26.0 (*) -│ │ │ │ │ ├── tower-service v0.3.2 -│ │ │ │ │ ├── tracing v0.1.37 (*) -│ │ │ │ │ └── want v0.3.0 -│ │ │ │ │ ├── log v0.4.17 (*) -│ │ │ │ │ └── try-lock v0.2.4 -│ │ │ │ ├── hyper-tls v0.5.0 -│ │ │ │ │ ├── bytes v1.4.0 -│ │ │ │ │ ├── hyper v0.14.25 (*) -│ │ │ │ │ ├── native-tls v0.2.11 -│ │ │ │ │ │ ├── log v0.4.17 (*) -│ │ │ │ │ │ ├── openssl v0.10.46 -│ │ │ │ │ │ │ ├── bitflags v1.3.2 -│ │ │ │ │ │ │ ├── cfg-if v1.0.0 -│ │ │ │ │ │ │ ├── foreign-types v0.3.2 -│ │ │ │ │ │ │ │ └── foreign-types-shared v0.1.1 -│ │ │ │ │ │ │ ├── libc v0.2.140 -│ │ │ │ │ │ │ ├── once_cell v1.17.1 -│ │ │ │ │ │ │ ├── openssl-macros v0.1.0 (proc-macro) -│ │ │ │ │ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ │ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ │ │ │ │ └── syn v1.0.109 (*) -│ │ │ │ │ │ │ └── openssl-sys v0.9.81 -│ │ │ │ │ │ │ └── libc v0.2.140 -│ │ │ │ │ │ │ [build-dependencies] -│ │ │ │ │ │ │ ├── autocfg v1.1.0 -│ │ │ │ │ │ │ ├── cc v1.0.79 -│ │ │ │ │ │ │ └── pkg-config v0.3.26 -│ │ │ │ │ │ ├── openssl-probe v0.1.5 -│ │ │ │ │ │ └── openssl-sys v0.9.81 (*) -│ │ │ │ │ ├── tokio v1.26.0 (*) -│ │ │ │ │ └── tokio-native-tls v0.3.1 -│ │ │ │ │ ├── native-tls v0.2.11 (*) -│ │ │ │ │ └── tokio v1.26.0 (*) -│ │ │ │ ├── ipnet v2.7.1 -│ │ │ │ ├── log v0.4.17 (*) -│ │ │ │ ├── mime v0.3.16 -│ │ │ │ ├── native-tls v0.2.11 (*) -│ │ │ │ ├── once_cell v1.17.1 -│ │ │ │ ├── percent-encoding v2.2.0 -│ │ │ │ ├── pin-project-lite v0.2.9 -│ │ │ │ ├── serde v1.0.156 (*) -│ │ │ │ ├── serde_urlencoded v0.7.1 (*) -│ │ │ │ ├── tokio v1.26.0 (*) -│ │ │ │ ├── tokio-native-tls v0.3.1 (*) -│ │ │ │ ├── tower-service v0.3.2 -│ │ │ │ └── url v2.3.1 -│ │ │ │ ├── form_urlencoded v1.1.0 (*) -│ │ │ │ ├── idna v0.3.0 -│ │ │ │ │ ├── unicode-bidi v0.3.11 -│ │ │ │ │ └── unicode-normalization v0.1.22 -│ │ │ │ │ └── tinyvec v1.6.0 -│ │ │ │ │ └── tinyvec_macros v0.1.1 -│ │ │ │ └── percent-encoding v2.2.0 -│ │ │ ├── serde v1.0.156 (*) -│ │ │ ├── serde_json v1.0.94 (*) -│ │ │ ├── serde_urlencoded v0.7.1 (*) -│ │ │ ├── server_fn_macro_default v0.2.3 (proc-macro) -│ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ ├── server_fn_macro v0.2.3 (*) -│ │ │ │ └── syn v1.0.109 (*) -│ │ │ ├── syn v1.0.109 -│ │ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ │ ├── quote v1.0.26 (*) -│ │ │ │ └── unicode-ident v1.0.8 -│ │ │ ├── thiserror v1.0.39 (*) -│ │ │ └── xxhash-rust v0.8.6 -│ │ └── thiserror v1.0.39 (*) -│ ├── server_fn v0.2.3 (*) -│ ├── tracing v0.1.37 (*) -│ └── typed-builder v0.14.0 (proc-macro) (*) -├── leptos_meta v0.2.3 -│ ├── cfg-if v1.0.0 -│ ├── leptos v0.2.3 (*) -│ ├── tracing v0.1.37 (*) -│ ├── typed-builder v0.14.0 (proc-macro) (*) -│ ├── wasm-bindgen v0.2.84 (*) -│ └── web-sys v0.3.61 (*) -├── leptos_router v0.2.3 -│ ├── cfg-if v1.0.0 -│ ├── common_macros v0.1.1 -│ ├── gloo-net v0.2.6 -│ │ ├── futures-channel v0.3.27 (*) -│ │ ├── futures-core v0.3.27 -│ │ ├── futures-sink v0.3.27 -│ │ ├── gloo-utils v0.1.6 -│ │ │ ├── js-sys v0.3.61 (*) -│ │ │ ├── serde v1.0.156 (*) -│ │ │ ├── serde_json v1.0.94 (*) -│ │ │ ├── wasm-bindgen v0.2.84 (*) -│ │ │ └── web-sys v0.3.61 (*) -│ │ ├── js-sys v0.3.61 (*) -│ │ ├── pin-project v1.0.12 -│ │ │ └── pin-project-internal v1.0.12 (proc-macro) -│ │ │ ├── proc-macro2 v1.0.52 (*) -│ │ │ ├── quote v1.0.26 (*) -│ │ │ └── syn v1.0.109 (*) -│ │ ├── serde v1.0.156 (*) -│ │ ├── serde_json v1.0.94 (*) -│ │ ├── thiserror v1.0.39 (*) -│ │ ├── wasm-bindgen v0.2.84 (*) -│ │ ├── wasm-bindgen-futures v0.4.34 (*) -│ │ └── web-sys v0.3.61 (*) -│ ├── js-sys v0.3.61 (*) -│ ├── lazy_static v1.4.0 -│ ├── leptos v0.2.3 (*) -│ ├── linear-map v1.2.0 -│ ├── log v0.4.17 (*) -│ ├── percent-encoding v2.2.0 -│ ├── serde v1.0.156 (*) -│ ├── serde_urlencoded v0.7.1 (*) -│ ├── thiserror v1.0.39 (*) -│ ├── tracing v0.1.37 (*) -│ ├── wasm-bindgen v0.2.84 (*) -│ ├── wasm-bindgen-futures v0.4.34 (*) -│ └── web-sys v0.3.61 (*) -├── log v0.4.17 (*) -├── simple_logger v4.0.0 -│ ├── colored v2.0.0 -│ │ ├── atty v0.2.14 -│ │ │ └── libc v0.2.140 -│ │ └── lazy_static v1.4.0 -│ ├── log v0.4.17 (*) -│ └── time v0.3.20 -│ ├── itoa v1.0.6 -│ ├── libc v0.2.140 -│ ├── num_threads v0.1.6 -│ ├── time-core v0.1.0 -│ └── time-macros v0.2.8 (proc-macro) -│ └── time-core v0.1.0 -├── thiserror v1.0.39 (*) -└── wasm-bindgen v0.2.84 (*)