From 2bb4858eeb02caceb745ba0c305a9f3071ef000b Mon Sep 17 00:00:00 2001
From: theMackabu <theMackabu@gmail.com>
Date: Mon, 25 Nov 2024 18:07:42 -0800
Subject: [PATCH] remove more redundant code, simplify models

---
 maid/client/cli/dispatch.rs     |  4 +-
 maid/client/cli/mod.rs          | 10 +++-
 maid/client/cli/run.rs          | 20 +++++---
 maid/client/main.rs             |  7 ++-
 maid/client/parse/file.rs       |  9 ++--
 maid/client/parse/import.rs     |  7 +--
 maid/client/parse/mod.rs        |  5 +-
 maid/client/server/cli.rs       | 15 ++++--
 maid/client/server/parse.rs     | 19 +++----
 maid/server/docker/run.rs       |  4 +-
 maid/server/main.rs             |  2 -
 maid/server/structs.rs          | 90 ---------------------------------
 maid/server/table.rs            | 62 -----------------------
 maid/shared/helpers/maidfile.rs |  4 +-
 maid/shared/models/client.rs    | 84 +++---------------------------
 maid/shared/models/server.rs    | 79 ++---------------------------
 maid/shared/models/shared.rs    | 69 +++++++++++++++++++++++++
 maid/shared/table.rs            |  7 ++-
 18 files changed, 148 insertions(+), 349 deletions(-)
 delete mode 100644 maid/server/structs.rs
 delete mode 100644 maid/server/table.rs

diff --git a/maid/client/cli/dispatch.rs b/maid/client/cli/dispatch.rs
index 294d5d3..4dbcc26 100644
--- a/maid/client/cli/dispatch.rs
+++ b/maid/client/cli/dispatch.rs
@@ -21,7 +21,7 @@ pub(crate) fn watch(path: &Path) {
     }
 }
 
-pub(crate) fn update() {
+pub(crate) fn check_update() {
     let checker = match blocking::get("https://api.maid.ci/versions/latest") {
         Ok(res) => res.json::<UpdateData>(),
         Err(err) => error!(%err, "Unable to check for updates"),
@@ -35,7 +35,7 @@ pub(crate) fn update() {
     if version == env!("CARGO_PKG_VERSION") {
         info!("Maid is currently on the latest version")
     } else {
-        warn!("Your install is currently out of date.\n\nThe current version is {version}\nPlease update from https://maid.ci/install")
+        warn!("Your install is currently out of date.\n\nThe current version is {version}\nPlease update using `maid upgrade`")
     }
 }
 
diff --git a/maid/client/cli/mod.rs b/maid/client/cli/mod.rs
index 2714564..ed439b4 100644
--- a/maid/client/cli/mod.rs
+++ b/maid/client/cli/mod.rs
@@ -6,8 +6,14 @@ use crate::parse;
 use crate::server;
 use crate::task;
 
-use maid::models::client::{Cache, CacheConfig, Project, Task};
-use maid::{helpers, log::prelude::*};
+use maid::{
+    helpers,
+    log::prelude::*,
+    models::{
+        client::{CacheConfig, Task},
+        shared::{Cache, Project},
+    },
+};
 
 use fs_extra::dir::get_size;
 use global_placeholders::global;
diff --git a/maid/client/cli/run.rs b/maid/client/cli/run.rs
index dfde1a8..c7e7482 100644
--- a/maid/client/cli/run.rs
+++ b/maid/client/cli/run.rs
@@ -1,20 +1,26 @@
-use maid::models::client::{Cache, Runner};
-use maid::{helpers, log::prelude::*, table};
-
-use crate::cli;
 use crate::shell::IntoArgs;
 
+use maid::{
+    helpers,
+    log::prelude::*,
+    models::{
+        client::{Runner, Task},
+        shared::Cache,
+    },
+    table,
+};
+
 use fs_extra::dir::get_size;
 use human_bytes::human_bytes;
-
 use std::env;
 use std::io::Error;
 use std::path::Path;
 use std::process::{Child, Command, ExitStatus, Stdio};
 use std::time::Instant;
 use text_placeholder::Template;
+use toml::Value;
 
-fn run_script(runner: Runner) {
+fn run_script(runner: Runner<Value>) {
     let mut cmd: Child;
     let start = Instant::now();
     let mut status_array: Vec<Result<ExitStatus, Error>> = vec![];
@@ -120,7 +126,7 @@ fn run_script(runner: Runner) {
     }
 }
 
-pub(crate) fn task(task: cli::Task) {
+pub(crate) fn task(task: Task<Value>) {
     let mut script: Vec<&str> = vec![];
 
     if task.script.is_str() {
diff --git a/maid/client/main.rs b/maid/client/main.rs
index 9dc2a2e..a173227 100644
--- a/maid/client/main.rs
+++ b/maid/client/main.rs
@@ -81,7 +81,9 @@ struct Cli {
 #[derive(ValueEnum, Clone)]
 enum System {
     /// Check for new Maid updates
-    Update,
+    CheckUpdates,
+    /// Upgrade Maid to the latest version
+    Upgrade,
     /// Return the Maidfile in json
     Json,
     /// Hydrate json with environment fields
@@ -130,7 +132,8 @@ fn main() {
 
     if let Some(system) = cli.system {
         return match system {
-            System::Update => cli::dispatch::update(),
+            System::CheckUpdates => cli::dispatch::check_update(),
+            System::Upgrade => cli::dispatch::check_update(),
             System::Json => cli::tasks::list_json(&cli.path, &cli.task, false),
             System::JsonHydrated => cli::tasks::list_json(&cli.path, &cli.task, true),
         };
diff --git a/maid/client/parse/file.rs b/maid/client/parse/file.rs
index 7587509..6b459ae 100644
--- a/maid/client/parse/file.rs
+++ b/maid/client/parse/file.rs
@@ -1,8 +1,9 @@
 use maid::log::prelude::*;
-use maid::models::client::Maidfile;
+use maid::models::shared::Maidfile;
 
 use macros_rs::{exp::then, fmt::string};
 use std::{env, fs, io::Result, path::Path, path::PathBuf};
+use toml::Value;
 
 macro_rules! create_path {
     ($file_name:expr, $kind:expr) => {{
@@ -75,7 +76,7 @@ fn find_file(starting_directory: &Path, file_name: &String) -> Option<PathBuf> {
     return None;
 }
 
-fn read_file(path: PathBuf, kind: &str) -> Maidfile {
+fn read_file(path: PathBuf, kind: &str) -> Maidfile<Value> {
     let contents = match fs::read_to_string(&path) {
         Ok(contents) => contents,
         Err(_) => error!("Cannot find Maidfile. Does it exist?"),
@@ -95,7 +96,7 @@ fn read_file(path: PathBuf, kind: &str) -> Maidfile {
     }
 }
 
-pub fn read_maidfile_with_error(filename: &String, error: &str) -> Maidfile {
+pub fn read_maidfile_with_error(filename: &String, error: &str) -> Maidfile<Value> {
     match env::current_dir() {
         Ok(path) => match find_file(&path, &filename) {
             Some(path) => {
@@ -127,4 +128,4 @@ pub fn find_maidfile_root(filename: &String) -> PathBuf {
     }
 }
 
-pub fn read_maidfile(filename: &String) -> Maidfile { read_maidfile_with_error(filename, "Cannot find maidfile. Does it exist?") }
+pub fn read_maidfile(filename: &String) -> Maidfile<Value> { read_maidfile_with_error(filename, "Cannot find maidfile. Does it exist?") }
diff --git a/maid/client/parse/import.rs b/maid/client/parse/import.rs
index 0b0f789..18e06b6 100644
--- a/maid/client/parse/import.rs
+++ b/maid/client/parse/import.rs
@@ -1,9 +1,10 @@
 use crate::parse;
 use macros_rs::fmt::fmtstr;
-use maid::models::client::Maidfile;
+use maid::models::shared::Maidfile;
+use toml::Value;
 
-pub fn push(path_list: Option<Vec<String>>) -> Vec<Maidfile> {
-    let mut values: Vec<Maidfile> = vec![];
+pub fn push(path_list: Option<Vec<String>>) -> Vec<Maidfile<Value>> {
+    let mut values: Vec<Maidfile<Value>> = vec![];
 
     let mut add_values = |paths: Vec<String>| {
         for path in paths.iter() {
diff --git a/maid/client/parse/mod.rs b/maid/client/parse/mod.rs
index 5cc7f0f..126f79e 100644
--- a/maid/client/parse/mod.rs
+++ b/maid/client/parse/mod.rs
@@ -2,9 +2,10 @@ pub mod file;
 pub mod import;
 
 use maid::log::prelude::*;
-use maid::models::client::Maidfile;
+use maid::models::shared::Maidfile;
+use toml::Value;
 
-pub(crate) fn merge(path: &String) -> Maidfile {
+pub(crate) fn merge(path: &String) -> Maidfile<Value> {
     let mut values = file::read_maidfile(path);
     let imported_values = import::push(values.import.clone());
 
diff --git a/maid/client/server/cli.rs b/maid/client/server/cli.rs
index c98c5b8..08e751d 100644
--- a/maid/client/server/cli.rs
+++ b/maid/client/server/cli.rs
@@ -1,15 +1,22 @@
 use crate::parse;
 use crate::server;
 
-use maid::models::client::{ConnectionData, ConnectionInfo, Kind, Level, Maidfile, Task, Websocket};
-use maid::{helpers, log::prelude::*};
+use maid::{
+    helpers,
+    log::prelude::*,
+    models::{
+        client::{ConnectionData, ConnectionInfo, Kind, Level, Task, Websocket},
+        shared::Maidfile,
+    },
+};
 
 use macros_rs::fmt::fmtstr;
 use reqwest::blocking::Client;
+use toml::Value;
 use tungstenite::protocol::frame::{coding::CloseCode::Normal, CloseFrame};
 use tungstenite::{client::connect_with_config, client::IntoClientRequest, protocol::WebSocketConfig, Message};
 
-fn health(client: Client, values: Maidfile) -> server::api::health::Route {
+fn health(client: Client, values: Maidfile<Value>) -> server::api::health::Route {
     let address = server::parse::address(&values);
     let token = server::parse::token(&values);
 
@@ -48,7 +55,7 @@ pub fn connect(path: &String) {
     );
 }
 
-pub fn remote(task: Task) {
+pub fn remote(task: Task<Value>) {
     let mut script: Vec<&str> = vec![];
 
     if task.script.is_str() {
diff --git a/maid/client/server/parse.rs b/maid/client/server/parse.rs
index 80b2e6b..ed76ea3 100644
--- a/maid/client/server/parse.rs
+++ b/maid/client/server/parse.rs
@@ -1,11 +1,12 @@
 use macros_rs::{exp::ternary, fmt::string};
-use maid::models::client::Maidfile;
+use maid::models::shared::Maidfile;
+use toml::Value;
 
-pub fn address(values: &Maidfile) -> String {
+pub fn address(values: &Maidfile<Value>) -> String {
     match &values.project {
         Some(project) => match &project.server {
             Some(server) => {
-                let prefix = ternary!(server.address.ssl, "https", "http");
+                let prefix = ternary!(server.address.tls, "https", "http");
                 format!("{}://{}:{}", prefix, server.address.host, server.address.port)
             }
             None => string!(""),
@@ -14,11 +15,11 @@ pub fn address(values: &Maidfile) -> String {
     }
 }
 
-pub fn websocket(values: &Maidfile) -> String {
+pub fn websocket(values: &Maidfile<Value>) -> String {
     match &values.project {
         Some(project) => match &project.server {
             Some(server) => {
-                let prefix = ternary!(server.address.ssl, "wss", "ws");
+                let prefix = ternary!(server.address.tls, "wss", "ws");
                 format!("{}://{}:{}/ws/gateway", prefix, server.address.host, server.address.port)
             }
             None => string!(""),
@@ -27,7 +28,7 @@ pub fn websocket(values: &Maidfile) -> String {
     }
 }
 
-pub fn host(values: &Maidfile) -> String {
+pub fn host(values: &Maidfile<Value>) -> String {
     match &values.project {
         Some(project) => match &project.server {
             Some(server) => server.address.host.clone(),
@@ -37,7 +38,7 @@ pub fn host(values: &Maidfile) -> String {
     }
 }
 
-pub fn port(values: &Maidfile) -> i64 {
+pub fn port(values: &Maidfile<Value>) -> i64 {
     match &values.project {
         Some(project) => match &project.server {
             Some(server) => server.address.port.clone(),
@@ -47,7 +48,7 @@ pub fn port(values: &Maidfile) -> i64 {
     }
 }
 
-pub fn token(values: &Maidfile) -> String {
+pub fn token(values: &Maidfile<Value>) -> String {
     match &values.project {
         Some(project) => match &project.server {
             Some(server) => server.token.clone(),
@@ -57,4 +58,4 @@ pub fn token(values: &Maidfile) -> String {
     }
 }
 
-pub fn all(maidfile: Maidfile) -> (String, String, String, String, i64) { (address(&maidfile), websocket(&maidfile), token(&maidfile), host(&maidfile), port(&maidfile)) }
+pub fn all(maidfile: Maidfile<Value>) -> (String, String, String, String, i64) { (address(&maidfile), websocket(&maidfile), token(&maidfile), host(&maidfile), port(&maidfile)) }
diff --git a/maid/server/docker/run.rs b/maid/server/docker/run.rs
index feb8421..51eac40 100644
--- a/maid/server/docker/run.rs
+++ b/maid/server/docker/run.rs
@@ -9,8 +9,8 @@ macro_rules! Handle {
     };
 }
 
-use crate::{structs::ConnectionData, table, Kind, Level, Response};
-use maid::log::prelude::*;
+use crate::{Kind, Level, Response};
+use maid::{log::prelude::*, models::server::ConnectionData, table};
 
 use bytes::Bytes;
 use flate2::{write::GzEncoder, Compression};
diff --git a/maid/server/main.rs b/maid/server/main.rs
index 44d2744..a12ad78 100644
--- a/maid/server/main.rs
+++ b/maid/server/main.rs
@@ -1,8 +1,6 @@
 mod docker;
 mod globals;
 mod helpers;
-mod structs;
-mod table;
 
 use bollard::{Docker, API_DEFAULT_VERSION};
 use docker::container;
diff --git a/maid/server/structs.rs b/maid/server/structs.rs
deleted file mode 100644
index ddb7b02..0000000
--- a/maid/server/structs.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-use serde::{Deserialize, Serialize};
-use serde_json::Value;
-use std::collections::BTreeMap;
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct ConnectionInfo {
-    pub name: String,
-    pub args: Vec<String>,
-    pub remote: Remote,
-    pub script: Vec<String>,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct ConnectionData {
-    pub info: ConnectionInfo,
-    pub maidfile: Maidfile,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Maidfile {
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub import: Option<Vec<String>>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub env: Option<BTreeMap<String, Value>>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub project: Option<Project>,
-    pub tasks: BTreeMap<String, Tasks>,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Project {
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub name: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub version: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub server: Option<Server>, // wip
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Server {
-    pub address: Address, // wip
-    pub token: String,    // wip
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Address {
-    pub host: String,
-    pub port: i64,
-    pub ssl: bool,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Tasks {
-    pub script: Value,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub hide: Option<bool>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub path: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub info: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub cache: Option<Cache>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub remote: Option<Remote>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub depends: Option<Vec<String>>,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Cache {
-    pub path: String,
-    pub target: Vec<String>,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct CacheConfig {
-    pub target: Vec<String>,
-    pub hash: String,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Remote {
-    pub push: Vec<String>,
-    pub pull: String,
-    pub image: String,
-    pub shell: String,
-    pub silent: bool,
-    pub exclusive: bool,
-}
diff --git a/maid/server/table.rs b/maid/server/table.rs
deleted file mode 100644
index 30f1e7e..0000000
--- a/maid/server/table.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-use crate::structs::Maidfile;
-use maid::{helpers, log::prelude::*};
-
-use macros_rs::{exp::ternary, fmt::str};
-use serde_json::Value;
-use std::path::PathBuf;
-use std::{collections::BTreeMap, collections::HashMap, env};
-use text_placeholder::Template;
-
-pub fn create(values: Maidfile, args: &Vec<String>, project: PathBuf) -> HashMap<&str, &str> {
-    let mut table = HashMap::new();
-    let empty_env: BTreeMap<String, Value> = BTreeMap::new();
-
-    table.insert("os.platform", env::consts::OS);
-    table.insert("os.arch", env::consts::ARCH);
-
-    trace!(os_platform = env::consts::OS);
-    trace!(os_arch = env::consts::ARCH);
-
-    match env::current_dir() {
-        Ok(path) => {
-            table.insert("dir.current", helpers::string::path_to_str(&path));
-            trace!("dir.current = \"{}\"", path.display());
-        }
-        Err(err) => error!(%err, "Current directory could not be added as script variable."),
-    }
-
-    match home::home_dir() {
-        Some(path) => {
-            table.insert("dir.home", helpers::string::path_to_str(&path));
-            trace!("dir.home = \"{}\"", path.display());
-        }
-        None => error!("Home directory could not be added as script variable."),
-    }
-
-    table.insert("dir.project", helpers::string::path_to_str(&project));
-    trace!("dir.project = \"{}\"", project.display());
-
-    for (pos, arg) in args.iter().enumerate() {
-        trace!("arg.{pos} = \"{arg}\"");
-        table.insert(str!(format!("arg.{pos}")), arg);
-    }
-
-    let user_env = match &values.env {
-        Some(env) => env.iter(),
-        None => empty_env.iter(),
-    };
-
-    for (key, value) in user_env {
-        let value_formatted = ternary!(
-            value.to_string().starts_with("\""),
-            helpers::string::trim_start_end(str!(Template::new_with_placeholder(&value.to_string(), "%{", "}").fill_with_hashmap(&table))).replace("\"", "\\\""),
-            str!(Template::new_with_placeholder(&value.to_string(), "%{", "}").fill_with_hashmap(&table)).replace("\"", "\\\"")
-        );
-
-        env::set_var(key, value_formatted.clone());
-        trace!("env.{key} = \"{value_formatted}\"");
-        table.insert(str!(format!("env.{}", key.clone())), str!(value_formatted));
-    }
-
-    return table;
-}
diff --git a/maid/shared/helpers/maidfile.rs b/maid/shared/helpers/maidfile.rs
index f06e7c4..5f10c3e 100644
--- a/maid/shared/helpers/maidfile.rs
+++ b/maid/shared/helpers/maidfile.rs
@@ -1,7 +1,7 @@
 use crate::log::prelude::*;
-use crate::models::client::{DisplayTask, Maidfile};
+use crate::models::{client::DisplayTask, shared::Maidfile};
 
-impl Maidfile {
+impl<T: serde::Serialize> Maidfile<T> {
     pub fn to_json(&self) -> String {
         match serde_json::to_string(&self) {
             Ok(contents) => contents,
diff --git a/maid/shared/models/client.rs b/maid/shared/models/client.rs
index 4fcc303..a1d7b5e 100644
--- a/maid/shared/models/client.rs
+++ b/maid/shared/models/client.rs
@@ -1,64 +1,6 @@
+use crate::models::shared::{Maidfile, Remote};
 use serde::{Deserialize, Serialize};
-use std::collections::BTreeMap;
 use std::path::PathBuf;
-use toml::Value;
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Maidfile {
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub import: Option<Vec<String>>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub env: Option<BTreeMap<String, Value>>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub project: Option<Project>,
-    pub tasks: BTreeMap<String, Tasks>,
-}
-
-#[derive(Clone, Debug, Default, Deserialize, Serialize)]
-pub struct Project {
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub name: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub version: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub server: Option<Server>, // wip
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Server {
-    pub address: Address, // wip
-    pub token: String,    // wip
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Address {
-    pub host: String,
-    pub port: i64,
-    pub ssl: bool,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Tasks {
-    pub script: Value,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub hide: Option<bool>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub path: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub info: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub cache: Option<Cache>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub remote: Option<Remote>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub depends: Option<Vec<String>>,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Cache {
-    pub path: String,
-    pub target: Vec<String>,
-}
 
 #[derive(Clone, Debug, Deserialize, Serialize)]
 pub struct CacheConfig {
@@ -67,23 +9,13 @@ pub struct CacheConfig {
 }
 
 #[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Remote {
-    pub push: Vec<String>,
-    pub pull: String,
-    pub image: String,
-    pub shell: String,
-    pub silent: bool,
-    pub exclusive: bool,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Task {
-    pub maidfile: Maidfile,
+pub struct Task<T> {
+    pub maidfile: Maidfile<T>,
     pub name: String,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub remote: Option<Remote>,
     pub project: PathBuf,
-    pub script: Value,
+    pub script: T,
     pub path: String,
     pub args: Vec<String>,
     pub silent: bool,
@@ -91,8 +23,8 @@ pub struct Task {
 }
 
 #[derive(Clone, Debug)]
-pub struct Runner<'a> {
-    pub maidfile: &'a Maidfile,
+pub struct Runner<'a, T> {
+    pub maidfile: &'a Maidfile<T>,
     pub name: &'a String,
     pub script: Vec<&'a str>,
     pub path: &'a String,
@@ -147,9 +79,9 @@ pub struct ConnectionInfo {
 }
 
 #[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct ConnectionData {
+pub struct ConnectionData<T> {
     pub info: ConnectionInfo,
-    pub maidfile: Maidfile,
+    pub maidfile: Maidfile<T>,
 }
 
 #[derive(Deserialize)]
diff --git a/maid/shared/models/server.rs b/maid/shared/models/server.rs
index ddb7b02..18fd5ad 100644
--- a/maid/shared/models/server.rs
+++ b/maid/shared/models/server.rs
@@ -1,90 +1,17 @@
+use crate::models::shared::{Maidfile, Remote};
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
-use std::collections::BTreeMap;
 
 #[derive(Clone, Debug, Deserialize, Serialize)]
 pub struct ConnectionInfo {
     pub name: String,
-    pub args: Vec<String>,
     pub remote: Remote,
+    pub args: Vec<String>,
     pub script: Vec<String>,
 }
 
 #[derive(Clone, Debug, Deserialize, Serialize)]
 pub struct ConnectionData {
     pub info: ConnectionInfo,
-    pub maidfile: Maidfile,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Maidfile {
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub import: Option<Vec<String>>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub env: Option<BTreeMap<String, Value>>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub project: Option<Project>,
-    pub tasks: BTreeMap<String, Tasks>,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Project {
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub name: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub version: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub server: Option<Server>, // wip
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Server {
-    pub address: Address, // wip
-    pub token: String,    // wip
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Address {
-    pub host: String,
-    pub port: i64,
-    pub ssl: bool,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Tasks {
-    pub script: Value,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub hide: Option<bool>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub path: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub info: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub cache: Option<Cache>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub remote: Option<Remote>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub depends: Option<Vec<String>>,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Cache {
-    pub path: String,
-    pub target: Vec<String>,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct CacheConfig {
-    pub target: Vec<String>,
-    pub hash: String,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Remote {
-    pub push: Vec<String>,
-    pub pull: String,
-    pub image: String,
-    pub shell: String,
-    pub silent: bool,
-    pub exclusive: bool,
+    pub maidfile: Maidfile<Value>,
 }
diff --git a/maid/shared/models/shared.rs b/maid/shared/models/shared.rs
index e69de29..b0c7d71 100644
--- a/maid/shared/models/shared.rs
+++ b/maid/shared/models/shared.rs
@@ -0,0 +1,69 @@
+use serde::{Deserialize, Serialize};
+use std::collections::BTreeMap;
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Maidfile<T> {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub import: Option<Vec<String>>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub env: Option<BTreeMap<String, T>>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub project: Option<Project>,
+    pub tasks: BTreeMap<String, Tasks<T>>,
+}
+
+#[derive(Clone, Debug, Default, Deserialize, Serialize)]
+pub struct Project {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub name: Option<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub version: Option<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub server: Option<Server>, // wip
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Server {
+    pub address: Address, // wip
+    pub token: String,    // wip
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Address {
+    pub host: String,
+    pub port: i64,
+    pub tls: bool,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Tasks<T> {
+    pub script: T,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub hide: Option<bool>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub path: Option<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub info: Option<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub cache: Option<Cache>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub remote: Option<Remote>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub depends: Option<Vec<String>>,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Cache {
+    pub path: String,
+    pub target: Vec<String>,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Remote {
+    pub push: Vec<String>,
+    pub pull: String,
+    pub image: String,
+    pub shell: String,
+    pub silent: bool,
+    pub exclusive: bool,
+}
diff --git a/maid/shared/table.rs b/maid/shared/table.rs
index b3db9da..091457f 100644
--- a/maid/shared/table.rs
+++ b/maid/shared/table.rs
@@ -1,16 +1,15 @@
 use crate::helpers;
 use crate::log::prelude::*;
-use crate::models::client::Maidfile;
+use crate::models::shared::Maidfile;
 
 use macros_rs::{exp::ternary, fmt::str};
 use std::path::PathBuf;
 use std::{collections::BTreeMap, collections::HashMap, env};
 use text_placeholder::Template;
-use toml::Value;
 
-pub fn create(values: Maidfile, args: &Vec<String>, project: PathBuf) -> HashMap<&str, &str> {
+pub fn create<T: ToString>(values: Maidfile<T>, args: &Vec<String>, project: PathBuf) -> HashMap<&str, &str> {
     let mut table = HashMap::new();
-    let empty_env: BTreeMap<String, Value> = BTreeMap::new();
+    let empty_env: BTreeMap<String, T> = BTreeMap::new();
 
     trace!(value = env::consts::OS, "os.platform");
     trace!(value = env::consts::ARCH, "os.arch");
-- 
GitLab