aboutsummaryrefslogtreecommitdiff
path: root/crates/divina_config/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2022-02-07 04:26:07 -0800
committerFuwn <[email protected]>2025-06-09 00:46:03 -0700
commitbfa483c6aa5db5c9825faded62176904d516faf7 (patch)
tree043b73bdd939f955f2b4d1e6309c8b697d6e0544 /crates/divina_config/src
downloadarchived-divina-bfa483c6aa5db5c9825faded62176904d516faf7.tar.xz
archived-divina-bfa483c6aa5db5c9825faded62176904d516faf7.zip
feat(divina): pre-release :star:
Diffstat (limited to 'crates/divina_config/src')
-rw-r--r--crates/divina_config/src/lib.rs424
1 files changed, 424 insertions, 0 deletions
diff --git a/crates/divina_config/src/lib.rs b/crates/divina_config/src/lib.rs
new file mode 100644
index 0000000..1df8ddb
--- /dev/null
+++ b/crates/divina_config/src/lib.rs
@@ -0,0 +1,424 @@
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+// SPDX-License-Identifier: GPL-3.0-only
+
+#![deny(
+ warnings,
+ nonstandard_style,
+ unused,
+ future_incompatible,
+ rust_2018_idioms,
+ unsafe_code
+)]
+#![deny(clippy::all, clippy::nursery, clippy::pedantic)]
+#![recursion_limit = "128"]
+#![doc(
+ html_logo_url = "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/twitter/282/ribbon_1f380.png",
+ html_favicon_url = "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/twitter/282/ribbon_1f380.png"
+)]
+
+use std::{fmt, fmt::Formatter, io::Read};
+
+use rlua::{Lua, Table};
+
+const VERSION: &str = env!("CARGO_PKG_VERSION");
+
+#[allow(unused)]
+#[derive(PartialEq)]
+enum GetRequired {
+ Yes,
+ No,
+}
+
+/// Get and store a variable from Lua context, if it doesn't exist; `None`.
+///
+/// With help from:
+///
+/// - <https://users.rust-lang.org/t/how-to-use-self-or-any-variable-in-macro-generated-function-body/6264>
+/// - <https://github.com/steveklabnik/trpl/blob/master/advanced-macros.md>
+/// - <https://www.reddit.com/r/rust/comments/iim7b7/how_to_identify_rust_tyttident_expr_patassociated/>
+/// - <https://medium.com/@phoomparin/a-beginners-guide-to-rust-macros-5c75594498f1>
+#[macro_export]
+macro_rules! get_or_none {
+ ($table:ident, $key:expr, $key_type:ty, $assign_to:tt, $required:expr) => {
+ $assign_to = if $required == GetRequired::No {
+ match $table.get::<_, $key_type>($key) {
+ Ok(some_value) => Some(some_value),
+ Err(_) => None,
+ }
+ } else {
+ Some($table.get::<_, $key_type>($key).expect(&format!(
+ "could not access required global `{}`, perhaps you've forgotten to define it ?",
+ $key,
+ )))
+ }
+ };
+ ($table:ident, $from_table:expr, $key:expr, $key_type:ty, $assign_to:tt, $required:expr) => {
+ $assign_to = if $required == GetRequired::No {
+ match $table.get::<_, $key_type>($key) {
+ Ok(some_value) => Some(some_value),
+ Err(_) => None,
+ }
+ } else {
+ Some($table.get::<_, $key_type>($key).expect(&format!(
+ "could not access required global `{}.{}`, perhaps you've forgotten to define it ?",
+ $from_table, $key,
+ )))
+ }
+ };
+}
+#[macro_export]
+macro_rules! get_table {
+ ($assign_to:ident, $key:expr, $globals:ident) => {
+ let $assign_to = $globals.get::<_, Table<'_>>($key).expect(&format!(
+ "could not get `{}`, perhaps you've made an error ?",
+ $key
+ ));
+ };
+}
+#[macro_export]
+macro_rules! get_enum_or_none {
+ ($table:ident, $key:expr, $assign_to:tt, $from_u8:tt, $error_message:expr, $required:expr) => {
+ $assign_to = if $required == GetRequired::No {
+ match $table.get::<_, u8>($key) {
+ Ok(some_value) => Some($from_u8(some_value).expect($error_message)),
+ Err(_) => None,
+ }
+ } else {
+ Some(
+ $from_u8($table.get::<_, u8>($key).expect(&format!(
+ "could not access required global `{}`, perhaps you've forgotten to define it ?",
+ $key,
+ )))
+ .expect($error_message),
+ )
+ }
+ };
+ (
+ $table:ident,
+ $from_table:expr,
+ $key:expr,
+ $assign_to:tt,
+ $from_u8:tt,
+ $error_message:expr,
+ $required:expr
+ ) => {
+ $assign_to = if $required == GetRequired::No {
+ match $table.get::<_, u8>($key) {
+ Ok(some_value) => Some($from_u8(some_value).expect($error_message)),
+ Err(_) => None,
+ }
+ } else {
+ Some(
+ $from_u8($table.get::<_, u8>($key).expect(&format!(
+ "could not access required global `{}.{}`, perhaps you've forgotten to define it ?",
+ $from_table, $key,
+ )))
+ .expect($error_message),
+ )
+ }
+ };
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum ConfigType {
+ Package,
+ Workspace,
+}
+impl fmt::Display for ConfigType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ // write!(f, "{:?}", self)
+ fmt::Debug::fmt(self, f)
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum PackageType {
+ Bin = 1,
+ Lib = 2,
+}
+impl PackageType {
+ #[must_use]
+ pub const fn from_u8(n: u8) -> Option<Self> {
+ match n {
+ 1 => Some(Self::Bin),
+ 2 => Some(Self::Lib),
+ _ => None,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub enum Arch {
+ X86 = 1,
+ X64 = 2,
+}
+impl Arch {
+ #[must_use]
+ pub const fn from_u8(n: u8) -> Option<Self> {
+ match n {
+ 1 => Some(Self::X86),
+ 2 => Some(Self::X64),
+ _ => None,
+ }
+ }
+
+ #[must_use]
+ pub fn from_string(arch: &Self) -> String {
+ match arch {
+ Arch::X86 => "32".to_string(),
+ Arch::X64 => "64".to_string(),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Config {
+ pub name: Option<String>,
+ pub version: Option<String>,
+ pub description: Option<String>,
+ pub license: Option<String>,
+ pub compile_options: Option<Vec<String>>,
+ pub minimum_divina_version: Option<String>,
+ pub sources: Option<Vec<String>>,
+ pub config_type: ConfigType,
+ pub members: Option<Vec<Self>>,
+ pub package_type: Option<PackageType>,
+ pub path: Option<String>,
+ pub arch: Option<Arch>,
+ pub compiler: Option<String>,
+}
+impl Config {
+ /// Create a new `Config`
+ #[must_use]
+ pub fn new() -> Self { Self::default() }
+
+ /// Grab configuration values from `Divina.lua` and set
+ ///
+ /// # Panics
+ /// if there are any errors
+ #[allow(clippy::too_many_lines)]
+ pub fn configure(&mut self, file: &str) {
+ let mut script = std::fs::File::open(file)
+ .unwrap_or_else(|_| panic!("!! could not locate `{}`, perhaps it doesn't exist ?", file));
+ let mut contents = String::new();
+ script.read_to_string(&mut contents).unwrap_or_else(|_| {
+ panic!(
+ "!! could not read `{}`, perhaps there's an encoding error ?",
+ file
+ )
+ });
+
+ let lua = Lua::new();
+
+ #[allow(clippy::cognitive_complexity)]
+ lua.context(|ctx| {
+ let globals = ctx.globals();
+ let test_function = ctx
+ .create_function(|_, ()| {
+ println!("test");
+
+ Ok(())
+ })
+ .expect("!! could not create function `test`, this *shouldn't* be possible");
+
+ let divina_table = ctx
+ .create_table()
+ .expect("!! could not create table `Divina`, this *shouldn't* be possible");
+ let type_table = ctx
+ .create_table()
+ .expect("!! could not create table `Divina.Type`, this *shouldn't* be possible");
+ let arch_table = ctx
+ .create_table()
+ .expect("!! could not create table `Divina.Arch`, this *shouldn't* be possible");
+
+ type_table
+ .set("Bin", 1)
+ .expect("!! could not set field `Divina.Type.Bin`, this *shouldn't* be possible");
+ type_table
+ .set("Lib", 2)
+ .expect("!! could not set field `Divina.Type.Lib`, this *shouldn't* be possible");
+
+ arch_table
+ .set("x86", 1)
+ .expect("!! could not set field `Divina.Type.x86`, this *shouldn't* be possible");
+ arch_table
+ .set("x64", 2)
+ .expect("!! could not set field `Divina.Type.x64`, this *shouldn't* be possible");
+
+ divina_table
+ .set("version", VERSION)
+ .expect("!! could not set field `Divina.version`, this *shouldn't* be possible");
+ divina_table
+ .set("Type", type_table)
+ .expect("!! could not set field `Divina.Type`, this *shouldn't* be possible");
+ divina_table
+ .set("Arch", arch_table)
+ .expect("!! could not set field `Divina.Arch`, this *shouldn't* be possible");
+
+ globals
+ .set("Divina", divina_table)
+ .expect("!! could not set table `Divina`, this *shouldn't* be possible");
+ globals
+ .set("test", test_function)
+ .expect("!! could not set function `test`, this *shouldn't* be possible");
+
+ ctx.load(contents.as_bytes()).exec().unwrap_or_else(|_| {
+ panic!(
+ "!! could not execute `{}`, perhaps you've made a syntax error ?",
+ file
+ )
+ });
+
+ self.config_type = if globals.get::<_, Table<'_>>("Workspace").is_ok() {
+ ConfigType::Workspace
+ } else if globals.get::<_, Table<'_>>("Package").is_ok() {
+ ConfigType::Package
+ } else {
+ println!(
+ "!! '{}' is neither `Workspace` nor `Package`, perhaps you've forgotten to assign to it \
+ ?",
+ file
+ );
+ std::process::exit(0);
+ };
+
+ if self.config_type == ConfigType::Package {
+ get_table!(config_table, "Package", globals);
+
+ get_or_none!(
+ config_table,
+ "Package",
+ "name",
+ String,
+ (self.name),
+ GetRequired::Yes
+ );
+ get_or_none!(
+ config_table,
+ "Package",
+ "version",
+ String,
+ (self.version),
+ GetRequired::Yes
+ );
+ get_or_none!(
+ config_table,
+ "Package",
+ "description",
+ String,
+ (self.description),
+ GetRequired::No
+ );
+ get_or_none!(
+ config_table,
+ "Package",
+ "license",
+ String,
+ (self.license),
+ GetRequired::No
+ );
+ get_or_none!(
+ config_table,
+ "Package",
+ "compile_options",
+ Vec<String>,
+ (self.compile_options),
+ GetRequired::No
+ );
+ get_or_none!(
+ config_table,
+ "Package",
+ "minimum_divina_version",
+ String,
+ (self.minimum_divina_version),
+ GetRequired::Yes
+ );
+ get_or_none!(
+ config_table,
+ "Package",
+ "sources",
+ Vec<String>,
+ (self.sources),
+ GetRequired::No
+ );
+ get_enum_or_none!(
+ config_table,
+ "Package",
+ "type",
+ (self.package_type),
+ (PackageType::from_u8),
+ "!! could not access `Package.type`, perhaps you've forgotten to assign it ?",
+ GetRequired::Yes
+ );
+ get_enum_or_none!(
+ config_table,
+ "Package",
+ "arch",
+ (self.arch),
+ (Arch::from_u8),
+ "!! could not access `Package.arch`, perhaps you've forgotten to assign it ?",
+ GetRequired::Yes
+ );
+ get_or_none!(
+ config_table,
+ "Package",
+ "compiler",
+ String,
+ (self.compiler),
+ GetRequired::No
+ );
+ } else {
+ get_table!(workspace_table, "Workspace", globals);
+
+ // This sequence of BS actually took about two hours to complete.
+ //
+ // With help from:
+ //
+ // - <https://github.com/amethyst/rlua/issues/57>
+ let members;
+ get_or_none!(
+ workspace_table,
+ "Workspace",
+ "members",
+ Vec<String>,
+ members,
+ GetRequired::No
+ );
+ if let Some(tables) = members {
+ self.members = Some(Vec::new());
+
+ for path in tables {
+ let mut config = Self::new();
+ config.configure(&format!("{}/Divina.lua", path));
+ config.path = Some(path);
+
+ self
+ .members
+ .as_mut()
+ .expect("!! could not access 'Config.members', this *shouldn't* be possible")
+ .push(config);
+ }
+ }
+ }
+ });
+ }
+}
+impl Default for Config {
+ fn default() -> Self {
+ Self {
+ name: None,
+ version: None,
+ description: None,
+ license: None,
+ compile_options: None,
+ minimum_divina_version: None,
+ sources: None,
+ config_type: ConfigType::Package,
+ members: None,
+ package_type: None,
+ path: None,
+ arch: None,
+ compiler: None,
+ }
+ }
+}