idl 0.1.1

Library used for the idl language.
Documentation
use crate::idl;
use crate::ids;
use convert_case::{Case, Casing};
use serde::{Deserialize, Serialize};
use std::{io::Write, path::Path};

pub mod protocol_test;

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientType {
    pub client_name: String,
    pub server_name: String,
}

#[derive(Debug, Deserialize, Serialize)]
pub enum BuildType {
    Release,
    Debug,
}

#[derive(Debug, Deserialize, Serialize)]
pub enum ServerArg {
    Run,
    Build,
    Generate,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ServerType {
    pub server_name: String,
    pub input_path: String,
    pub build_type: BuildType,
    pub args: ServerArg,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DataType {
    pub server_name: String,
    pub input_path: String,
}

#[derive(Debug, Deserialize, Serialize)]
pub enum RequestType {
    Client(ClientType),
    Server(ServerType),
    Data(DataType),
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LibraryItem {
    pub nodes: Vec<idl::nodes::IdlNode>,
    pub hash: Box<[u8]>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LanguageRequest {
    pub libraries: Vec<LibraryItem>,
    pub ids_nodes: Vec<ids::nodes::IdsNode>,
    pub request_type: RequestType,
}

#[derive(Debug, Deserialize, Serialize)]
pub enum ResponseType {
    Generated(Vec<StorageItem>),
    Undefined(String),
}

#[derive(Debug, Deserialize, Serialize)]
pub enum StorageItem {
    Source {
        name: String,
        txt: String,
    },
    Folder {
        name: String,
        items: Vec<StorageItem>,
    },
    BinarySource {
        name: String,
        data: String,
    },
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LanguageResponse {
    pub response_type: ResponseType,
    pub response_messages: Vec<String>,
}

pub trait IdlGen {
    fn send_request(&self, request: LanguageRequest) -> anyhow::Result<LanguageResponse>;
}

impl StorageItem {
    // 'Merged' means that no folder will have the contents removed if another folder of the same
    // name is found in the same directory.
    pub fn write_items(&self, path: &Path, is_merged: bool) -> anyhow::Result<()> {
        match self {
            Self::Source { name, txt } => {
                let mut file = std::fs::OpenOptions::new()
                    .write(true)
                    .truncate(true)
                    .create(true)
                    .open(path.join(name.as_str()))?;
                file.write_all(txt.as_bytes())?;
            }
            Self::Folder { name, items } => {
                let new_path = path.join(&name);
                if !is_merged {
                    let _ = std::fs::remove_dir_all(&new_path);
                }
                std::fs::create_dir_all(&new_path)?;

                for item in items {
                    item.write_items(&new_path, is_merged)?;
                }
            }
            Self::BinarySource { name, data } => {
                let mut file = std::fs::OpenOptions::new()
                    .write(true)
                    .truncate(true)
                    .create(true)
                    .open(path.join(name.as_str()))?;
                file.write_all(&base64::decode(data)?)?;
            }
        }

        Ok(())
    }
}

pub fn create_new_module(name: &str) -> StorageItem {
    let mut result = vec![];

    result.push(StorageItem::Folder {
        name: ".idl".to_owned(),
        items: vec![],
    });

    result.push(StorageItem::Source {
        name: ".gitignore".to_owned(),
        txt: "".to_owned(),
    });

    result.push(StorageItem::Source {
        name: format!("{}.ids", name.to_case(Case::Snake)),
        txt: format!(
            r#"package {} {{
    idl: 0,
    version: 1,
}}

server Main {{ }}

client Main {{ }}
"#,
            name.to_case(Case::Camel),
        ),
    });

    result.push(StorageItem::Source {
        name: format!("{}.idl", name.to_case(Case::Snake)),
        txt: format!(
            r#"library {};

interface Service {{
    hello: (value: string) -> string,
}}
"#,
            name.to_case(Case::Snake),
        ),
    });

    return StorageItem::Folder {
        name: name.to_case(Case::Snake),
        items: result,
    };
}