mirror of
https://github.com/kjuulh/dagger-rs.git
synced 2025-07-25 19:09:22 +02:00
with objects
This commit is contained in:
@@ -8,9 +8,8 @@ use graphql_introspection_query::introspection_response::{
|
||||
FullType, IntrospectionResponse, Schema,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
handlers::{enumeration::Enumeration, input::Input, scalar::Scalar, DynHandler, Handlers},
|
||||
predicates::is_custom_scalar_type,
|
||||
use crate::handlers::{
|
||||
enumeration::Enumeration, input::Input, object::Object, scalar::Scalar, DynHandler, Handlers,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -25,6 +24,7 @@ impl CodeGeneration {
|
||||
Arc::new(Scalar {}),
|
||||
Arc::new(Enumeration {}),
|
||||
Arc::new(Input {}),
|
||||
Arc::new(Object {}),
|
||||
],
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,10 @@ impl CodeGeneration {
|
||||
$(format!("// code generated by dagger. DO NOT EDIT."))
|
||||
});
|
||||
|
||||
output.push();
|
||||
output.append(render_base_types());
|
||||
output.push();
|
||||
|
||||
let types = get_types(schema)?;
|
||||
//let remaining: Vec<Option<String>> = types.into_iter().map(type_name).collect();
|
||||
//
|
||||
@@ -93,7 +97,8 @@ impl CodeGeneration {
|
||||
pub fn type_name(&self, t: &FullType) -> Option<String> {
|
||||
let name = t.name.as_ref();
|
||||
if let Some(name) = name {
|
||||
if name.starts_with("_") || !is_custom_scalar_type(t) {
|
||||
if name.starts_with("_") {
|
||||
//|| !is_custom_scalar_type(t) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -124,13 +129,22 @@ impl CodeGeneration {
|
||||
}
|
||||
}
|
||||
|
||||
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<Vec<&FullType>> {
|
||||
let types = schema
|
||||
.types
|
||||
.as_ref()
|
||||
.ok_or(eyre::anyhow!("types not found on schema"))?;
|
||||
|
||||
// 1. Get a list of all types and map it to handlers
|
||||
let types: Vec<&FullType> = types
|
||||
.iter()
|
||||
.map(|t| t.as_ref().map(|t| &t.full_type))
|
||||
|
@@ -23,7 +23,9 @@ impl Handler for Enumeration {
|
||||
|
||||
let out = quote! {
|
||||
$description
|
||||
pub enum $name {}
|
||||
pub enum $name {
|
||||
// TODO: Add individual items
|
||||
}
|
||||
};
|
||||
|
||||
Ok(out)
|
||||
|
33
crates/dagger-codegen/src/handlers/fields.rs
Normal file
33
crates/dagger-codegen/src/handlers/fields.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use convert_case::{Case, Casing};
|
||||
use genco::{prelude::rust, quote};
|
||||
use graphql_introspection_query::introspection_response::FullTypeFields;
|
||||
|
||||
use super::{
|
||||
type_ref,
|
||||
utility::{render_description, render_description_from_field},
|
||||
};
|
||||
|
||||
pub fn render_fields(fields: &Vec<FullTypeFields>) -> eyre::Result<Option<rust::Tokens>> {
|
||||
let mut collected_fields: Vec<rust::Tokens> = 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);
|
||||
|
||||
collected_fields.push(quote! {
|
||||
$(if description.is_some() => $description)
|
||||
pub fn $name(&self) -> $output {
|
||||
todo!()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Ok(Some(quote! {
|
||||
$(for field in collected_fields => $field $['\n'] )
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn render_field_output(field: &FullTypeFields) -> eyre::Result<rust::Tokens> {
|
||||
let inner = &field.type_.as_ref().unwrap();
|
||||
type_ref::render_type_ref(&inner.type_ref)
|
||||
}
|
@@ -1,93 +1,12 @@
|
||||
use genco::prelude::rust;
|
||||
use genco::prelude::*;
|
||||
use graphql_introspection_query::introspection_response::{FullType, FullTypeInputFields, TypeRef};
|
||||
use graphql_introspection_query::introspection_response::FullType;
|
||||
|
||||
use crate::predicates::{
|
||||
is_custom_scalar_type_ref, is_input_object_type, is_list_type, is_required_type_ref,
|
||||
is_scalar_type_ref,
|
||||
};
|
||||
use crate::predicates::is_input_object_type;
|
||||
|
||||
use super::{utility::render_description, Handler};
|
||||
use super::{input_field::render_input_fields, utility::render_description, Handler};
|
||||
|
||||
pub struct Input;
|
||||
impl Input {
|
||||
fn render_input_fields(
|
||||
&self,
|
||||
input_fields: &Vec<FullTypeInputFields>,
|
||||
) -> eyre::Result<Option<rust::Tokens>> {
|
||||
let mut fields: Vec<(String, rust::Tokens)> = vec![];
|
||||
for field in input_fields.iter() {
|
||||
fields.push((
|
||||
field.input_value.name.clone(),
|
||||
self.render_input_field(field)?,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Some(quote! {
|
||||
$(for (name, field) in fields => pub $name: $field $['\n'] )
|
||||
}))
|
||||
}
|
||||
|
||||
fn render_input_field(&self, field: &FullTypeInputFields) -> eyre::Result<rust::Tokens> {
|
||||
let inner = &field.input_value.type_;
|
||||
self.render_type_ref(inner)
|
||||
}
|
||||
|
||||
fn render_type_ref(&self, inner: &TypeRef) -> eyre::Result<rust::Tokens> {
|
||||
let extract_of_type = |t: &TypeRef| -> Option<TypeRef> {
|
||||
return t.clone().of_type.map(|t| *t);
|
||||
};
|
||||
|
||||
if !is_required_type_ref(inner) {
|
||||
if let Some(inner_of_type) = extract_of_type(inner) {
|
||||
let inner_field = self.render_type_ref(&inner_of_type)?;
|
||||
return Ok(quote! {
|
||||
Option<$inner_field>
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if is_list_type(&inner) {
|
||||
if let Some(inner_of_type) = extract_of_type(inner) {
|
||||
let inner_field = self.render_type_ref(&inner_of_type)?;
|
||||
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 = self.render_type_ref(&inner_of_type)?;
|
||||
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"),
|
||||
};
|
||||
|
||||
return Ok(quote! {
|
||||
$name
|
||||
});
|
||||
}
|
||||
|
||||
eyre::bail!("could not determine type")
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler for Input {
|
||||
fn predicate(&self, t: &FullType) -> bool {
|
||||
@@ -104,7 +23,7 @@ impl Handler for Input {
|
||||
let input = rust::import("dagger_core", "Input");
|
||||
|
||||
let fields = match t.input_fields.as_ref() {
|
||||
Some(i) => self.render_input_fields(i)?,
|
||||
Some(i) => render_input_fields(i)?,
|
||||
None => None,
|
||||
};
|
||||
|
||||
|
22
crates/dagger-codegen/src/handlers/input_field.rs
Normal file
22
crates/dagger-codegen/src/handlers/input_field.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use genco::{prelude::rust, quote};
|
||||
use graphql_introspection_query::introspection_response::FullTypeInputFields;
|
||||
|
||||
use super::type_ref;
|
||||
|
||||
pub fn render_input_fields(
|
||||
input_fields: &Vec<FullTypeInputFields>,
|
||||
) -> eyre::Result<Option<rust::Tokens>> {
|
||||
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<rust::Tokens> {
|
||||
let inner = &field.input_value.type_;
|
||||
type_ref::render_type_ref(inner)
|
||||
}
|
@@ -1,6 +1,10 @@
|
||||
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;
|
||||
|
101
crates/dagger-codegen/src/handlers/object.rs
Normal file
101
crates/dagger-codegen/src/handlers/object.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use genco::{prelude::rust, quote};
|
||||
use graphql_introspection_query::introspection_response::FullType;
|
||||
|
||||
use crate::predicates::is_object_type;
|
||||
|
||||
use super::{fields, 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<rust::Tokens> {
|
||||
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.fields.as_ref() {
|
||||
Some(i) => fields::render_fields(i)?,
|
||||
None => None,
|
||||
};
|
||||
|
||||
let out = quote! {
|
||||
$(if description.is_some() => $description)
|
||||
pub struct $name {
|
||||
}
|
||||
|
||||
impl $name {
|
||||
$(if fields.is_some() => $fields)
|
||||
}
|
||||
|
||||
impl $input for $name {}
|
||||
};
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use graphql_introspection_query::introspection_response::{
|
||||
FullType, FullTypeFields, FullTypeFieldsType, 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 dagger_core::Input;
|
||||
|
||||
|
||||
/// A directory whose contents persists across sessions
|
||||
pub struct CacheVolume {
|
||||
pub id: Option<CacheID>
|
||||
}
|
||||
|
||||
impl Input for CacheVolume {}
|
||||
"#;
|
||||
let handler = Object {};
|
||||
let obj = handler.render(&t).unwrap();
|
||||
let actual = obj.to_file_string().unwrap();
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
72
crates/dagger-codegen/src/handlers/type_ref.rs
Normal file
72
crates/dagger-codegen/src/handlers/type_ref.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use genco::prelude::rust;
|
||||
use genco::prelude::*;
|
||||
use graphql_introspection_query::introspection_response::TypeRef;
|
||||
|
||||
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<rust::Tokens> {
|
||||
let extract_of_type = |t: &TypeRef| -> Option<TypeRef> {
|
||||
return t.clone().of_type.map(|t| *t);
|
||||
};
|
||||
|
||||
if !is_required_type_ref(inner) {
|
||||
if let Some(inner_of_type) = extract_of_type(inner) {
|
||||
let inner_field = render_type_ref(&inner_of_type)?;
|
||||
return Ok(quote! {
|
||||
Option<$inner_field>
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if is_list_type(&inner) {
|
||||
if let Some(inner_of_type) = extract_of_type(inner) {
|
||||
let inner_field = render_type_ref(&inner_of_type)?;
|
||||
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)?;
|
||||
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"),
|
||||
};
|
||||
|
||||
return Ok(quote! {
|
||||
$name
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(inner_type) = inner.of_type.as_ref() {
|
||||
return render_type_ref(&inner_type);
|
||||
}
|
||||
|
||||
if let Some(name) = inner.name.as_ref() {
|
||||
return Ok(quote! {
|
||||
$name
|
||||
});
|
||||
}
|
||||
|
||||
eyre::bail!("could not determine type")
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
use genco::{prelude::*, quote};
|
||||
use graphql_introspection_query::introspection_response::FullType;
|
||||
use graphql_introspection_query::introspection_response::{FullType, FullTypeFields};
|
||||
|
||||
pub fn render_description(t: &FullType) -> Option<rust::Tokens> {
|
||||
if let Some(description) = t.description.as_ref() {
|
||||
@@ -13,3 +13,16 @@ pub fn render_description(t: &FullType) -> Option<rust::Tokens> {
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn render_description_from_field(t: &FullTypeFields) -> Option<rust::Tokens> {
|
||||
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
|
||||
}
|
||||
|
@@ -55,6 +55,13 @@ pub fn is_list_type(t: &TypeRef) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_object_type(t: &FullType) -> bool {
|
||||
if let Some(introspection_response::__TypeKind::OBJECT) = t.kind {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_custom_scalar_type(t: &FullType) -> bool {
|
||||
if is_scalar_type(t) {
|
||||
// TODO: Insert scalar
|
||||
|
Reference in New Issue
Block a user