Skip to content

Commit

Permalink
Merge pull request #53 from dzcode-io/chore/algebraic-data-types-impl…
Browse files Browse the repository at this point in the history
…ementation

use const and match statement instead of json data
  • Loading branch information
ZibanPirate authored Jan 7, 2024
2 parents 257bcf1 + 8e13627 commit a4948a2
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 93 deletions.
2 changes: 1 addition & 1 deletion rust/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
target
src/_data
src/_auto_generated
23 changes: 23 additions & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]

[build-dependencies]
serde = { version = "1.0.194", features = ["derive"] }
serde_json = "1.0.110"
serde_json = { version = "1.0.110", features = ["preserve_order"] }
134 changes: 125 additions & 9 deletions rust/build.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,135 @@
use serde_json::Value;
use std::{fs, io, path::Path};

fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
fs::create_dir_all(&dst)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
let ty = entry.file_type()?;
fn dir_tree_to_list(dir: impl AsRef<Path>) -> (String, String) {
let info_path = dir.as_ref().join("info.json");
let info_dot_json = match info_path.exists() {
true => {
let info = fs::read_to_string(&info_path).unwrap();
let info: Value = serde_json::from_str(info.as_str()).unwrap();
let mut info = info.as_object().unwrap().clone();
info.remove("$schema");
Some(Value::Object(info))
}
false => None,
};
let this_node = match &info_dot_json {
Some(info) => {
let path = dir.as_ref().display().to_string();
let path = path.split("_data/").last().unwrap_or(&path);

format!(
r#"const {}: Node = Node {{
name: NodeName {{
en: {},
ar: {},
fr: {},
}},
r#type: {},
}};
"#,
path.replace("/", "_").to_uppercase(),
info.get("name").unwrap().get("en").unwrap(),
info.get("name").unwrap().get("ar").unwrap(),
info.get("name").unwrap().get("fr").unwrap(),
match &info.get("type").unwrap() {
Value::String(s) => {
let ty = s
.chars()
.enumerate()
.map(|(i, c)| {
if i == 0 {
c.to_uppercase().to_string()
} else {
c.to_lowercase().to_string()
}
})
.collect::<String>();

match ty.as_str() {
"Specialty" | "Sector" => format!(
r#"NodeType::{}{{
terms: NodeTerms {{
per_year: 2,
slots: &[7, 8, 9, 10],
}},
}}"#,
ty
),

_ => format!("NodeType::{}", ty),
}
}
_ => "".to_string(),
}
)
}
None => String::new(),
};

let this_match = match &info_dot_json {
Some(_) => {
let path = dir.as_ref().display().to_string();
let path = path.split("_data/").last().unwrap_or(&path);
format!(
" \"{}\" => Some(&{}),\n",
path,
path.replace("/", "_").to_uppercase()
)
}
None => String::new(),
};

let sub_dirs = fs::read_dir(&dir).unwrap();
let children = sub_dirs.filter_map(|entry| {
let entry = entry.unwrap();
let ty = entry.file_type().unwrap();
if ty.is_dir() {
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
Some(dir_tree_to_list(entry.path()))
} else {
fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
None
}
}
});

let mut constants = String::new();
let mut matches = String::new();
children.for_each(|(c, m)| {
constants.push_str(&c);
matches.push_str(&m);
});

(
format!("{}{}", this_node, constants),
format!("{}{}", this_match, matches),
)
}

fn generate_data_file() -> Result<(), io::Error> {
let string_tree = dir_tree_to_list("../_data");

let data = format!(
r##"// This is auto-generated. Do not edit manually.
use crate::node::model::{{Node, NodeName, NodeType, NodeTerms}};
{}
pub fn get_node_by_path(path: &str) -> Option<&Node> {{
match path {{
{}
_ => None,
}}
}}"##,
string_tree.0, string_tree.1
);
fs::create_dir_all("./src/_auto_generated")?;
fs::write("./src/_auto_generated/data.rs", data)?;
fs::write(
"./src/_auto_generated/mod.rs",
"// This is auto-generated. Do not edit manually\npub mod data;\n",
)?;
Ok(())
}

fn main() {
copy_dir_all(Path::new("../_data"), Path::new("./src/_data")).unwrap();
generate_data_file().unwrap();
}
39 changes: 14 additions & 25 deletions rust/src/api/get_node_by_path.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
use crate::node::model::Node;
use std::fs;
use crate::{_auto_generated, node::model::Node};

static DATA_FOLDER: &'static str = "src/_data";
static CARGO_MANIFEST_DIR: &'static str = env!("CARGO_MANIFEST_DIR");

pub fn get_node_by_path(path: &str) -> Option<Node> {
let fs_path = format!("{}/{}/{}/info.json", CARGO_MANIFEST_DIR, DATA_FOLDER, path);
let Ok(info) = fs::read_to_string(fs_path) else {
return None;
};
let Ok(node) = serde_json::from_str::<Node>(info.as_str()) else {
return None;
};
Some(node)
pub fn get_node_by_path(path: &str) -> Option<&Node> {
_auto_generated::data::get_node_by_path(path)
}

#[cfg(test)]
Expand All @@ -39,9 +28,9 @@ mod test {
"umkb",
Node {
name: NodeName {
ar: "جامعة محمد خيضر بسكرة".to_string(),
en: "University of Mohamed Khider Biskra".to_string(),
fr: "Université Mohamed Khider Biskra".to_string(),
ar: "جامعة محمد خيضر بسكرة",
en: "University of Mohamed Khider Biskra",
fr: "Université Mohamed Khider Biskra",
},
r#type: NodeType::University,
},
Expand All @@ -50,9 +39,9 @@ mod test {
"umkb/fst",
Node {
name: NodeName {
ar: "كلية العلوم والتكنلوجيا".to_string(),
en: "Faculty of Science and Technology".to_string(),
fr: "Faculté des Sciences et de la Technologie".to_string(),
ar: "كلية العلوم والتكنلوجيا",
en: "Faculty of Science and Technology",
fr: "Faculté des Sciences et de la Technologie",
},
r#type: NodeType::Faculty,
},
Expand All @@ -61,14 +50,14 @@ mod test {
"umkb/fst/dee/sec",
Node {
name: NodeName {
ar: "تخصص التحكم الكهربائي".to_string(),
en: "Specialy of Electrical Control".to_string(),
fr: "Spécialité de commande électrique".to_string(),
ar: "تخصص التحكم الكهربائي",
en: "Specialy of Electrical Control",
fr: "Spécialité de commande électrique",
},
r#type: NodeType::Specialty {
terms: NodeTerms {
per_year: 2,
slots: vec![7, 8, 9, 10],
slots: &[7, 8, 9, 10],
},
},
},
Expand All @@ -90,7 +79,7 @@ mod test {
fn assert_node(expected: &Node, actual: &Node) {
assert_eq!(
expected, actual,
"Expected node to be '{}', but got '{}'",
"Expected node to be '{:?}', but got '{:?}'",
expected, actual
);
}
Expand Down
1 change: 1 addition & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
mod _auto_generated;
pub mod api;
pub mod node;
65 changes: 8 additions & 57 deletions rust/src/node/model.rs
Original file line number Diff line number Diff line change
@@ -1,79 +1,30 @@
use serde::{Deserialize, Serialize};
use serde_json::json;

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct NodeName {
pub ar: String,
pub en: String,
pub fr: String,
}

impl std::fmt::Display for NodeName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let obj = json!({"ar": self.ar, "en": self.en, "fr": self.fr});
write!(f, "{}", serde_json::to_string_pretty(&obj).unwrap())
}
pub ar: &'static str,
pub en: &'static str,
pub fr: &'static str,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type")] // to flatten the enum to the parent struct
#[derive(Debug, PartialEq)]
pub enum NodeType {
#[serde(rename = "UNIVERSITY")]
University,
#[serde(rename = "ACADEMY")]
Academy,
#[serde(rename = "PRIVATE_SCHOOL")]
PrivateSchool,
#[serde(rename = "INSTITUTE")]
Institute,
#[serde(rename = "FACULTY")]
Faculty,
#[serde(rename = "DEPARTMENT")]
Department,
#[serde(rename = "SPECIALTY")]
Specialty { terms: NodeTerms },
#[serde(rename = "SECTOR")]
Sector { terms: NodeTerms },
}

impl std::fmt::Display for NodeType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NodeType::University => write!(f, "UNIVERSITY"),
NodeType::Academy => write!(f, "ACADEMY"),
NodeType::PrivateSchool => write!(f, "PRIVATE_SCHOOL"),
NodeType::Institute => write!(f, "INSTITUTE"),
NodeType::Faculty => write!(f, "FACULTY"),
NodeType::Department => write!(f, "DEPARTMENT"),
NodeType::Specialty { .. } => write!(f, "SPECIALTY"),
NodeType::Sector { .. } => write!(f, "SECTOR"),
}
}
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
#[derive(Debug, PartialEq, Clone)]
pub struct NodeTerms {
#[serde(rename = "perYear")]
pub per_year: usize,
pub slots: Vec<usize>,
pub slots: &'static [i32],
}

impl std::fmt::Display for NodeTerms {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let obj = json!({"perYear": self.per_year, "slots": self.slots});
write!(f, "{}", serde_json::to_string_pretty(&obj).unwrap())
}
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct Node {
pub name: NodeName,
#[serde(flatten)]
pub r#type: NodeType,
}

impl std::fmt::Display for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", serde_json::to_string_pretty(&self).unwrap())
}
}

0 comments on commit a4948a2

Please sign in to comment.