From 9be6f435d9ea39f31a8906e55dbd3e8b1e5ec598 Mon Sep 17 00:00:00 2001 From: kjuulh Date: Sun, 19 Feb 2023 21:37:54 +0100 Subject: [PATCH] feat(sdk,core): Use async runtime instead of blocking. Default to using async runtime instead of blocking. I.e. ```rust fn main() -> eyre::Result<()> { // ... client.container().from("rust").publish("somewhere")?; // ... } // to async fn main() -> eyre::Result<()> { // ... client.container().from("rust").publish("somewhere").await?; // ... } ``` --- crates/dagger-codegen/src/rust/functions.rs | 52 +- .../src/rust/templates/object_tmpl.rs | 16 +- .../examples/build-the-application/main.rs | 7 +- crates/dagger-sdk/examples/caching/main.rs | 11 +- .../examples/existing-dockerfile/main.rs | 7 +- .../examples/first-pipeline/main.rs | 7 +- .../examples/multi-stage-build/main.rs | 10 +- .../examples/publish-the-application/main.rs | 9 +- .../examples/test-the-application/main.rs | 7 +- crates/dagger-sdk/src/gen.rs | 1033 +++++++++++------ crates/dagger-sdk/src/querybuilder.rs | 9 +- crates/dagger-sdk/tests/mod.rs | 5 +- 12 files changed, 778 insertions(+), 395 deletions(-) diff --git a/crates/dagger-codegen/src/rust/functions.rs b/crates/dagger-codegen/src/rust/functions.rs index e5c9f86..492210e 100644 --- a/crates/dagger-codegen/src/rust/functions.rs +++ b/crates/dagger-codegen/src/rust/functions.rs @@ -10,6 +10,8 @@ use crate::functions::{ }; use crate::utility::OptionExt; +use super::templates::object_tmpl::render_optional_field_args; + pub fn format_name(s: &str) -> String { s.to_case(Case::Pascal) } @@ -29,10 +31,33 @@ pub fn field_options_struct_name(field: &FullTypeFields) -> Option { } pub fn format_function(funcs: &CommonFunctions, field: &FullTypeFields) -> Option { + let is_async = field.type_.pipe(|t| &t.type_ref).pipe(|t| { + if type_ref_is_object(&t) || type_ref_is_list_of_objects(&t) { + return None; + } else { + return Some(quote! { + async + }); + }; + }); + let signature = quote! { - pub fn $(field.name.pipe(|n | format_struct_name(n))) + pub $(is_async) fn $(field.name.pipe(|n | format_struct_name(n))) }; - let args = format_function_args(funcs, field); + + let lifecycle = format_optional_args(funcs, field) + .pipe(|(_, contains_lifecycle)| contains_lifecycle) + .and_then(|c| { + if *c { + Some(quote! { + <'a> + }) + } else { + None + } + }); + + let args = format_function_args(funcs, field, lifecycle.as_ref()); let output_type = field .type_ @@ -52,7 +77,7 @@ pub fn format_function(funcs: &CommonFunctions, field: &FullTypeFields) -> Optio $(render_execution(funcs, field)) } - $(&signature)_opts( + $(&signature)_opts$(lifecycle)( $args ) -> $(output_type) { let mut query = self.selection.select($(quoted(field.name.as_ref()))); @@ -235,13 +260,14 @@ fn render_execution(funcs: &CommonFunctions, field: &FullTypeFields) -> rust::To let graphql_client = rust::import("crate::client", "graphql_client"); quote! { - query.execute(&$graphql_client(&self.conn)) + query.execute(&$graphql_client(&self.conn)).await } } fn format_function_args( funcs: &CommonFunctions, field: &FullTypeFields, + lifecycle: Option<&rust::Tokens>, ) -> Option<(rust::Tokens, bool)> { if let Some(args) = field.args.as_ref() { let args = args @@ -271,7 +297,7 @@ fn format_function_args( Some(( quote! { $(required_args) - opts: $(field_options_struct_name(field)) + opts: $(field_options_struct_name(field))$(lifecycle) }, true, )) @@ -316,3 +342,19 @@ fn format_required_function_args( None } } + +pub fn format_optional_args( + funcs: &CommonFunctions, + field: &FullTypeFields, +) -> Option<(rust::Tokens, bool)> { + field + .args + .pipe(|t| t.into_iter().flatten().collect::>()) + .map(|t| { + t.into_iter() + .filter(|t| type_ref_is_optional(&t.input_value.type_)) + .collect::>() + }) + .pipe(|t| render_optional_field_args(funcs, t)) + .flatten() +} diff --git a/crates/dagger-codegen/src/rust/templates/object_tmpl.rs b/crates/dagger-codegen/src/rust/templates/object_tmpl.rs index b0f8ea9..dc09a3a 100644 --- a/crates/dagger-codegen/src/rust/templates/object_tmpl.rs +++ b/crates/dagger-codegen/src/rust/templates/object_tmpl.rs @@ -5,7 +5,8 @@ use itertools::Itertools; use crate::functions::{type_ref_is_optional, CommonFunctions}; use crate::rust::functions::{ - field_options_struct_name, format_function, format_name, format_struct_name, + field_options_struct_name, format_function, format_name, format_optional_args, + format_struct_name, }; use crate::utility::OptionExt; @@ -51,16 +52,7 @@ fn render_optional_args( fn render_optional_arg(funcs: &CommonFunctions, field: &FullTypeFields) -> Option { let output_type = field_options_struct_name(field); - let fields = field - .args - .pipe(|t| t.into_iter().flatten().collect::>()) - .map(|t| { - t.into_iter() - .filter(|t| type_ref_is_optional(&t.input_value.type_)) - .collect::>() - }) - .pipe(|t| render_optional_field_args(funcs, t)) - .flatten(); + let fields = format_optional_args(funcs, field); let builder = rust::import("derive_builder", "Builder"); let _phantom_data = rust::import("std::marker", "PhantomData"); @@ -79,7 +71,7 @@ fn render_optional_arg(funcs: &CommonFunctions, field: &FullTypeFields) -> Optio } } -fn render_optional_field_args( +pub fn render_optional_field_args( funcs: &CommonFunctions, args: &Vec<&FullTypeFieldsArgs>, ) -> Option<(rust::Tokens, bool)> { diff --git a/crates/dagger-sdk/examples/build-the-application/main.rs b/crates/dagger-sdk/examples/build-the-application/main.rs index 6e47270..8f9d2b4 100644 --- a/crates/dagger-sdk/examples/build-the-application/main.rs +++ b/crates/dagger-sdk/examples/build-the-application/main.rs @@ -1,6 +1,7 @@ use dagger_sdk::HostDirectoryOpts; -fn main() -> eyre::Result<()> { +#[tokio::main] +async fn main() -> eyre::Result<()> { let client = dagger_sdk::connect()?; let host_source_dir = client.host().directory_opts( @@ -14,7 +15,7 @@ fn main() -> eyre::Result<()> { let source = client .container() .from("node:16") - .with_mounted_directory("/src", host_source_dir.id()?); + .with_mounted_directory("/src", host_source_dir.id().await?); let runner = source .with_workdir("/src") @@ -28,7 +29,7 @@ fn main() -> eyre::Result<()> { let _ = build_dir.export("./build"); - let entries = build_dir.entries(); + let entries = build_dir.entries().await; println!("build dir contents: \n {:?}", entries); diff --git a/crates/dagger-sdk/examples/caching/main.rs b/crates/dagger-sdk/examples/caching/main.rs index 70a497c..2d8819c 100644 --- a/crates/dagger-sdk/examples/caching/main.rs +++ b/crates/dagger-sdk/examples/caching/main.rs @@ -1,6 +1,7 @@ use rand::Rng; -fn main() -> eyre::Result<()> { +#[tokio::main] +async fn main() -> eyre::Result<()> { let client = dagger_sdk::connect()?; let host_source_dir = client.host().directory_opts( @@ -10,12 +11,12 @@ fn main() -> eyre::Result<()> { .build()?, ); - let node_cache = client.cache_volume("node").id()?; + let node_cache = client.cache_volume("node").id().await?; let source = client .container() .from("node:16") - .with_mounted_directory("/src", host_source_dir.id()?) + .with_mounted_directory("/src", host_source_dir.id().await?) .with_mounted_cache("/src/node_modules", node_cache); let runner = source @@ -33,8 +34,8 @@ fn main() -> eyre::Result<()> { let ref_ = client .container() .from("nginx") - .with_directory("/usr/share/nginx/html", build_dir.id()?) - .publish(format!("ttl.sh/hello-dagger-rs-{}:1h", rng.gen::()))?; + .with_directory("/usr/share/nginx/html", build_dir.id().await?) + .publish(format!("ttl.sh/hello-dagger-rs-{}:1h", rng.gen::())).await?; println!("published image to: {}", ref_); diff --git a/crates/dagger-sdk/examples/existing-dockerfile/main.rs b/crates/dagger-sdk/examples/existing-dockerfile/main.rs index 0f8d056..c1b4e77 100644 --- a/crates/dagger-sdk/examples/existing-dockerfile/main.rs +++ b/crates/dagger-sdk/examples/existing-dockerfile/main.rs @@ -1,6 +1,7 @@ use rand::Rng; -fn main() -> eyre::Result<()> { +#[tokio::main] +async fn main() -> eyre::Result<()> { let mut rng = rand::thread_rng(); let client = dagger_sdk::connect()?; @@ -11,8 +12,8 @@ fn main() -> eyre::Result<()> { let ref_ = client .container() - .build(context_dir.id()?) - .publish(format!("ttl.sh/hello-dagger-rs-{}:1h", rng.gen::()))?; + .build(context_dir.id().await?) + .publish(format!("ttl.sh/hello-dagger-rs-{}:1h", rng.gen::())).await?; println!("published image to: {}", ref_); diff --git a/crates/dagger-sdk/examples/first-pipeline/main.rs b/crates/dagger-sdk/examples/first-pipeline/main.rs index 72cf6d7..2c2c088 100644 --- a/crates/dagger-sdk/examples/first-pipeline/main.rs +++ b/crates/dagger-sdk/examples/first-pipeline/main.rs @@ -1,11 +1,12 @@ -fn main() -> eyre::Result<()> { +#[tokio::main] +async fn main() -> eyre::Result<()> { let client = dagger_sdk::connect()?; let version = client .container() .from("golang:1.19") - .with_exec(vec!["go", "version".into()]) - .stdout()?; + .with_exec(vec!["go", "version"]) + .stdout().await?; println!("Hello from Dagger and {}", version.trim()); diff --git a/crates/dagger-sdk/examples/multi-stage-build/main.rs b/crates/dagger-sdk/examples/multi-stage-build/main.rs index 42ecbb4..5baeac2 100644 --- a/crates/dagger-sdk/examples/multi-stage-build/main.rs +++ b/crates/dagger-sdk/examples/multi-stage-build/main.rs @@ -1,7 +1,8 @@ use dagger_sdk::HostDirectoryOpts; use rand::Rng; -fn main() -> eyre::Result<()> { +#[tokio::main] +async fn main() -> eyre::Result<()> { let client = dagger_sdk::connect()?; let host_source_dir = client.host().directory_opts( @@ -15,7 +16,7 @@ fn main() -> eyre::Result<()> { let source = client .container() .from("node:16") - .with_mounted_directory("/src", host_source_dir.id()?); + .with_mounted_directory("/src", host_source_dir.id().await?); let runner = source .with_workdir("/src") @@ -32,8 +33,9 @@ fn main() -> eyre::Result<()> { let ref_ = client .container() .from("nginx") - .with_directory("/usr/share/nginx/html", build_dir.id()?) - .publish(format!("ttl.sh/hello-dagger-rs-{}:1h", rng.gen::()))?; + .with_directory("/usr/share/nginx/html", build_dir.id().await?) + .publish(format!("ttl.sh/hello-dagger-rs-{}:1h", rng.gen::())) + .await?; println!("published image to: {}", ref_); diff --git a/crates/dagger-sdk/examples/publish-the-application/main.rs b/crates/dagger-sdk/examples/publish-the-application/main.rs index 8b33f6d..cba4b0d 100644 --- a/crates/dagger-sdk/examples/publish-the-application/main.rs +++ b/crates/dagger-sdk/examples/publish-the-application/main.rs @@ -1,7 +1,8 @@ use dagger_sdk::HostDirectoryOpts; use rand::Rng; -fn main() -> eyre::Result<()> { +#[tokio::main] +async fn main() -> eyre::Result<()> { let client = dagger_sdk::connect()?; let output = "examples/publish-the-application/app/build"; @@ -16,7 +17,7 @@ fn main() -> eyre::Result<()> { let source = client .container() .from("node:16") - .with_mounted_directory("/src", host_source_dir.id()?); + .with_mounted_directory("/src", host_source_dir.id().await?); let runner = source .with_workdir("/src") @@ -36,9 +37,9 @@ fn main() -> eyre::Result<()> { .from("nginx") .with_directory( "/usr/share/nginx/html", - client.host().directory(output).id()?, + client.host().directory(output).id().await?, ) - .publish(format!("ttl.sh/hello-dagger-rs-{}:1h", rng.gen::()))?; + .publish(format!("ttl.sh/hello-dagger-rs-{}:1h", rng.gen::())).await?; println!("published image to: {}", ref_); diff --git a/crates/dagger-sdk/examples/test-the-application/main.rs b/crates/dagger-sdk/examples/test-the-application/main.rs index 964b86b..a3b69c1 100644 --- a/crates/dagger-sdk/examples/test-the-application/main.rs +++ b/crates/dagger-sdk/examples/test-the-application/main.rs @@ -1,6 +1,7 @@ use dagger_sdk::HostDirectoryOpts; -fn main() -> eyre::Result<()> { +#[tokio::main] +async fn main() -> eyre::Result<()> { let client = dagger_sdk::connect()?; let host_source_dir = client.host().directory_opts( @@ -14,7 +15,7 @@ fn main() -> eyre::Result<()> { let source = client .container() .from("node:16") - .with_mounted_directory("/src", host_source_dir.id()?); + .with_mounted_directory("/src", host_source_dir.id().await?); let runner = source .with_workdir("/src") @@ -22,7 +23,7 @@ fn main() -> eyre::Result<()> { let out = runner .with_exec(vec!["npm", "test", "--", "--watchAll=false"]) - .stderr()?; + .stderr().await?; println!("{}", out); diff --git a/crates/dagger-sdk/src/gen.rs b/crates/dagger-sdk/src/gen.rs index 79f5a0a..92b0fc8 100644 --- a/crates/dagger-sdk/src/gen.rs +++ b/crates/dagger-sdk/src/gen.rs @@ -32,10 +32,12 @@ pub struct CacheVolume { } impl CacheVolume { - pub fn id(&self) -> eyre::Result { - let query = self.selection.select("id"); + pub async fn id( + &self, + ) -> eyre::Result { + let mut query = self.selection.select("id"); - query.execute(&graphql_client(&self.conn)) + query.execute(&graphql_client(&self.conn)).await } } pub struct Container { @@ -46,6 +48,7 @@ pub struct Container { #[derive(Builder, Debug, PartialEq)] pub struct ContainerBuildOpts<'a> { + #[builder(setter(into, strip_option))] pub dockerfile: Option<&'a str>, #[builder(setter(into, strip_option))] @@ -55,6 +58,7 @@ pub struct ContainerBuildOpts<'a> { } #[derive(Builder, Debug, PartialEq)] pub struct ContainerExecOpts<'a> { + #[builder(setter(into, strip_option))] pub args: Option>, #[builder(setter(into, strip_option))] @@ -68,26 +72,31 @@ pub struct ContainerExecOpts<'a> { } #[derive(Builder, Debug, PartialEq)] pub struct ContainerExportOpts { + #[builder(setter(into, strip_option))] pub platform_variants: Option>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerPipelineOpts<'a> { + #[builder(setter(into, strip_option))] pub description: Option<&'a str>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerPublishOpts { + #[builder(setter(into, strip_option))] pub platform_variants: Option>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithDefaultArgsOpts<'a> { + #[builder(setter(into, strip_option))] pub args: Option>, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithDirectoryOpts<'a> { + #[builder(setter(into, strip_option))] pub exclude: Option>, #[builder(setter(into, strip_option))] @@ -95,6 +104,7 @@ pub struct ContainerWithDirectoryOpts<'a> { } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithExecOpts<'a> { + #[builder(setter(into, strip_option))] pub stdin: Option<&'a str>, #[builder(setter(into, strip_option))] @@ -106,11 +116,13 @@ pub struct ContainerWithExecOpts<'a> { } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithFileOpts { + #[builder(setter(into, strip_option))] pub permissions: Option, } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithMountedCacheOpts { + #[builder(setter(into, strip_option))] pub source: Option, #[builder(setter(into, strip_option))] @@ -118,6 +130,7 @@ pub struct ContainerWithMountedCacheOpts { } #[derive(Builder, Debug, PartialEq)] pub struct ContainerWithNewFileOpts<'a> { + #[builder(setter(into, strip_option))] pub contents: Option<&'a str>, #[builder(setter(into, strip_option))] @@ -125,7 +138,10 @@ pub struct ContainerWithNewFileOpts<'a> { } impl Container { - pub fn build(&self, context: DirectoryId) -> Container { + pub fn build( + &self, + context: DirectoryId, + ) -> Container { let mut query = self.selection.select("build"); query = query.arg("context", context); @@ -134,10 +150,14 @@ impl Container { proc: self.proc.clone(), selection: query, conn: self.conn.clone(), - }; + } } - pub fn build_opts(&self, context: DirectoryId, opts: ContainerBuildOpts) -> Container { + pub fn build_opts<'a>( + &self, + context: DirectoryId, + opts: ContainerBuildOpts<'a> + ) -> Container { let mut query = self.selection.select("build"); query = query.arg("context", context); @@ -155,14 +175,19 @@ impl Container { proc: self.proc.clone(), selection: query, conn: self.conn.clone(), - }; + } } - pub fn default_args(&self) -> eyre::Result> { - let query = self.selection.select("defaultArgs"); + pub async fn default_args( + &self, + ) -> eyre::Result> { + let mut query = self.selection.select("defaultArgs"); - query.execute(&graphql_client(&self.conn)) + query.execute(&graphql_client(&self.conn)).await } - pub fn directory(&self, path: impl Into) -> Directory { + pub fn directory( + &self, + path: impl Into, + ) -> Directory { let mut query = self.selection.select("directory"); query = query.arg("path", path.into()); @@ -171,40 +196,52 @@ impl Container { proc: self.proc.clone(), selection: query, conn: self.conn.clone(), - }; + } } - pub fn entrypoint(&self) -> eyre::Result> { - let query = self.selection.select("entrypoint"); + pub async fn entrypoint( + &self, + ) -> eyre::Result> { + let mut query = self.selection.select("entrypoint"); - query.execute(&graphql_client(&self.conn)) + query.execute(&graphql_client(&self.conn)).await } - pub fn env_variable(&self, name: impl Into) -> eyre::Result { + pub async fn env_variable( + &self, + name: impl Into, + ) -> eyre::Result { let mut query = self.selection.select("envVariable"); query = query.arg("name", name.into()); - query.execute(&graphql_client(&self.conn)) + query.execute(&graphql_client(&self.conn)).await } - pub fn env_variables(&self) -> Vec { - let query = self.selection.select("envVariables"); + pub fn env_variables( + &self, + ) -> Vec { + let mut query = self.selection.select("envVariables"); return vec![EnvVariable { proc: self.proc.clone(), selection: query, conn: self.conn.clone(), - }]; + }] } - pub fn exec(&self) -> Container { - let query = self.selection.select("exec"); + pub fn exec( + &self, + ) -> Container { + let mut query = self.selection.select("exec"); return Container { proc: self.proc.clone(), selection: query, conn: self.conn.clone(), - }; + } } - pub fn exec_opts(&self, opts: ContainerExecOpts) -> Container { + pub fn exec_opts<'a>( + &self, + opts: ContainerExecOpts<'a> + ) -> Container { let mut query = self.selection.select("exec"); if let Some(args) = opts.args { @@ -220,35 +257,37 @@ impl Container { query = query.arg("redirectStderr", redirect_stderr); } if let Some(experimental_privileged_nesting) = opts.experimental_privileged_nesting { - query = query.arg( - "experimentalPrivilegedNesting", - experimental_privileged_nesting, - ); + query = query.arg("experimentalPrivilegedNesting", experimental_privileged_nesting); } return Container { proc: self.proc.clone(), selection: query, conn: self.conn.clone(), - }; + } } - pub fn exit_code(&self) -> eyre::Result { - let query = self.selection.select("exitCode"); + pub async fn exit_code( + &self, + ) -> eyre::Result { + let mut query = self.selection.select("exitCode"); - query.execute(&graphql_client(&self.conn)) + query.execute(&graphql_client(&self.conn)).await } - pub fn export(&self, path: impl Into) -> eyre::Result { + pub async fn export( + &self, + path: impl Into, + ) -> eyre::Result { let mut query = self.selection.select("export"); query = query.arg("path", path.into()); - query.execute(&graphql_client(&self.conn)) + query.execute(&graphql_client(&self.conn)).await } - pub fn export_opts( + pub async fn export_opts( &self, path: impl Into, - opts: ContainerExportOpts, + opts: ContainerExportOpts ) -> eyre::Result { let mut query = self.selection.select("export"); @@ -257,9 +296,12 @@ impl Container { query = query.arg("platformVariants", platform_variants); } - query.execute(&graphql_client(&self.conn)) + query.execute(&graphql_client(&self.conn)).await } - pub fn file(&self, path: impl Into) -> File { + pub fn file( + &self, + path: impl Into, + ) -> File { let mut query = self.selection.select("file"); query = query.arg("path", path.into()); @@ -268,9 +310,12 @@ impl Container { proc: self.proc.clone(), selection: query, conn: self.conn.clone(), - }; + } } - pub fn from(&self, address: impl Into) -> Container { + pub fn from( + &self, + address: impl Into, + ) -> Container { let mut query = self.selection.select("from"); query = query.arg("address", address.into()); @@ -279,44 +324,58 @@ impl Container { proc: self.proc.clone(), selection: query, conn: self.conn.clone(), - }; + } } - pub fn fs(&self) -> Directory { - let query = self.selection.select("fs"); + pub fn fs( + &self, + ) -> Directory { + let mut query = self.selection.select("fs"); return Directory { proc: self.proc.clone(), selection: query, conn: self.conn.clone(), - }; + } } - pub fn id(&self) -> eyre::Result { - let query = self.selection.select("id"); + pub async fn id( + &self, + ) -> eyre::Result { + let mut query = self.selection.select("id"); - query.execute(&graphql_client(&self.conn)) + query.execute(&graphql_client(&self.conn)).await } - pub fn label(&self, name: impl Into) -> eyre::Result { + pub async fn label( + &self, + name: impl Into, + ) -> eyre::Result { let mut query = self.selection.select("label"); query = query.arg("name", name.into()); - query.execute(&graphql_client(&self.conn)) + query.execute(&graphql_client(&self.conn)).await } - pub fn labels(&self) -> Vec