Use cargo workspaces
This commit is contained in:
250
src/lib.rs
250
src/lib.rs
@@ -1,52 +1,202 @@
|
||||
use actix_web::{dev::Server, http, middleware, web, App, HttpServer};
|
||||
use listenfd::ListenFd;
|
||||
use std::env;
|
||||
use std::net::TcpListener;
|
||||
use tracing_actix_web::TracingLogger;
|
||||
use chrono::{DateTime, Utc};
|
||||
use http::uri::Uri;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
pub mod configuration;
|
||||
pub mod contextswitch;
|
||||
pub mod observability;
|
||||
pub mod routes;
|
||||
|
||||
pub fn run(listener: TcpListener) -> Result<Server, std::io::Error> {
|
||||
let cs_front_base_url =
|
||||
env::var("CS_FRONT_BASE_URL").unwrap_or_else(|_| "http://localhost:8080".to_string());
|
||||
let mut server = HttpServer::new(move || {
|
||||
App::new()
|
||||
.wrap(TracingLogger::default())
|
||||
.wrap(middleware::Compress::default())
|
||||
.wrap(
|
||||
middleware::DefaultHeaders::new()
|
||||
.add(("Access-Control-Allow-Origin", cs_front_base_url.as_bytes()))
|
||||
.add((
|
||||
"Access-Control-Allow-Methods",
|
||||
"POST, GET, OPTIONS".as_bytes(),
|
||||
))
|
||||
.add(("Access-Control-Allow-Headers", "content-type".as_bytes())),
|
||||
)
|
||||
.route("/ping", web::get().to(routes::ping))
|
||||
.route("/tasks", web::get().to(routes::list_tasks))
|
||||
.route("/tasks", web::post().to(routes::add_task))
|
||||
.route("/tasks/{task_id}", web::put().to(routes::update_task))
|
||||
.route(
|
||||
"/tasks",
|
||||
web::method(http::Method::OPTIONS).to(routes::option_task),
|
||||
)
|
||||
})
|
||||
.keep_alive(60)
|
||||
.shutdown_timeout(60);
|
||||
|
||||
let mut listenfd = ListenFd::from_env();
|
||||
|
||||
server = if let Some(fdlistener) = listenfd.take_tcp_listener(0)? {
|
||||
server.listen(fdlistener)?
|
||||
} else {
|
||||
server.listen(listener)?
|
||||
};
|
||||
|
||||
Ok(server.run())
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Recurrence {
|
||||
Daily,
|
||||
Weekly,
|
||||
Monthly,
|
||||
Yearly,
|
||||
}
|
||||
|
||||
impl fmt::Display for Recurrence {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", format!("{:?}", self).to_lowercase())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Status {
|
||||
Pending,
|
||||
Completed,
|
||||
Recurring,
|
||||
Deleted,
|
||||
}
|
||||
|
||||
impl fmt::Display for Status {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", format!("{:?}", self).to_lowercase())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy, Eq)]
|
||||
pub enum Priority {
|
||||
H,
|
||||
M,
|
||||
L,
|
||||
}
|
||||
|
||||
impl fmt::Display for Priority {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Eq)]
|
||||
pub struct Bookmark {
|
||||
#[serde(with = "uri")]
|
||||
pub uri: Uri,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub content: Option<BookmarkContent>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Eq)]
|
||||
pub struct BookmarkContent {
|
||||
pub title: String,
|
||||
pub content_preview: Option<String>,
|
||||
}
|
||||
|
||||
pub mod uri {
|
||||
use http::uri::Uri;
|
||||
use serde::{self, Deserialize, Deserializer, Serializer};
|
||||
|
||||
pub fn serialize<S>(uri: &Uri, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let uri_str = uri.to_string();
|
||||
serializer.serialize_str(&uri_str)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Uri, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
s.parse::<Uri>().map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Eq)]
|
||||
pub struct ContextswitchData {
|
||||
pub bookmarks: Vec<Bookmark>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Eq)]
|
||||
pub struct TaskId(pub Uuid);
|
||||
|
||||
impl fmt::Display for TaskId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
pub struct Task {
|
||||
pub id: TaskId,
|
||||
#[serde(with = "tw_date_format")]
|
||||
pub entry: DateTime<Utc>,
|
||||
#[serde(with = "tw_date_format")]
|
||||
pub modified: DateTime<Utc>,
|
||||
pub status: Status,
|
||||
pub description: String,
|
||||
pub urgency: f64,
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
with = "opt_tw_date_format"
|
||||
)]
|
||||
pub due: Option<DateTime<Utc>>,
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
with = "opt_tw_date_format"
|
||||
)]
|
||||
pub start: Option<DateTime<Utc>>,
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
with = "opt_tw_date_format"
|
||||
)]
|
||||
pub end: Option<DateTime<Utc>>,
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
with = "opt_tw_date_format"
|
||||
)]
|
||||
pub wait: Option<DateTime<Utc>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub parent: Option<TaskId>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub project: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub priority: Option<Priority>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub recur: Option<Recurrence>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub tags: Option<Vec<String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub contextswitch: Option<ContextswitchData>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct NewTask {
|
||||
pub definition: String,
|
||||
}
|
||||
|
||||
pub mod tw_date_format {
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use serde::{self, Deserialize, Deserializer, Serializer};
|
||||
|
||||
const FORMAT: &str = "%Y%m%dT%H%M%SZ";
|
||||
|
||||
pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let s = format!("{}", date.format(FORMAT));
|
||||
serializer.serialize_str(&s)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
Utc.datetime_from_str(&s, FORMAT)
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod opt_tw_date_format {
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use serde::{self, Deserialize, Deserializer, Serializer};
|
||||
|
||||
const FORMAT: &str = "%Y%m%dT%H%M%SZ";
|
||||
|
||||
pub fn serialize<S>(date: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
if let Some(ref d) = *date {
|
||||
return serializer.serialize_str(&d.format(FORMAT).to_string());
|
||||
}
|
||||
|
||||
serializer.serialize_none()
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<DateTime<Utc>>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
Utc.datetime_from_str(&s, FORMAT)
|
||||
.map(Some)
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user