use bytes::Bytes;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::http::HttpClient;
use crate::models::{CostEstimate, Document, Template};
use crate::pagination::Page;
#[derive(Debug)]
pub struct ListTemplatesRequest<'a> {
http: &'a HttpClient,
account_id: &'a str,
page: Option<u32>,
per_page: Option<u32>,
search: Option<String>,
sort: Option<String>,
status: Option<String>,
tags: Vec<String>,
}
impl<'a> ListTemplatesRequest<'a> {
pub fn page(mut self, page: u32) -> Self {
self.page = Some(page);
self
}
pub fn per_page(mut self, per_page: u32) -> Self {
self.per_page = Some(per_page);
self
}
pub fn search<S: Into<String>>(mut self, term: S) -> Self {
self.search = Some(term.into());
self
}
pub fn sort<S: Into<String>>(mut self, sort: S) -> Self {
self.sort = Some(sort.into());
self
}
pub fn status<S: Into<String>>(mut self, status: S) -> Self {
self.status = Some(status.into());
self
}
pub fn tags<I, S>(mut self, tags: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.tags = tags.into_iter().map(Into::into).collect();
self
}
pub async fn send(self) -> Result<Page<Template>> {
let path = format!("accounts/{}/templates", self.account_id);
let mut req = self.http.request(Method::GET, &path)?;
let mut q: Vec<(&str, String)> = Vec::new();
if let Some(v) = self.page {
q.push(("page", v.to_string()));
}
if let Some(v) = self.per_page {
q.push(("per-page", v.to_string()));
}
if let Some(v) = self.search {
q.push(("search", v));
}
if let Some(v) = self.sort {
q.push(("sort", v));
}
if let Some(v) = self.status {
q.push(("status", v));
}
if !self.tags.is_empty() {
q.push(("tags", self.tags.join(",")));
}
if !q.is_empty() {
req = req.query(&q);
}
self.http.send_paged(req).await
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemplateDocumentSigner {
pub role_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub full_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub whatsapp_phone_number: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub verification_method: Option<crate::models::VerificationMethod>,
#[serde(skip_serializing_if = "Option::is_none")]
pub notification_methods: Option<Vec<crate::models::NotificationMethod>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub step: Option<u32>,
}
impl TemplateDocumentSigner {
pub fn role<R: Into<String>>(role_id: R) -> Self {
Self {
role_id: role_id.into(),
id: None,
full_name: None,
email: None,
whatsapp_phone_number: None,
verification_method: None,
notification_methods: None,
step: None,
}
}
pub fn existing<R, S>(role_id: R, signer_id: S) -> Self
where
R: Into<String>,
S: Into<String>,
{
Self {
role_id: role_id.into(),
id: Some(signer_id.into()),
full_name: None,
email: None,
whatsapp_phone_number: None,
verification_method: None,
notification_methods: None,
step: None,
}
}
pub fn inline<R, N>(role_id: R, full_name: N) -> Self
where
R: Into<String>,
N: Into<String>,
{
Self {
role_id: role_id.into(),
id: None,
full_name: Some(full_name.into()),
email: None,
whatsapp_phone_number: None,
verification_method: None,
notification_methods: None,
step: None,
}
}
pub fn email<S: Into<String>>(mut self, email: S) -> Self {
self.email = Some(email.into());
self
}
pub fn whatsapp<S: Into<String>>(mut self, phone: S) -> Self {
self.whatsapp_phone_number = Some(phone.into());
self
}
pub fn verification_method(mut self, method: crate::models::VerificationMethod) -> Self {
self.verification_method = Some(method);
self
}
pub fn notification_methods(mut self, methods: Vec<crate::models::NotificationMethod>) -> Self {
self.notification_methods = Some(methods);
self
}
pub fn step(mut self, step: u32) -> Self {
self.step = Some(step);
self
}
}
pub type TemplateRoleBinding = TemplateDocumentSigner;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CreateDocumentFromTemplateBody {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expiration: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_at: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub signers: Vec<TemplateDocumentSigner>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub editor_fields: Vec<serde_json::Value>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub tags: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub tag_ids: Vec<String>,
}
impl CreateDocumentFromTemplateBody {
pub fn name<S: Into<String>>(mut self, name: S) -> Self {
self.name = Some(name.into());
self
}
pub fn message<S: Into<String>>(mut self, message: S) -> Self {
self.message = Some(message.into());
self
}
pub fn expiration<S: Into<String>>(mut self, expiration: S) -> Self {
self.expiration = Some(expiration.into());
self
}
pub fn expires_at<S: Into<String>>(mut self, expires_at: S) -> Self {
self.expires_at = Some(expires_at.into());
self
}
pub fn roles(mut self, bindings: Vec<TemplateRoleBinding>) -> Self {
self.signers = bindings;
self
}
pub fn signers(mut self, signers: Vec<TemplateDocumentSigner>) -> Self {
self.signers = signers;
self
}
pub fn editor_fields(mut self, fields: Vec<serde_json::Value>) -> Self {
self.editor_fields = fields;
self
}
pub fn tags<I, S>(mut self, tags: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.tags = tags.into_iter().map(Into::into).collect();
self
}
pub fn tag_ids<I, S>(mut self, tags: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.tag_ids = tags.into_iter().map(Into::into).collect();
self
}
}
pub type EstimateTemplateCostBody = CreateDocumentFromTemplateBody;
#[derive(Debug)]
pub struct TemplatesApi<'a> {
http: &'a HttpClient,
account_id: String,
}
impl<'a> TemplatesApi<'a> {
pub(crate) fn new(http: &'a HttpClient, account_id: String) -> Self {
Self { http, account_id }
}
pub fn list(&self) -> ListTemplatesRequest<'_> {
ListTemplatesRequest {
http: self.http,
account_id: &self.account_id,
page: None,
per_page: None,
search: None,
sort: None,
status: None,
tags: Vec::new(),
}
}
pub async fn get<S: AsRef<str>>(&self, template_id: S) -> Result<Template> {
let path = format!(
"accounts/{}/templates/{}",
self.account_id,
template_id.as_ref()
);
let req = self.http.request(Method::GET, &path)?;
self.http.send_envelope(req).await
}
pub async fn create<B>(&self, body: &B) -> Result<Template>
where
B: Serialize + ?Sized,
{
let path = format!("accounts/{}/templates", self.account_id);
let req = self.http.request(Method::POST, &path)?.json(body);
self.http.send_data(req).await
}
pub async fn update<S, B>(&self, template_id: S, body: &B) -> Result<Template>
where
S: AsRef<str>,
B: Serialize + ?Sized,
{
let path = format!(
"accounts/{}/templates/{}",
self.account_id,
template_id.as_ref()
);
let req = self.http.request(Method::PUT, &path)?.json(body);
self.http.send_data(req).await
}
pub async fn download_page<T: AsRef<str>, P: AsRef<str>>(
&self,
template_id: T,
page_id: P,
) -> Result<(Bytes, String)> {
let path = format!(
"accounts/{}/templates/{}/pages/{}/download",
self.account_id,
template_id.as_ref(),
page_id.as_ref()
);
let req = self.http.request(Method::GET, &path)?;
let (bytes, headers) = self.http.send_bytes(req).await?;
let content_type = headers
.get(reqwest::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream")
.to_owned();
Ok((bytes, content_type))
}
pub async fn create_document<S: AsRef<str>>(
&self,
template_id: S,
body: &CreateDocumentFromTemplateBody,
) -> Result<Document> {
let path = format!(
"accounts/{}/templates/{}/documents",
self.account_id,
template_id.as_ref()
);
let req = self.http.request(Method::POST, &path)?.json(body);
self.http.send_data(req).await
}
pub async fn estimate_cost<S: AsRef<str>>(
&self,
template_id: S,
body: &EstimateTemplateCostBody,
) -> Result<CostEstimate> {
let path = format!(
"accounts/{}/templates/{}/documents/estimate-cost",
self.account_id,
template_id.as_ref()
);
let req = self.http.request(Method::POST, &path)?.json(body);
self.http.send_envelope(req).await
}
}