mirror of
https://github.com/kjuulh/dagger-rs.git
synced 2024-11-22 15:22:13 +01:00
with input objects
This commit is contained in:
parent
2a1f7c3f26
commit
dc53fc1d47
@ -9,7 +9,7 @@ use graphql_introspection_query::introspection_response::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
handlers::{enumeration::Enumeration, scalar::Scalar, DynHandler, Handlers},
|
handlers::{enumeration::Enumeration, input::Input, scalar::Scalar, DynHandler, Handlers},
|
||||||
predicates::is_custom_scalar_type,
|
predicates::is_custom_scalar_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,7 +21,11 @@ pub struct CodeGeneration {
|
|||||||
impl CodeGeneration {
|
impl CodeGeneration {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
handlers: vec![Arc::new(Scalar {}), Arc::new(Enumeration {})],
|
handlers: vec![
|
||||||
|
Arc::new(Scalar {}),
|
||||||
|
Arc::new(Enumeration {}),
|
||||||
|
Arc::new(Input {}),
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
243
crates/dagger-codegen/src/handlers/input.rs
Normal file
243
crates/dagger-codegen/src/handlers/input.rs
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
use genco::prelude::rust;
|
||||||
|
use genco::prelude::*;
|
||||||
|
use graphql_introspection_query::introspection_response::{FullType, FullTypeInputFields, TypeRef};
|
||||||
|
|
||||||
|
use crate::predicates::{
|
||||||
|
is_custom_scalar_type_ref, is_input_object_type, is_list_type, is_required_type_ref,
|
||||||
|
is_scalar_type_ref,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{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 name = &field.input_value.name;
|
||||||
|
|
||||||
|
let mut inner: Option<&TypeRef> = None;
|
||||||
|
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 {
|
||||||
|
is_input_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.input_fields.as_ref() {
|
||||||
|
Some(i) => self.render_input_fields(i)?,
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let out = quote! {
|
||||||
|
$(if description.is_some() => $description)
|
||||||
|
pub struct $name {
|
||||||
|
$(if fields.is_some() => $fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $input for $name {}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use graphql_introspection_query::introspection_response::{
|
||||||
|
FullType, FullTypeFields, 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#"use dagger_core::Input;
|
||||||
|
|
||||||
|
pub struct BuildArg {
|
||||||
|
pub name: Option<String>
|
||||||
|
|
||||||
|
pub value: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Input for BuildArg {}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let output = input.render(&t).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(output.to_file_string().unwrap(), expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const something: &str = r#"
|
||||||
|
{
|
||||||
|
"description": "",
|
||||||
|
"enumValues": null,
|
||||||
|
"fields": null,
|
||||||
|
"inputFields": [
|
||||||
|
{
|
||||||
|
"defaultValue": null,
|
||||||
|
"description": "",
|
||||||
|
"name": "name",
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defaultValue": null,
|
||||||
|
"description": "",
|
||||||
|
"name": "value",
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interfaces": null,
|
||||||
|
"kind": "INPUT_OBJECT",
|
||||||
|
"name": "BuildArg",
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
|
|
||||||
|
"#;
|
@ -1,4 +1,5 @@
|
|||||||
pub mod enumeration;
|
pub mod enumeration;
|
||||||
|
pub mod input;
|
||||||
pub mod scalar;
|
pub mod scalar;
|
||||||
mod utility;
|
mod utility;
|
||||||
|
|
||||||
@ -76,8 +77,12 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res.to_string().unwrap(),
|
res.to_string().unwrap(),
|
||||||
"pub struct SomeName {} { }
|
"pub struct SomeName {} {
|
||||||
impl SomeName {} { }"
|
|
||||||
|
}
|
||||||
|
impl SomeName {} {
|
||||||
|
|
||||||
|
}"
|
||||||
.to_string()
|
.to_string()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,33 @@ mod tests {
|
|||||||
fn can_generate_from_schema() {
|
fn can_generate_from_schema() {
|
||||||
let schema: IntrospectionResponse = serde_json::from_str(INTROSPECTION_QUERY).unwrap();
|
let schema: IntrospectionResponse = serde_json::from_str(INTROSPECTION_QUERY).unwrap();
|
||||||
let code = CodeGeneration::new().generate(&schema).unwrap();
|
let code = CodeGeneration::new().generate(&schema).unwrap();
|
||||||
assert_eq!("some-code", code);
|
let expected = "use dagger_core::Scalar;
|
||||||
|
|
||||||
|
// code generated by dagger. DO NOT EDIT.
|
||||||
|
|
||||||
|
/// A global cache volume identifier.
|
||||||
|
pub struct CacheID(Scalar);
|
||||||
|
|
||||||
|
/// A content-addressed directory identifier.
|
||||||
|
pub struct DirectoryID(Scalar);
|
||||||
|
|
||||||
|
/// A file identifier.
|
||||||
|
pub struct FileID(Scalar);
|
||||||
|
|
||||||
|
/// A unique identifier for a secret.
|
||||||
|
pub struct SecretID(Scalar);
|
||||||
|
|
||||||
|
/// The platform config OS and architecture in a Container.
|
||||||
|
/// The format is [os]/[platform]/[version] (e.g. darwin/arm64/v7, windows/amd64, linux/arm64).
|
||||||
|
pub struct Platform(Scalar);
|
||||||
|
|
||||||
|
/// A unique container identifier. Null designates an empty container (scratch).
|
||||||
|
pub struct ContainerID(Scalar);
|
||||||
|
|
||||||
|
/// A content-addressed socket identifier.
|
||||||
|
pub struct SocketID(Scalar);
|
||||||
|
";
|
||||||
|
assert_eq!(expected, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
const INTROSPECTION_QUERY: &str = r#"{
|
const INTROSPECTION_QUERY: &str = r#"{
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use graphql_introspection_query::introspection_response::{self, FullType};
|
use graphql_introspection_query::introspection_response::{
|
||||||
|
self, FullType, FullTypeInputFields, TypeRef, __TypeKind,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::models::Scalars;
|
use crate::models::Scalars;
|
||||||
|
|
||||||
@ -9,6 +11,13 @@ pub fn is_scalar_type(t: &FullType) -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_scalar_type_ref(t: &TypeRef) -> bool {
|
||||||
|
if let Some(introspection_response::__TypeKind::SCALAR) = t.kind {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_enum_type(t: &FullType) -> bool {
|
pub fn is_enum_type(t: &FullType) -> bool {
|
||||||
if let Some(introspection_response::__TypeKind::ENUM) = t.kind {
|
if let Some(introspection_response::__TypeKind::ENUM) = t.kind {
|
||||||
return true;
|
return true;
|
||||||
@ -16,6 +25,36 @@ pub fn is_enum_type(t: &FullType) -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_input_object_type(t: &FullType) -> bool {
|
||||||
|
if let Some(introspection_response::__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 false,
|
||||||
|
Some(_) => return true,
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_required_type_ref(t: &TypeRef) -> bool {
|
||||||
|
match t.kind {
|
||||||
|
Some(__TypeKind::NON_NULL) => return false,
|
||||||
|
Some(_) => return true,
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_list_type(t: &TypeRef) -> bool {
|
||||||
|
if let Some(introspection_response::__TypeKind::LIST) = t.kind {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_custom_scalar_type(t: &FullType) -> bool {
|
pub fn is_custom_scalar_type(t: &FullType) -> bool {
|
||||||
if is_scalar_type(t) {
|
if is_scalar_type(t) {
|
||||||
// TODO: Insert scalar
|
// TODO: Insert scalar
|
||||||
@ -35,3 +74,23 @@ pub fn is_custom_scalar_type(t: &FullType) -> bool {
|
|||||||
}
|
}
|
||||||
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
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user