use crate::api::RequestFailure;
use crate::icons::Icon;
use crate::node;
use crate::properties::{Property, PropertyFormat};
use serde::Serialize;
use serde_json::{Map, Value};
use std::fmt;
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub enum TypeLayout {
Basic,
Profile,
Action,
Note,
}
impl std::fmt::Display for TypeLayout {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let label = match self {
TypeLayout::Basic => "basic",
TypeLayout::Profile => "profile",
TypeLayout::Action => "action",
TypeLayout::Note => "note",
};
write!(f, "{}", label)
}
}
impl TypeLayout {
pub fn to_string(&self) -> String {
match self {
TypeLayout::Basic => "basic".to_string(),
TypeLayout::Profile => "profile".to_string(),
TypeLayout::Action => "action".to_string(),
TypeLayout::Note => "note".to_string(),
}
}
pub fn from_str(s: &str) -> Self {
match s {
"basic" => TypeLayout::Basic,
"profile" => TypeLayout::Profile,
"action" => TypeLayout::Action,
"note" => TypeLayout::Note,
_ => TypeLayout::Basic, }
}
}
#[derive(Clone, Debug)]
pub struct Type {
pub archived: bool,
pub icon: Option<Icon>,
pub id: String,
pub key: String,
pub name: String,
pub object: String,
pub layout: TypeLayout,
pub plural_name: String,
pub properties: Vec<Property>,
}
impl Type {
pub fn to_json(&self) -> Value {
let mut values = Map::new();
let icon = Value::Null;
if self.icon.is_some() {
self.icon.clone().unwrap().to_json();
}
values.insert("archived".to_string(), Value::Bool(self.archived));
values.insert("icon".to_string(), icon);
values.insert("id".to_string(), Value::String(self.id.clone()));
values.insert("key".to_string(), Value::String(self.key.clone()));
values.insert("name".to_string(), Value::String(self.name.clone()));
values.insert("object".to_string(), Value::String(self.object.clone()));
values.insert("layout".to_string(), Value::String(self.layout.to_string()));
values.insert(
"plural_name".to_string(),
Value::String(self.plural_name.clone()),
);
let mut properties: Vec<Value> = Vec::new();
for p in self.properties.iter() {
properties.push(p.to_json())
}
values.insert("properties".to_string(), Value::Array(properties));
Value::Object(values)
}
pub(crate) fn from_json(json: Value) -> Type {
let archived = json["archived"].as_bool().unwrap();
let icon = Icon::from_json(json["icon"].clone());
let id = json["id"].as_str().unwrap().to_string();
let key = json["key"].as_str().unwrap().to_string();
let layout = json["layout"].as_str().unwrap().to_string();
let name = json["name"].as_str().unwrap().to_string();
let object = json["object"].as_str().unwrap().to_string();
let plural_name = json["plural_name"].as_str().unwrap().to_string();
let mut properties: Vec<Property> = Vec::new();
match json["properties"].as_array() {
Some(property_values) => {
for p in 0..property_values.len() {
let property = Property::from_json(property_values[p].clone());
properties.push(property);
}
}
None => {}
}
Type {
archived,
icon,
id,
key,
name,
object,
layout: TypeLayout::from_str(&layout),
plural_name,
properties,
}
}
pub(crate) async fn from_response(response: reqwest::Response) -> Self {
let json_input = response.text().await.unwrap();
let json: serde_json::Value = serde_json::from_str(json_input.as_ref()).unwrap();
let t = json["type"].clone();
Type::from_json(t)
}
}
#[derive(Debug)]
pub struct ListOfTypes {
pub types: Vec<Type>,
pub has_more: bool,
pub next_offset: usize,
pub offset: usize,
pub limit: usize,
pub total: usize,
}
#[derive(Debug)]
pub struct CreateTypeRequest<'a> {
api_key: &'a str,
icon: Value,
key: &'a str,
layout: TypeLayout,
name: &'a str,
plural_name: &'a str,
properties: Vec<Value>,
server: &'a str,
space_id: &'a str,
}
impl<'a> CreateTypeRequest<'a> {
pub fn new(api_key: &'a str, server: &'a str) -> Self {
CreateTypeRequest {
api_key,
icon: Value::Null,
key: "",
layout: TypeLayout::Basic,
name: "",
plural_name: "",
properties: Vec::new(),
server,
space_id: "",
}
}
pub fn icon(mut self, icon: &Icon) -> Self {
self.icon = icon.to_json();
self
}
pub fn layout(mut self, layout: TypeLayout) -> Self {
self.layout = layout;
self
}
pub fn key(mut self, key: &'a str) -> Self {
self.key = key;
self
}
pub fn name(mut self, name: &'a str) -> Self {
self.name = name;
self
}
pub fn plural_name(mut self, plural_name: &'a str) -> Self {
self.plural_name = plural_name;
self
}
pub fn property(mut self, format: PropertyFormat, key: &str, name: &str) -> Self {
let mut map = Map::new();
map.insert("format".to_string(), Value::String(format.to_string()));
map.insert("key".to_string(), Value::String(key.to_string()));
map.insert("name".to_string(), Value::String(name.to_string()));
self.properties.push(Value::Object(map));
self
}
pub fn space_id(mut self, space_id: &'a str) -> Self {
self.space_id = space_id;
self
}
fn body(&self) -> String {
let mut values = Map::new();
values.insert("icon".to_string(), self.icon.clone());
values.insert("key".to_string(), Value::String(self.key.to_string()));
values.insert("name".to_string(), Value::String(self.name.to_string()));
values.insert("layout".to_string(), Value::String(self.layout.to_string()));
values.insert(
"plural_name".to_string(),
Value::String(self.plural_name.to_string()),
);
let mut properties: Vec<Value> = Vec::new();
for p in self.properties.iter() {
properties.push(p.clone());
}
values.insert("properties".to_string(), Value::Array(properties));
Value::Object(values).to_string()
}
pub async fn send(&self) -> Result<Type, RequestFailure> {
let endpoint = format!("/v1/spaces/{}/types", self.space_id,);
let body = self.body();
match node::post(self.api_key, self.server, &endpoint, &body).await {
Ok(response) => {
if response.status() == http::StatusCode::CREATED {
return Ok(Type::from_response(response).await);
} else {
let possible_status: Vec<http::StatusCode> = Vec::from([
http::StatusCode::UNAUTHORIZED,
http::StatusCode::BAD_REQUEST,
http::StatusCode::TOO_MANY_REQUESTS,
http::StatusCode::INTERNAL_SERVER_ERROR,
]);
return Err(RequestFailure::api_error(response, possible_status).await);
}
}
Err(e) => Err(RequestFailure::reqwest_error(e)),
}
}
}
#[derive(Debug)]
pub struct DeleteTypeRequest<'a> {
api_key: &'a str,
server: &'a str,
space_id: &'a str,
type_id: &'a str,
}
impl<'a> DeleteTypeRequest<'a> {
pub fn new(api_key: &'a str, server: &'a str) -> Self {
DeleteTypeRequest {
api_key,
server,
space_id: "",
type_id: "",
}
}
pub fn type_id(mut self, type_id: &'a str) -> Self {
self.type_id = type_id;
self
}
pub fn space_id(mut self, space_id: &'a str) -> Self {
self.space_id = space_id;
self
}
pub async fn send(&self) -> Result<Type, RequestFailure> {
let endpoint = format!("/v1/spaces/{}/types/{}", self.space_id, self.type_id);
match node::delete(self.api_key, self.server, &endpoint).await {
Ok(response) => {
if response.status() == http::StatusCode::OK {
return Ok(Type::from_response(response).await);
} else {
let possible_status: Vec<http::StatusCode> = Vec::from([
http::StatusCode::UNAUTHORIZED,
http::StatusCode::FORBIDDEN,
http::StatusCode::NOT_FOUND,
http::StatusCode::GONE,
http::StatusCode::TOO_MANY_REQUESTS,
http::StatusCode::INTERNAL_SERVER_ERROR,
]);
return Err(RequestFailure::api_error(response, possible_status).await);
}
}
Err(e) => Err(RequestFailure::reqwest_error(e)),
}
}
}
#[derive(Debug)]
pub struct GetTypeRequest<'a> {
api_key: &'a str,
server: &'a str,
space_id: &'a str,
type_id: &'a str,
}
impl<'a> GetTypeRequest<'a> {
pub fn new(api_key: &'a str, server: &'a str) -> Self {
GetTypeRequest {
api_key,
server,
space_id: "",
type_id: "",
}
}
pub fn type_id(mut self, type_id: &'a str) -> Self {
self.type_id = type_id;
self
}
pub fn space_id(mut self, space_id: &'a str) -> Self {
self.space_id = space_id;
self
}
pub async fn send(&self) -> Result<Type, RequestFailure> {
let endpoint = format!("/v1/spaces/{}/types/{}", self.space_id, self.type_id);
match node::get(self.api_key, self.server, &endpoint).await {
Ok(response) => {
if response.status() == http::StatusCode::OK {
return Ok(Type::from_response(response).await);
} else {
let possible_status: Vec<http::StatusCode> = Vec::from([
http::StatusCode::UNAUTHORIZED,
http::StatusCode::NOT_FOUND,
http::StatusCode::GONE,
http::StatusCode::INTERNAL_SERVER_ERROR,
]);
return Err(RequestFailure::api_error(response, possible_status).await);
}
}
Err(e) => Err(RequestFailure::reqwest_error(e)),
}
}
}
#[derive(Debug)]
pub struct UpdateTypeRequest<'a> {
api_key: &'a str,
icon: Value,
key: &'a str,
layout: TypeLayout,
name: &'a str,
plural_name: &'a str,
properties: Vec<Value>,
server: &'a str,
space_id: &'a str,
type_id: &'a str,
}
impl<'a> UpdateTypeRequest<'a> {
pub fn new(api_key: &'a str, server: &'a str) -> Self {
UpdateTypeRequest {
api_key,
icon: Value::Null,
key: "",
layout: TypeLayout::Basic,
name: "",
plural_name: "",
properties: Vec::new(),
server,
space_id: "",
type_id: "",
}
}
pub fn icon(mut self, icon: &Icon) -> Self {
self.icon = icon.to_json();
self
}
pub fn layout(mut self, layout: TypeLayout) -> Self {
self.layout = layout;
self
}
pub fn key(mut self, key: &'a str) -> Self {
self.key = key;
self
}
pub fn name(mut self, name: &'a str) -> Self {
self.name = name;
self
}
pub fn plural_name(mut self, plural_name: &'a str) -> Self {
self.plural_name = plural_name;
self
}
pub fn property(mut self, format: PropertyFormat, key: &str, name: &str) -> Self {
let mut map = Map::new();
map.insert("format".to_string(), Value::String(format.to_string()));
map.insert("key".to_string(), Value::String(key.to_string()));
map.insert("name".to_string(), Value::String(name.to_string()));
self.properties.push(Value::Object(map));
self
}
pub fn space_id(mut self, space_id: &'a str) -> Self {
self.space_id = space_id;
self
}
pub fn type_id(mut self, type_id: &'a str) -> Self {
self.type_id = type_id;
self
}
fn body(&self) -> String {
let mut values = Map::new();
values.insert("icon".to_string(), self.icon.clone());
values.insert("key".to_string(), Value::String(self.key.to_string()));
values.insert("name".to_string(), Value::String(self.name.to_string()));
values.insert("layout".to_string(), Value::String(self.layout.to_string()));
values.insert(
"plural_name".to_string(),
Value::String(self.plural_name.to_string()),
);
let mut properties: Vec<Value> = Vec::new();
for p in self.properties.iter() {
properties.push(p.clone());
}
values.insert("properties".to_string(), Value::Array(properties));
Value::Object(values).to_string()
}
pub async fn send(&self) -> Result<Type, RequestFailure> {
let endpoint = format!("/v1/spaces/{}/types/{}", self.space_id, self.type_id);
let body = self.body();
match node::patch(self.api_key, self.server, &endpoint, &body).await {
Ok(response) => {
if response.status() == http::StatusCode::OK {
return Ok(Type::from_response(response).await);
} else {
let possible_status: Vec<http::StatusCode> = Vec::from([
http::StatusCode::UNAUTHORIZED,
http::StatusCode::BAD_REQUEST,
http::StatusCode::FORBIDDEN,
http::StatusCode::NOT_FOUND,
http::StatusCode::GONE,
http::StatusCode::TOO_MANY_REQUESTS,
http::StatusCode::INTERNAL_SERVER_ERROR,
]);
return Err(RequestFailure::api_error(response, possible_status).await);
}
}
Err(e) => Err(RequestFailure::reqwest_error(e)),
}
}
}
#[derive(Debug)]
pub struct ListTypesRequest<'a> {
api_key: &'a str,
offset: u32,
limit: u32,
server: &'a str,
space_id: &'a str,
}
impl<'a> ListTypesRequest<'a> {
pub fn new(api_key: &'a str, server: &'a str) -> Self {
ListTypesRequest {
api_key,
offset: 0,
limit: 100,
server,
space_id: "",
}
}
pub fn offset(mut self, offset: u32) -> Self {
self.offset = offset;
self
}
pub fn limit(mut self, limit: u32) -> Self {
self.limit = limit;
self
}
pub fn space_id(mut self, space_id: &'a str) -> Self {
self.space_id = space_id;
self
}
pub async fn send(&self) -> Result<ListOfTypes, RequestFailure> {
let endpoint = format!(
"/v1/spaces/{}/types?offset={}&limit={}",
self.space_id, self.offset, self.limit
);
match node::get(self.api_key, self.server, &endpoint).await {
Ok(response) => {
if response.status() == http::StatusCode::OK {
return Ok(self.response_to_list(response).await);
} else {
let possible_status: Vec<http::StatusCode> = Vec::from([
http::StatusCode::UNAUTHORIZED,
http::StatusCode::INTERNAL_SERVER_ERROR,
]);
return Err(RequestFailure::api_error(response, possible_status).await);
}
}
Err(e) => Err(RequestFailure::reqwest_error(e)),
}
}
async fn response_to_list(&self, response: reqwest::Response) -> ListOfTypes {
let json_input = response.text().await.unwrap();
let json: serde_json::Value = serde_json::from_str(json_input.as_ref()).unwrap();
let received = json["data"].as_array().unwrap();
let mut data: Vec<Type> = Vec::new();
for t in 0..received.len() {
let type_obj = Type::from_json(received[t].clone());
data.push(type_obj);
}
let has_more: bool = json["pagination"]["has_more"].as_bool().unwrap();
let total = json["pagination"]["total"].as_u64().unwrap() as usize;
let offset = json["pagination"]["offset"].as_u64().unwrap() as usize;
let limit = json["pagination"]["limit"].as_u64().unwrap() as usize;
let next_offset = (offset as usize) + received.len();
ListOfTypes {
types: data,
has_more,
next_offset,
offset,
limit,
total,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
pub fn test_list_types_request() {
let request = ListTypesRequest::new("secret_api_key", "server_url")
.offset(0)
.space_id("lost_in_space")
.limit(100);
let internals = format!("{:#?}", request);
assert!(internals.contains("api_key: \"secret_api_key\""));
assert!(internals.contains("offset: 0"));
assert!(internals.contains("limit: 100"));
assert!(internals.contains("space_id: \"lost_in_space\""));
assert!(internals.contains("server: \"server_url\""));
println!("{}", internals);
}
}