# Constructs
Having a way to modularize our deployment code can decrease code complexity and reduce code duplication. `tf-bindgen` will utilize constructs similar to [Terraform CDK Constructs](https://developer.hashicorp.com/terraform/cdktf/concepts/constructs) and will use the `Construct` derive macro to implement the required traits.
In this section, we will create a construct to deploy a nginx pod to Kubernetes. To create our construct, we will start with creating a struct and adding the `Construct` derive macro:
```rust
use tf_bindgen::codegen::Construct;
use tf_bindgen::Scope;
#[derive(Construct)]
pub struct Nginx {
#[construct(id)]
name: String,
#[construct(scope)]
scope: Rc<dyn Scope>
}
```
In addition, we added two fields to our struct: `name` and `scope`. Both fields are necessary and we have to add the `#[construct(id)]` and `#[construct(scope)]` annotation to these fields.
Using this declaration, our derive macro will do nothing more than implementing the `Scope` trait for our `Nginx` struct. This trait is necessary to use this struct instead of stack in resources and data sources. <!-- TODO: link to Scope section -->
## Generating a Builder
In most cases, it will be necessary to pass extra information to our construct. So we need a way to add and set parameters to our construct. We can use the `builder` option of the `Construct` derive macro by adding `#[construct(builder)]` annotation to our struct. In addition, we will add to fields `namespace` and `image` to our struct:
```rust
use tf_bindgen::codegen::{Construct, resource};
use tf_bindgen::Scope;
#[derive(Construct)]
#[construct(builder)]
pub struct Nginx {
#[construct(id)]
name: String,
#[construct(scope)]
scope: Rc<dyn Scope>,
#[construct(setter(into_value))]
namespace: Value<String>,
#[construct(setter(into_value))]
image: Value<String>,
}
```
This code snippet allows creating an Nginx construct using the following builder:
```rust
Nginx::create(scope, "<name>")
.namespace("default")
.image("nginx")
.build();
```
Note that we have to implement the build function ourselves. But before we will implement this function, we must consider different setter options:
- `#[construct(setter)]` or no annotation: You can this to generate setters taking the same type as the field as input.
- `#[construct(setter(into))]` This annotation is used to generate setters taking `Into<T>` where T is the type of the field as an argument.
- `#[construct(setter(into_value))]` This annotation is used to generate setters taking `IntoValue<T>` where T is the type of the field as an argument. In addition, this field must be of type `Value<T>`.
- `#[construct(setter(into_value_list))]` This annotation is used to generate setters taking objects implementing `IntoValueList<T>` as an argument. In addition, this field must be of type `Vec<Value<T>>`.
- `#[construct(setter(into_value_set))]` This annotation is used to generate setters taking objects implementing `IntoValueSet<T>` as an argument. In addition, this field must be of type `HashSet<Value<T>>`.
- `#[construct(setter(into_value_map)))]` This annotation is used to generate setters taking objects implementing `IntoValueMap<T>` as an argument. In addition, this field must be of type `HashMap<String, Value<T>>`.
In general, it is recommended to use `Value` wrapped types to ensure better compatibility with `tf-bindgen`. It also allows using references as a Value. <!-- TODO: Link to section about values -->
## Creating Resources
Finally, we want to create our resources. We will create the already mentioned `build` function for that. It is important to note, that we will not implement the function for `Nginx` but rather for `NginxBuilder`, a type generated by our `Construct` derive macro.
To implement our build function, we will start with creating our construct type. For that we will need to clone our `name` and `scope` field. Because every other field will be wrapped inside an `Option`-type, we will need to clone and unwrap them (in our case, we will use expect instead). In addition, it is essential to wrap our type inside a reference counter `Rc`, because it is required to use a construct as a scope.
After we created our construct, we can use it to create our resources. The following example, will show an implementation for a nginx container inside a Kubernetes pod:
```rust
impl NginxBuilder {
pub fn build(&mut self) -> Rc<Postgres> {
let this = Rc::new(Postgres {
name: self.name.clone(),
scope: self.scope.clone(),
namespace: self.namespace.clone().expect("missing field 'namespace'"),
image: self.image.clone().expect("missing field 'image'")
});
let name = &this.name;
tf_bindgen::codegen::resource! {
&this, resource "kubernetes_pod" "nginx" {
metadata {
namespace = &this.namespace
name = format!("nginx-{name}")
}
spec {
container {
name = "nginx"
image = &this.image
port {
container_port = 80
}
}
}
}
};
this
}
}
```
## Outputs
TODO