diff --git a/crates/dagger-codegen/src/codegen.rs b/crates/dagger-codegen/src/codegen.rs deleted file mode 100644 index 94d167e..0000000 --- a/crates/dagger-codegen/src/codegen.rs +++ /dev/null @@ -1,155 +0,0 @@ -use std::{ - io::{BufWriter, Write}, - sync::Arc, -}; - -use dagger_core::introspection::{FullType, IntrospectionResponse, Schema}; -use genco::{fmt, prelude::rust, prelude::*, quote}; - -use crate::handlers::{ - enumeration::Enumeration, input::Input, object::Object, scalar::Scalar, DynHandler, Handlers, -}; - -#[allow(dead_code)] -pub struct CodeGeneration { - handlers: Handlers, -} - -impl CodeGeneration { - pub fn new() -> Self { - Self { - handlers: vec![ - Arc::new(Scalar {}), - Arc::new(Enumeration {}), - Arc::new(Input {}), - Arc::new(Object {}), - ], - } - } - - pub fn generate(&self, schema: &IntrospectionResponse) -> eyre::Result { - let code = self.generate_from_schema( - schema - .as_schema() - .schema - .as_ref() - .ok_or(eyre::anyhow!("could not get schema to generate code from"))?, - )?; - Ok(code) - } - - fn generate_from_schema(&self, schema: &Schema) -> eyre::Result { - let mut output = rust::Tokens::new(); - output.push(); - output.append(quote! { - $(format!("// code generated by dagger. DO NOT EDIT.")) - }); - - output.push(); - output.append(render_base_types()); - output.push(); - - let mut types = get_types(schema)?; - //let remaining: Vec> = types.into_iter().map(type_name).collect(); - - types.sort_by_key(|a| a.name.as_ref()); - - for (handler, types) in self.group_by_handlers(&types) { - for t in types { - if let Some(_) = self.type_name(&t) { - let rendered = handler.render(&t)?; - output.push(); - output.append(rendered); - } - } - } - - let mut buffer = BufWriter::new(Vec::new()); - let mut w = fmt::IoWriter::new(buffer.by_ref()); - let fmt = fmt::Config::from_lang::().with_indentation(fmt::Indentation::Space(4)); - let config = rust::Config::default(); - // Prettier imports and use. - //.with_default_import(rust::ImportMode::Qualified); - - output.format_file(&mut w.as_formatter(&fmt), &config)?; - - let out = String::from_utf8(buffer.into_inner()?)?; - Ok(out) - } - - pub fn group_by_handlers(&self, types: &Vec<&FullType>) -> Vec<(DynHandler, Vec)> { - let mut group = vec![]; - - for handler in self.handlers.iter() { - let mut group_types: Vec = vec![]; - for t in types.iter() { - if handler.predicate(*t) { - group_types.push(t.clone().clone()); - } - } - - group.push((handler.clone(), group_types)) - } - - group - } - - pub fn type_name(&self, t: &FullType) -> Option { - let name = t.name.as_ref(); - if let Some(name) = name { - if name.starts_with("_") { - //|| !is_custom_scalar_type(t) { - return None; - } - - return Some(name.replace("Query", "Client")); - } - - None - } - - fn group_key(&self, t: &FullType) -> Option { - for handler in self.handlers.iter() { - if handler.predicate(&t) { - return Some(handler.clone()); - } - } - - None - } - - fn sort_key(&self, t: &FullType) -> (isize, String) { - for (i, handler) in self.handlers.iter().enumerate() { - if handler.predicate(t) { - return (i as isize, t.name.as_ref().unwrap().clone()); - } - } - - return (-1, t.name.as_ref().unwrap().clone()); - } -} - -fn render_base_types() -> rust::Tokens { - let i = rust::import("dagger_core", "Int"); - let b = rust::import("dagger_core", "Boolean"); - - quote! { - $(register(i)) - $(register(b)) - } -} - -fn get_types(schema: &Schema) -> eyre::Result> { - let types = schema - .types - .as_ref() - .ok_or(eyre::anyhow!("types not found on schema"))?; - - let types: Vec<&FullType> = types - .iter() - .map(|t| t.as_ref().map(|t| &t.full_type)) - .flatten() - .collect(); - - Ok(types) -} diff --git a/crates/dagger-codegen/src/functions.rs b/crates/dagger-codegen/src/functions.rs new file mode 100644 index 0000000..0a55482 --- /dev/null +++ b/crates/dagger-codegen/src/functions.rs @@ -0,0 +1,126 @@ +use std::sync::Arc; + +use dagger_core::introspection::{TypeRef, __TypeKind}; + +pub trait FormatTypeFuncs { + fn format_kind_list(&self, representation: &str) -> String; + fn format_kind_scalar_string(&self, representation: &str) -> String; + fn format_kind_scalar_int(&self, representation: &str) -> String; + fn format_kind_scalar_float(&self, representation: &str) -> String; + fn format_kind_scalar_boolean(&self, representation: &str) -> String; + fn format_kind_scalar_default( + &self, + representation: &str, + ref_name: &str, + input: bool, + ) -> String; + fn format_kind_object(&self, representation: &str, ref_name: &str) -> String; + fn format_kind_input_object(&self, representation: &str, ref_name: &str) -> String; + fn format_kind_enum(&self, representation: &str, ref_name: &str) -> String; +} + +pub type DynFormatTypeFuncs = Arc; + +pub struct CommonFunctions { + format_type_funcs: DynFormatTypeFuncs, +} + +impl CommonFunctions { + pub fn new(funcs: DynFormatTypeFuncs) -> Self { + Self { + format_type_funcs: funcs, + } + } + + pub fn format_input_type(&self, t: &TypeRef) -> String { + self.format_type(t, true) + } + + pub fn format_output_type(&self, t: &TypeRef) -> String { + self.format_type(t, false) + } + + fn format_type(&self, t: &TypeRef, input: bool) -> String { + let mut representation = String::new(); + let mut r = Some(t.clone()); + while r.is_some() { + match r.as_ref() { + Some(rf) => match rf.kind.as_ref() { + Some(k) => match k { + __TypeKind::SCALAR => match Scalar::from(rf) { + Scalar::Int => { + self.format_type_funcs + .format_kind_scalar_int(&mut representation); + } + Scalar::Float => { + self.format_type_funcs + .format_kind_scalar_float(&mut representation); + } + Scalar::String => { + self.format_type_funcs + .format_kind_scalar_string(&mut representation); + } + Scalar::Boolean => { + self.format_type_funcs + .format_kind_scalar_boolean(&mut representation); + } + Scalar::Default => { + self.format_type_funcs.format_kind_scalar_default( + &mut representation, + rf.name.as_ref().unwrap(), + input, + ); + } + }, + __TypeKind::OBJECT => { + self.format_type_funcs + .format_kind_object(&mut representation, rf.name.as_ref().unwrap()); + } + __TypeKind::ENUM => { + self.format_type_funcs + .format_kind_enum(&mut representation, rf.name.as_ref().unwrap()); + } + __TypeKind::INPUT_OBJECT => { + self.format_type_funcs.format_kind_input_object( + &mut representation, + &rf.name.as_ref().unwrap(), + ); + } + __TypeKind::LIST => { + self.format_type_funcs.format_kind_list(&mut representation); + } + __TypeKind::Other(_) => { + r = rf.of_type.as_ref().map(|t| t.clone()).map(|t| *t) + } + _ => {} + }, + None => break, + }, + None => break, + } + } + + representation + } +} + +pub enum Scalar { + Int, + Float, + String, + Boolean, + Default, +} + +impl From<&TypeRef> for Scalar { + fn from(value: &TypeRef) -> Self { + match value.name.as_ref().map(|n| n.as_str()) { + Some("Int") => Scalar::Int, + Some("Float") => Scalar::Float, + Some("String") => Scalar::String, + Some("Boolean") => Scalar::Boolean, + Some(_) => Scalar::Default, + None => Scalar::Default, + } + } +} diff --git a/crates/dagger-codegen/src/generator.rs b/crates/dagger-codegen/src/generator.rs new file mode 100644 index 0000000..5ef6e8a --- /dev/null +++ b/crates/dagger-codegen/src/generator.rs @@ -0,0 +1,21 @@ +use std::sync::Arc; + +use dagger_core::introspection::Schema; + +pub trait Generator { + fn generate(&self, schema: Schema) -> eyre::Result; +} + +pub type DynGenerator = Arc; + +pub trait FormatTypeRefs { + fn format_kind_list(representation: &str) -> String; + fn format_kind_scalar_string(representation: &str) -> String; + fn format_kind_scalar_int(representation: &str) -> String; + fn format_kind_scalar_float(representation: &str) -> String; + fn format_kind_scalar_boolean(representation: &str) -> String; + fn format_kind_scalar_default(representation: &str, ref_name: &str, input: bool) -> String; + fn format_kind_object(representation: &str, ref_name: &str) -> String; + fn format_kind_input_object(representation: &str, ref_name: &str) -> String; + fn format_kind_enum(representation: &str, ref_name: &str) -> String; +} diff --git a/crates/dagger-codegen/src/handlers/enumeration.rs b/crates/dagger-codegen/src/handlers/enumeration.rs deleted file mode 100644 index ee2a7b8..0000000 --- a/crates/dagger-codegen/src/handlers/enumeration.rs +++ /dev/null @@ -1,33 +0,0 @@ -use dagger_core::introspection::FullType; -use genco::{prelude::rust, quote}; - -use crate::predicates::is_enum_type; - -use super::{utility::render_description, Handler}; - -pub struct Enumeration; - -impl Handler for Enumeration { - fn predicate(&self, t: &FullType) -> bool { - is_enum_type(t) - } - - fn render(&self, t: &FullType) -> eyre::Result { - let name = t - .name - .as_ref() - .ok_or(eyre::anyhow!("could not get name from type"))?; - - let description = - render_description(t).ok_or(eyre::anyhow!("could not find description"))?; - - let out = quote! { - $description - pub enum $name { - // TODO: Add individual items - } - }; - - Ok(out) - } -} diff --git a/crates/dagger-codegen/src/handlers/fields.rs b/crates/dagger-codegen/src/handlers/fields.rs deleted file mode 100644 index 83f5758..0000000 --- a/crates/dagger-codegen/src/handlers/fields.rs +++ /dev/null @@ -1,123 +0,0 @@ -use convert_case::{Case, Casing}; -use dagger_core::introspection::{FullTypeFields, FullTypeFieldsArgs}; -use genco::{prelude::rust, quote}; - -use super::{ - type_ref::{self, render_type_ref}, - utility::{render_description_from_field, render_description_from_input_value}, -}; - -pub fn render_fields( - fields: &Vec, -) -> eyre::Result> { - let mut collected_fields: Vec = vec![]; - let mut collected_args: Vec = vec![]; - for field in fields.iter() { - let name = field.name.as_ref().map(|n| n.to_case(Case::Snake)).unwrap(); - let output = render_field_output(field)?; - let description = render_description_from_field(field); - let args = match field.args.as_ref() { - Some(a) => render_args(a), - None => None, - }; - - if let Some(args) = args.as_ref() { - let mut args_tkns = rust::Tokens::new(); - args_tkns.append(quote! { - $description - pub struct $(&field.name.as_ref().map(|n| n.to_case(Case::Pascal)).unwrap())Args { - $(&args.args) - } - }); - args_tkns.push(); - - collected_args.push(args_tkns); - } - let mut tkns = rust::Tokens::new(); - tkns.append(quote! { - pub fn $(&name)( - &self, - $(if let Some(_) = args.as_ref() => args: &$(&field.name.as_ref().map(|n| n.to_case(Case::Pascal)).unwrap())Args) - ) -> $(&output) { - let query = self.selection.select($(field.name.as_ref().map(|n| format!("\"{}\"", n)))); - $(if let Some(_) = args.as_ref() => query.args(args);) - - $output { - conn: self.conn.clone(), - proc: self.proc.clone(), - selection: query, - } - } - }); - - collected_fields.push(tkns); - } - - Ok(Some(( - quote! { - $(for arg in collected_args => $arg $['\n'] ) - }, - quote! { - $(for field in collected_fields => $field $['\n'] ) - }, - ))) -} - -struct Arg { - name: String, - description: Option, - type_: rust::Tokens, -} - -struct CollectedArgs { - description: Option, - args: rust::Tokens, -} - -fn render_args(args: &[Option]) -> Option { - let mut collected_args: Vec = vec![]; - - for arg in args { - if let Some(arg) = arg.as_ref().map(|a| &a.input_value) { - let name = arg.name.clone(); - let description = render_description_from_input_value(&arg, &name); - let t = render_type_ref(&arg.type_).unwrap(); - - collected_args.push(Arg { - name, - description, - type_: t, - }) - } - } - - if collected_args.len() > 0 { - let mut collected_arg = CollectedArgs { - description: Some(rust::Tokens::new()), - args: rust::Tokens::new(), - }; - - for arg in collected_args { - collected_arg.args.append(quote! { - $(arg.description) - pub $(arg.name.to_case(Case::Snake)): $(arg.type_), - }); - collected_arg.args.push(); - } - - if let Some(desc) = collected_arg.description.as_ref() { - if desc.is_empty() { - collected_arg.description = None; - } - } - - Some(collected_arg) - } else { - None - } -} - -pub fn render_field_output(field: &FullTypeFields) -> eyre::Result { - let inner = &field.type_.as_ref().unwrap(); - type_ref::render_type_ref(&inner.type_ref) -} diff --git a/crates/dagger-codegen/src/handlers/input.rs b/crates/dagger-codegen/src/handlers/input.rs deleted file mode 100644 index 9d16578..0000000 --- a/crates/dagger-codegen/src/handlers/input.rs +++ /dev/null @@ -1,110 +0,0 @@ -use dagger_core::introspection::FullType; -use genco::prelude::rust; -use genco::prelude::*; - -use crate::predicates::is_input_object_type; - -use super::{input_field::render_input_fields, utility::render_description, Handler}; - -pub struct Input; - -impl Handler for Input { - fn predicate(&self, t: &FullType) -> bool { - is_input_object_type(t) - } - - fn render(&self, t: &FullType) -> eyre::Result { - let name = t - .name - .as_ref() - .ok_or(eyre::anyhow!("could not find name"))?; - let description = render_description(t); - - //let input = rust::import("dagger_core", "Input"); - - let fields = match t.input_fields.as_ref() { - Some(i) => render_input_fields(i)?, - None => None, - }; - - let out = quote! { - $(if description.is_some() => $description) - pub struct $name { - $(if fields.is_some() => $fields) - } - }; - - Ok(out) - } -} - -#[cfg(test)] -mod tests { - use dagger_core::introspection::{ - FullType, FullTypeInputFields, InputValue, TypeRef, __TypeKind, - }; - use pretty_assertions::assert_eq; - - use crate::handlers::Handler; - - use super::Input; - - #[test] - fn can_gen_input() { - let input = Input {}; - let t = FullType { - kind: Some(__TypeKind::INPUT_OBJECT), - name: Some("BuildArg".into()), - description: None, - input_fields: Some(vec![ - FullTypeInputFields { - input_value: InputValue { - name: "name".into(), - description: None, - type_: TypeRef { - name: None, - kind: Some(__TypeKind::NON_NULL), - of_type: Some(Box::new(TypeRef { - kind: Some(__TypeKind::SCALAR), - name: Some("String".into()), - of_type: None, - })), - }, - default_value: None, - }, - }, - FullTypeInputFields { - input_value: InputValue { - name: "value".into(), - description: None, - type_: TypeRef { - name: None, - kind: Some(__TypeKind::NON_NULL), - of_type: Some(Box::new(TypeRef { - kind: Some(__TypeKind::SCALAR), - name: Some("String".into()), - of_type: None, - })), - }, - default_value: None, - }, - }, - ]), - interfaces: None, - enum_values: None, - possible_types: None, - fields: None, - }; - - let expected = r#"pub struct BuildArg { - pub name: String, - - pub value: String, -} -"#; - - let output = input.render(&t).unwrap(); - - assert_eq!(output.to_file_string().unwrap(), expected); - } -} diff --git a/crates/dagger-codegen/src/handlers/input_field.rs b/crates/dagger-codegen/src/handlers/input_field.rs deleted file mode 100644 index 2bd7ccd..0000000 --- a/crates/dagger-codegen/src/handlers/input_field.rs +++ /dev/null @@ -1,22 +0,0 @@ -use dagger_core::introspection::FullTypeInputFields; -use genco::{prelude::rust, quote}; - -use super::type_ref; - -pub fn render_input_fields( - input_fields: &Vec, -) -> eyre::Result> { - let mut fields: Vec<(String, rust::Tokens)> = vec![]; - for field in input_fields.iter() { - fields.push((field.input_value.name.clone(), render_input_field(field)?)); - } - - Ok(Some(quote! { - $(for (name, field) in fields => pub $name: $field, $['\n'] ) - })) -} - -pub fn render_input_field(field: &FullTypeInputFields) -> eyre::Result { - let inner = &field.input_value.type_; - type_ref::render_type_ref(inner) -} diff --git a/crates/dagger-codegen/src/handlers/mod.rs b/crates/dagger-codegen/src/handlers/mod.rs deleted file mode 100644 index a584e87..0000000 --- a/crates/dagger-codegen/src/handlers/mod.rs +++ /dev/null @@ -1,93 +0,0 @@ -pub mod enumeration; -mod fields; -pub mod input; -mod input_field; -pub mod object; -pub mod scalar; -mod type_ref; -mod utility; - -use std::sync::Arc; - -use dagger_core::introspection::FullType; -use genco::prelude::rust::Tokens; -use genco::prelude::*; - -pub trait Handler { - fn predicate(&self, _t: &FullType) -> bool { - false - } - - fn render(&self, t: &FullType) -> eyre::Result { - let tstruct = self.render_struct(t)?; - let timpl = self.render_impl(t)?; - let mut out = rust::Tokens::new(); - out.append(tstruct); - out.push(); - out.append(timpl); - out.push(); - Ok(out) - } - - fn render_struct(&self, t: &FullType) -> eyre::Result { - let name = t.name.as_ref().ok_or(eyre::anyhow!("name not found"))?; - - Ok(quote! { - pub struct $name {} { - // TODO: Add fields - } - }) - } - - fn render_impl(&self, t: &FullType) -> eyre::Result { - let name = t.name.as_ref().ok_or(eyre::anyhow!("name not found"))?; - - Ok(quote! { - impl $name {} { - // TODO: Add fields - } - }) - } -} - -pub type DynHandler = Arc; -pub type Handlers = Vec; - -#[cfg(test)] -mod tests { - use dagger_core::introspection::FullType; - use pretty_assertions::assert_eq; - - use super::Handler; - - struct DefaultHandler; - impl Handler for DefaultHandler {} - - #[test] - fn render_returns_expected() { - let handler = DefaultHandler {}; - let t = FullType { - kind: None, - name: Some("SomeName".into()), - description: None, - fields: None, - input_fields: None, - interfaces: None, - enum_values: None, - possible_types: None, - }; - - let res = handler.render(&t).unwrap(); - - assert_eq!( - res.to_string().unwrap(), - "pub struct SomeName {} { - -} -impl SomeName {} { - -}" - .to_string() - ); - } -} diff --git a/crates/dagger-codegen/src/handlers/object.rs b/crates/dagger-codegen/src/handlers/object.rs deleted file mode 100644 index 7bb3ee4..0000000 --- a/crates/dagger-codegen/src/handlers/object.rs +++ /dev/null @@ -1,234 +0,0 @@ -use dagger_core::introspection::FullType; -use genco::{prelude::rust, quote}; - -use crate::predicates::is_object_type; - -use super::{fields, input_field, utility::render_description, Handler}; - -pub struct Object; - -impl Handler for Object { - fn predicate(&self, t: &FullType) -> bool { - is_object_type(t) - } - - fn render(&self, t: &FullType) -> eyre::Result { - let name = t - .name - .as_ref() - .ok_or(eyre::anyhow!("could not find name"))?; - let description = render_description(t); - - let fields = match t.fields.as_ref() { - Some(i) => fields::render_fields(i)?, - None => None, - }; - - let input_fields = match t.input_fields.as_ref() { - Some(i) => input_field::render_input_fields(i)?, - None => None, - }; - - let child = rust::import("std::process", "Child"); - let connect_params = rust::import("dagger_core::connect_params", "ConnectParams"); - let selection = rust::import("crate::querybuilder", "Selection"); - let arc = rust::import("std::sync", "Arc"); - - let out = quote! { - $(if fields.as_ref().is_some() => $(fields.as_ref().map(|f| &f.0))) - - $(if description.is_some() => $description) - pub struct $name { - $(if input_fields.is_some() => $input_fields) - pub conn: $connect_params, - pub proc: $arc<$child>, - pub selection: $selection, - } - - impl $name { - $(if fields.is_some() => $(fields.map(|f| f.1))) - } - }; - - Ok(out) - } -} - -#[cfg(test)] -mod tests { - use dagger_core::introspection::{ - FullType, FullTypeFields, FullTypeFieldsArgs, FullTypeFieldsType, InputValue, TypeRef, - __TypeKind, - }; - use pretty_assertions::assert_eq; - - use crate::handlers::Handler; - - use super::Object; - - #[test] - fn can_render_object() { - let t: FullType = FullType { - kind: Some(__TypeKind::OBJECT), - name: Some("CacheVolume".into()), - description: Some("A directory whose contents persists across sessions".into()), - fields: Some(vec![FullTypeFields { - name: Some("id".into()), - description: None, - args: None, - type_: Some(FullTypeFieldsType { - type_ref: TypeRef { - kind: Some(__TypeKind::NON_NULL), - name: None, - of_type: Some(Box::new(TypeRef { - kind: Some(__TypeKind::SCALAR), - name: Some("CacheID".into()), - of_type: None, - })), - }, - }), - is_deprecated: Some(false), - deprecation_reason: None, - }]), - input_fields: None, - interfaces: None, - enum_values: None, - possible_types: None, - }; - let expected = r#"use crate::querybuilder::Selection; -use dagger_core::connect_params::ConnectParams; -use std::process::Child; -use std::sync::Arc; - - -/// A directory whose contents persists across sessions -pub struct CacheVolume { - pub conn: ConnectParams, - pub proc: Arc, - pub selection: Selection, -} - -impl CacheVolume { - pub fn id( - &self, - ) -> CacheID { - let query = self.selection.select("id"); - - CacheID { - conn: self.conn.clone(), - proc: self.proc.clone(), - selection: query, - } - - todo!() - } -} -"#; - let handler = Object {}; - let obj = handler.render(&t).unwrap(); - let actual = obj.to_file_string().unwrap(); - - assert_eq!(actual, expected); - } - - #[test] - fn can_render_query_container() { - let description = "Loads a container from ID.\nNull ID returns an empty container (scratch).\nOptional platform argument initializes new containers to execute and publish as that platform. Platform defaults to that of the builder's host.".into(); - - let t: FullType = FullType { - kind: Some(__TypeKind::OBJECT), - name: Some("Query".into()), - description: None, - fields: Some(vec![FullTypeFields { - name: Some("container".into()), - description: Some(description), - args: Some(vec![ - Some(FullTypeFieldsArgs { - input_value: InputValue { - name: "id".into(), - description: None, - type_: TypeRef { - kind: Some(__TypeKind::SCALAR), - name: Some("ContainerID".into()), - of_type: None, - }, - default_value: None, - }, - }), - Some(FullTypeFieldsArgs { - input_value: InputValue { - name: "platform".into(), - description: None, - type_: TypeRef { - kind: Some(__TypeKind::SCALAR), - name: Some("Platform".into()), - of_type: None, - }, - default_value: None, - }, - }), - ]), - type_: Some(FullTypeFieldsType { - type_ref: TypeRef { - kind: Some(__TypeKind::NON_NULL), - name: None, - of_type: Some(Box::new(TypeRef { - kind: Some(__TypeKind::SCALAR), - name: Some("CacheID".into()), - of_type: None, - })), - }, - }), - is_deprecated: Some(false), - deprecation_reason: None, - }]), - input_fields: None, - interfaces: None, - enum_values: None, - possible_types: None, - }; - let expected = r#"use crate::querybuilder::Selection; -use dagger_core::connect_params::ConnectParams; -use std::process::Child; -use std::sync::Arc; - - -/// Loads a container from ID. -/// Null ID returns an empty container (scratch). -/// Optional platform argument initializes new containers to execute and publish as that platform. Platform defaults to that of the builder's host. -pub struct ContainerArgs { - pub id: Option, - pub platform: Option, -} - -pub struct Query { - pub conn: ConnectParams, - pub proc: Arc, - pub selection: Selection, -} - -impl Query { - pub fn container( - &self, - args: &ContainerArgs - ) -> CacheID { - let query = self.selection.select("container"); - query.args(args); - - CacheID { - conn: self.conn.clone(), - proc: self.proc.clone(), - selection: query, - } - - todo!() - } -} -"#; - let handler = Object {}; - let obj = handler.render(&t).unwrap(); - let actual = obj.to_file_string().unwrap(); - - assert_eq!(actual, expected); - } -} diff --git a/crates/dagger-codegen/src/handlers/scalar.rs b/crates/dagger-codegen/src/handlers/scalar.rs deleted file mode 100644 index e8d7b2f..0000000 --- a/crates/dagger-codegen/src/handlers/scalar.rs +++ /dev/null @@ -1,42 +0,0 @@ -use dagger_core::introspection::FullType; -use genco::{prelude::rust, quote}; - -use crate::predicates::is_custom_scalar_type; - -use super::{utility::render_description, Handler}; - -pub struct Scalar; - -impl Handler for Scalar { - fn predicate(&self, t: &FullType) -> bool { - is_custom_scalar_type(t) - } - - fn render(&self, t: &FullType) -> eyre::Result { - let mut out = rust::Tokens::new(); - - let description = - render_description(t).ok_or(eyre::anyhow!("could not find description"))?; - let tstruct = self.render_struct(t)?; - - out.append(description); - out.push(); - out.append(tstruct); - - Ok(out) - } - - fn render_struct(&self, t: &FullType) -> eyre::Result { - let name = t.name.as_ref().ok_or(eyre::anyhow!("name not found"))?; - - let scalar = rust::import("dagger_core", "Scalar"); - - Ok(quote! { - pub struct $name($scalar); - }) - } - - fn render_impl(&self, _t: &FullType) -> eyre::Result { - todo!() - } -} diff --git a/crates/dagger-codegen/src/handlers/type_ref.rs b/crates/dagger-codegen/src/handlers/type_ref.rs deleted file mode 100644 index 1096313..0000000 --- a/crates/dagger-codegen/src/handlers/type_ref.rs +++ /dev/null @@ -1,97 +0,0 @@ -use dagger_core::introspection::TypeRef; -use genco::prelude::rust; -use genco::prelude::*; - -use crate::predicates::{ - is_custom_scalar_type_ref, is_list_type, is_required_type_ref, is_scalar_type_ref, -}; - -pub fn render_type_ref(inner: &TypeRef) -> eyre::Result { - let extract_of_type = |t: &TypeRef| -> Option { - return t.clone().of_type.map(|t| *t); - }; - - let (optional, inner) = if !is_required_type_ref(inner) { - (true, inner.clone()) - } else { - (false, extract_of_type(inner).unwrap()) - }; - - if is_list_type(&inner) { - if let Some(inner_of_type) = extract_of_type(&inner) { - let inner_field = render_type_ref(&inner_of_type)?; - if optional { - return Ok(quote! { - Option> - }); - } - return Ok(quote! { - Vec<$inner_field> - }); - } - } - - if is_custom_scalar_type_ref(&inner) { - if let Some(inner_of_type) = extract_of_type(&inner) { - let inner_field = render_type_ref(&inner_of_type)?; - if optional { - return Ok(quote! { - Option<$inner_field> - }); - } - return Ok(quote! { - $inner_field - }); - } - } - - if is_scalar_type_ref(&inner) { - let name = match inner.name.as_ref().map(|s| s.as_str()) { - Some("ID") => "ID", - Some("Int") => "Int", - Some("String") => "String", - Some("Float") => "Float", - Some("Boolean") => "Boolean", - Some("Date") => "Date", - Some("DateTime") => "DateTime", - Some("Time") => "Time", - Some("Decimal") => "Decimal", - Some(n) => n, - _ => eyre::bail!("missing type in the end of chain"), - }; - - if optional { - return Ok(quote! { - Option<$name> - }); - } - - return Ok(quote! { - $name - }); - } - - if let Some(inner_type) = inner.of_type.as_ref() { - let inner_field = render_type_ref(&inner_type)?; - if optional { - return Ok(quote! { - Option<$inner_field> - }); - } - - return Ok(inner_field); - } - - if let Some(name) = inner.name.as_ref() { - if optional { - return Ok(quote! { - Option<$name> - }); - } - return Ok(quote! { - $name - }); - } - - eyre::bail!("could not determine type") -} diff --git a/crates/dagger-codegen/src/handlers/utility.rs b/crates/dagger-codegen/src/handlers/utility.rs deleted file mode 100644 index 3fc9f6d..0000000 --- a/crates/dagger-codegen/src/handlers/utility.rs +++ /dev/null @@ -1,56 +0,0 @@ -use dagger_core::introspection::{FullType, FullTypeFields, InputValue}; -use genco::{prelude::*, quote}; - -pub fn render_description(t: &FullType) -> Option { - if let Some(description) = t.description.as_ref() { - let lines = description.split('\n'); - let output: rust::Tokens = quote! { - $(for line in lines => $(format!("\n/// {line}"))) - }; - - return Some(output); - } - - None -} - -pub fn render_description_from_field(t: &FullTypeFields) -> Option { - if let Some(description) = t.description.as_ref() { - let lines = description.split('\n'); - let output: rust::Tokens = quote! { - $(for line in lines => $(format!("\n/// {line}"))) - }; - - return Some(output); - } - - None -} - -pub fn render_description_from_input_value(t: &InputValue, name: &String) -> Option { - if let Some(description) = t.description.as_ref() { - if description == "" { - return None; - } - let lines = description.split('\n').collect::>(); - let mut output = rust::Tokens::new(); - - if let Some(line) = lines.first() { - output.append(quote! { - $(format!("/// * `{name}` - {line}")) - }); - output.push(); - } - - for line in lines.iter().skip(1) { - output.append(quote! { - $(format!("/// {line}")) - }); - output.push(); - } - - return Some(output); - } - - None -} diff --git a/crates/dagger-codegen/src/lib.rs b/crates/dagger-codegen/src/lib.rs index af8fd65..c03339e 100644 --- a/crates/dagger-codegen/src/lib.rs +++ b/crates/dagger-codegen/src/lib.rs @@ -1,4 +1,12 @@ -pub mod codegen; -mod handlers; -mod models; -mod predicates; +mod functions; +mod generator; +pub mod rust; +mod visitor; + +use dagger_core::introspection::Schema; + +use self::generator::DynGenerator; + +pub fn generate(schema: Schema, generator: DynGenerator) -> eyre::Result { + generator.generate(schema) +} diff --git a/crates/dagger-codegen/src/models.rs b/crates/dagger-codegen/src/models.rs deleted file mode 100644 index 7ed5a54..0000000 --- a/crates/dagger-codegen/src/models.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub enum Scalars { - ID(String), - Int(usize), - String(String), - Float(f64), - Boolean(bool), - Date(String), - DateTime(String), - Time(String), - Decimal(f64), -} diff --git a/crates/dagger-codegen/src/predicates.rs b/crates/dagger-codegen/src/predicates.rs deleted file mode 100644 index d75f0ce..0000000 --- a/crates/dagger-codegen/src/predicates.rs +++ /dev/null @@ -1,101 +0,0 @@ -use dagger_core::introspection::{FullType, FullTypeInputFields, TypeRef, __TypeKind}; - -use crate::models::Scalars; - -pub fn is_scalar_type(t: &FullType) -> bool { - if let Some(__TypeKind::SCALAR) = t.kind { - return true; - } - false -} - -pub fn is_scalar_type_ref(t: &TypeRef) -> bool { - if let Some(__TypeKind::SCALAR) = t.kind { - return true; - } - false -} - -pub fn is_enum_type(t: &FullType) -> bool { - if let Some(__TypeKind::ENUM) = t.kind { - return true; - } - false -} - -pub fn is_input_object_type(t: &FullType) -> bool { - if let Some(__TypeKind::INPUT_OBJECT) = t.kind { - return true; - } - false -} - -pub fn is_required_type(t: &FullTypeInputFields) -> bool { - match t.input_value.type_.kind { - Some(__TypeKind::NON_NULL) => return true, - Some(_) => return false, - _ => return false, - } -} - -pub fn is_required_type_ref(t: &TypeRef) -> bool { - match t.kind { - Some(__TypeKind::NON_NULL) => return true, - Some(_) => return false, - _ => return false, - } -} - -pub fn is_list_type(t: &TypeRef) -> bool { - if let Some(__TypeKind::LIST) = t.kind { - return true; - } - false -} - -pub fn is_object_type(t: &FullType) -> bool { - if let Some(__TypeKind::OBJECT) = t.kind { - return true; - } - false -} - -pub fn is_custom_scalar_type(t: &FullType) -> bool { - if is_scalar_type(t) { - // TODO: Insert scalar - let _ = match t.name.as_ref().map(|s| s.as_str()) { - Some("ID") => Scalars::ID("ID".into()), - Some("Int") => Scalars::Int(0), - Some("String") => Scalars::String("ID".into()), - Some("Float") => Scalars::Float(0.0), - Some("Boolean") => Scalars::Boolean(false), - Some("Date") => Scalars::Date("ID".into()), - Some("DateTime") => Scalars::DateTime("ID".into()), - Some("Time") => Scalars::Time("ID".into()), - Some("Decimal") => Scalars::Decimal(0.0), - Some(_) => return true, - None => return false, - }; - } - false -} - -pub fn is_custom_scalar_type_ref(t: &TypeRef) -> bool { - if is_scalar_type_ref(t) { - // TODO: Insert scalar - let _ = match t.name.as_ref().map(|s| s.as_str()) { - Some("ID") => Scalars::ID("ID".into()), - Some("Int") => Scalars::Int(0), - Some("String") => Scalars::String("ID".into()), - Some("Float") => Scalars::Float(0.0), - Some("Boolean") => Scalars::Boolean(false), - Some("Date") => Scalars::Date("ID".into()), - Some("DateTime") => Scalars::DateTime("ID".into()), - Some("Time") => Scalars::Time("ID".into()), - Some("Decimal") => Scalars::Decimal(0.0), - Some(_) => return true, - None => return false, - }; - } - false -} diff --git a/crates/dagger-codegen/src/rust/format.rs b/crates/dagger-codegen/src/rust/format.rs new file mode 100644 index 0000000..ad7af38 --- /dev/null +++ b/crates/dagger-codegen/src/rust/format.rs @@ -0,0 +1,64 @@ +use crate::functions::FormatTypeFuncs; + +use super::functions::format_name; + +pub struct FormatTypeFunc; + +impl FormatTypeFuncs for FormatTypeFunc { + fn format_kind_list(&self, representation: &str) -> String { + format!("Vec<{}>", representation) + } + + fn format_kind_scalar_string(&self, representation: &str) -> String { + let mut rep = representation.to_string(); + rep.push_str("String"); + rep + } + + fn format_kind_scalar_int(&self, representation: &str) -> String { + let mut rep = representation.to_string(); + rep.push_str("isize"); + rep + } + + fn format_kind_scalar_float(&self, representation: &str) -> String { + let mut rep = representation.to_string(); + rep.push_str("float"); + rep + } + + fn format_kind_scalar_boolean(&self, representation: &str) -> String { + let mut rep = representation.to_string(); + rep.push_str("bool"); + rep + } + + fn format_kind_scalar_default( + &self, + representation: &str, + ref_name: &str, + input: bool, + ) -> String { + let mut rep = representation.to_string(); + rep.push_str(ref_name); + rep + } + + fn format_kind_object(&self, representation: &str, ref_name: &str) -> String { + let mut rep = representation.to_string(); + rep.push_str(&format_name(ref_name)); + rep + } + + fn format_kind_input_object(&self, representation: &str, ref_name: &str) -> String { + let mut rep = representation.to_string(); + rep.push_str(&format_name(ref_name)); + rep + } + + fn format_kind_enum(&self, representation: &str, ref_name: &str) -> String { + let mut rep = representation.to_string(); + rep.push_str(ref_name); + rep + } +} diff --git a/crates/dagger-codegen/src/rust/functions.rs b/crates/dagger-codegen/src/rust/functions.rs new file mode 100644 index 0000000..2daa519 --- /dev/null +++ b/crates/dagger-codegen/src/rust/functions.rs @@ -0,0 +1,5 @@ +use convert_case::{Case, Casing}; + +pub fn format_name(s: &str) -> String { + s.to_case(Case::Pascal) +} diff --git a/crates/dagger-codegen/src/rust/mod.rs b/crates/dagger-codegen/src/rust/mod.rs new file mode 100644 index 0000000..6910fa0 --- /dev/null +++ b/crates/dagger-codegen/src/rust/mod.rs @@ -0,0 +1,94 @@ +pub mod format; +mod functions; +pub mod templates; + +use std::sync::{Arc, Mutex}; + +use dagger_core::introspection::Schema; +use eyre::Context; +use genco::prelude::rust; + +use crate::generator::Generator; +use crate::visitor::{VisitHandlers, Visitor}; + +use self::templates::enum_tmpl::render_enum; +use self::templates::input_tmpl::render_input; +use self::templates::object_tmpl::render_object; +use self::templates::scalar_tmpl::render_scalar; + +pub struct RustGenerator {} + +impl Generator for RustGenerator { + fn generate(&self, schema: Schema) -> eyre::Result { + let render = Arc::new(Mutex::new(rust::Tokens::new())); + + let visitor = Visitor { + schema, + handlers: VisitHandlers { + visit_scalar: Arc::new({ + let render = render.clone(); + move |t| { + let rendered_scalar = render_scalar()?; + + let mut render = render.lock().unwrap(); + + render.append(rendered_scalar); + render.push(); + + Ok(()) + } + }), + visit_object: Arc::new({ + let render = render.clone(); + + move |t| { + let rendered_scalar = render_object()?; + + let mut render = render.lock().unwrap(); + + render.append(rendered_scalar); + render.push(); + + Ok(()) + } + }), + visit_input: Arc::new({ + let render = render.clone(); + + move |t| { + let rendered_scalar = render_input()?; + + let mut render = render.lock().unwrap(); + + render.append(rendered_scalar); + render.push(); + + Ok(()) + } + }), + visit_enum: Arc::new({ + let render = render.clone(); + + move |t| { + let rendered_scalar = render_enum()?; + + let mut render = render.lock().unwrap(); + + render.append(rendered_scalar); + render.push(); + + Ok(()) + } + }), + }, + }; + + visitor.run()?; + + let rendered = render.lock().unwrap(); + + rendered + .to_file_string() + .context("could not render to file string") + } +} diff --git a/crates/dagger-codegen/src/rust/templates/enum_tmpl.rs b/crates/dagger-codegen/src/rust/templates/enum_tmpl.rs new file mode 100644 index 0000000..bd22bbf --- /dev/null +++ b/crates/dagger-codegen/src/rust/templates/enum_tmpl.rs @@ -0,0 +1,6 @@ +use genco::prelude::rust; +use genco::quote; + +pub fn render_enum() -> eyre::Result { + Ok(quote! {}) +} diff --git a/crates/dagger-codegen/src/rust/templates/input_tmpl.rs b/crates/dagger-codegen/src/rust/templates/input_tmpl.rs new file mode 100644 index 0000000..10add34 --- /dev/null +++ b/crates/dagger-codegen/src/rust/templates/input_tmpl.rs @@ -0,0 +1,6 @@ +use genco::prelude::rust; +use genco::quote; + +pub fn render_input() -> eyre::Result { + Ok(quote! {}) +} diff --git a/crates/dagger-codegen/src/rust/templates/mod.rs b/crates/dagger-codegen/src/rust/templates/mod.rs new file mode 100644 index 0000000..b37c44f --- /dev/null +++ b/crates/dagger-codegen/src/rust/templates/mod.rs @@ -0,0 +1,4 @@ +pub mod enum_tmpl; +pub mod input_tmpl; +pub mod object_tmpl; +pub mod scalar_tmpl; diff --git a/crates/dagger-codegen/src/rust/templates/object_tmpl.rs b/crates/dagger-codegen/src/rust/templates/object_tmpl.rs new file mode 100644 index 0000000..842fe08 --- /dev/null +++ b/crates/dagger-codegen/src/rust/templates/object_tmpl.rs @@ -0,0 +1,6 @@ +use genco::prelude::rust; +use genco::quote; + +pub fn render_object() -> eyre::Result { + Ok(quote! {}) +} diff --git a/crates/dagger-codegen/src/rust/templates/scalar_tmpl.rs b/crates/dagger-codegen/src/rust/templates/scalar_tmpl.rs new file mode 100644 index 0000000..daebe58 --- /dev/null +++ b/crates/dagger-codegen/src/rust/templates/scalar_tmpl.rs @@ -0,0 +1,6 @@ +use genco::prelude::rust; +use genco::quote; + +pub fn render_scalar() -> eyre::Result { + Ok(quote! {}) +} diff --git a/crates/dagger-codegen/src/visitor.rs b/crates/dagger-codegen/src/visitor.rs new file mode 100644 index 0000000..b7d967b --- /dev/null +++ b/crates/dagger-codegen/src/visitor.rs @@ -0,0 +1,97 @@ +use std::sync::Arc; + +use dagger_core::introspection::{FullType, Schema, __TypeKind}; +use itertools::Itertools; + +pub struct Visitor { + pub schema: Schema, + pub handlers: VisitHandlers, +} + +pub type VisitFunc = Arc eyre::Result<()>>; + +pub struct VisitHandlers { + pub visit_scalar: VisitFunc, + pub visit_object: VisitFunc, + pub visit_input: VisitFunc, + pub visit_enum: VisitFunc, +} + +struct SequenceItem { + kind: __TypeKind, + handler: VisitFunc, + ignore: Option>, +} + +impl Visitor { + pub fn run(&self) -> eyre::Result<()> { + let sequence = vec![ + SequenceItem { + kind: __TypeKind::SCALAR, + handler: self.handlers.visit_scalar.clone(), + ignore: Some(vec![ + "String".into(), + "Float".into(), + "Int".into(), + "Boolean".into(), + "DateTime".into(), + "ID".into(), + ]), + }, + SequenceItem { + kind: __TypeKind::INPUT_OBJECT, + handler: self.handlers.visit_input.clone(), + ignore: None, + }, + SequenceItem { + kind: __TypeKind::OBJECT, + handler: self.handlers.visit_object.clone(), + ignore: None, + }, + SequenceItem { + kind: __TypeKind::ENUM, + handler: self.handlers.visit_enum.clone(), + ignore: None, + }, + ]; + + for item in sequence { + self.visit(&item)?; + } + + Ok(()) + } + + fn visit(&self, item: &SequenceItem) -> eyre::Result<()> { + self.schema + .types + .as_ref() + .unwrap() + .into_iter() + .map(|t| t.as_ref().unwrap()) + .filter(|t| match t.full_type.kind.as_ref().unwrap() == &item.kind { + true => match (item.ignore.as_ref(), t.full_type.name.as_ref()) { + (Some(ignore), Some(name)) => { + if ignore.contains(name) { + return false; + } + + return true; + } + _ => false, + }, + false => false, + }) + .sorted_by(|a, b| { + a.full_type + .name + .as_ref() + .unwrap() + .cmp(&b.full_type.name.as_ref().unwrap()) + }) + .map(|t| (*item.handler)(&t.full_type)) + .collect::>>()?; + + Ok(()) + } +} diff --git a/crates/dagger-sdk/src/gen.rs b/crates/dagger-sdk/src/gen.rs index 39290a9..81d98c0 100644 --- a/crates/dagger-sdk/src/gen.rs +++ b/crates/dagger-sdk/src/gen.rs @@ -382,11 +382,7 @@ impl Container { let query = self.selection.select("envVariable"); query.args(args); - Option { - conn: self.conn.clone(), - proc: self.proc.clone(), - selection: query, - } + query.execute(&graphql_client(&self.conn)).unwrap() } pub fn env_variables( @@ -420,11 +416,7 @@ impl Container { ) -> Option { let query = self.selection.select("exitCode"); - Option { - conn: self.conn.clone(), - proc: self.proc.clone(), - selection: query, - } + query.execute(&graphql_client(&self.conn)).unwrap() } pub fn export( @@ -500,11 +492,7 @@ impl Container { let query = self.selection.select("label"); query.args(args); - Option { - conn: self.conn.clone(), - proc: self.proc.clone(), - selection: query, - } + query.execute(&graphql_client(&self.conn)).unwrap() } pub fn labels( @@ -588,11 +576,7 @@ impl Container { ) -> Option { let query = self.selection.select("stderr"); - Option { - conn: self.conn.clone(), - proc: self.proc.clone(), - selection: query, - } + query.execute(&graphql_client(&self.conn)).unwrap() } pub fn stdout( @@ -600,11 +584,7 @@ impl Container { ) -> Option { let query = self.selection.select("stdout"); - Option { - conn: self.conn.clone(), - proc: self.proc.clone(), - selection: query, - } + query.execute(&graphql_client(&self.conn)).unwrap() } pub fn user( @@ -612,11 +592,7 @@ impl Container { ) -> Option { let query = self.selection.select("user"); - Option { - conn: self.conn.clone(), - proc: self.proc.clone(), - selection: query, - } + query.execute(&graphql_client(&self.conn)).unwrap() } pub fn with_default_args( @@ -946,11 +922,7 @@ impl Container { ) -> Option { let query = self.selection.select("workdir"); - Option { - conn: self.conn.clone(), - proc: self.proc.clone(), - selection: query, - } + query.execute(&graphql_client(&self.conn)).unwrap() } } @@ -1784,11 +1756,7 @@ impl Project { ) -> Option { let query = self.selection.select("schema"); - Option { - conn: self.conn.clone(), - proc: self.proc.clone(), - selection: query, - } + query.execute(&graphql_client(&self.conn)).unwrap() } pub fn sdk( @@ -1796,11 +1764,7 @@ impl Project { ) -> Option { let query = self.selection.select("sdk"); - Option { - conn: self.conn.clone(), - proc: self.proc.clone(), - selection: query, - } + query.execute(&graphql_client(&self.conn)).unwrap() } } diff --git a/src/cli_generate.rs b/src/cli_generate.rs index 1005266..c9c5e75 100644 --- a/src/cli_generate.rs +++ b/src/cli_generate.rs @@ -1,7 +1,9 @@ use std::io::Write; +use std::sync::Arc; use clap::{Arg, ArgMatches}; -use dagger_codegen::codegen::CodeGeneration; +use dagger_codegen::generate; +use dagger_codegen::rust::RustGenerator; use dagger_core::config::Config; use dagger_core::engine::Engine; use dagger_core::session::Session; @@ -21,7 +23,10 @@ impl GenerateCommand { let session = Session::new(); let req = session.start(&cfg, &conn)?; let schema = session.schema(req)?; - let code = CodeGeneration::new().generate(&schema)?; + let code = generate( + schema.into_schema().schema.unwrap(), + Arc::new(RustGenerator {}), + )?; if let Some(output) = arg_matches.get_one::("output") { let mut file = std::fs::File::create(output)?;