Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AvdanOS Configuration Pt. II #53

Merged
12 commits merged into from
Sep 10, 2022
11 changes: 10 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ documentation = "https://docs.avdanos.com/"
homepage = "https://avdanos.com/"
license = "GPL-3.0"
version = "0.0.1"
authors = ["Akane <[email protected]>", "Froxcey <[email protected]>", "Sammy <placeholder@email.com>"]
authors = ["Akane <[email protected]>", "Froxcey <[email protected]>", "Sammy <sammy99jsp@avdanos.com>"]
edition = "2021"
build = "build.rs"

Expand All @@ -18,6 +18,15 @@ json_comments = "0.2.1"
regex = "1.6.0"
slog-stdlog = "4.1.1"
bitflags = "1"
colored = "2.0.0"

[dependencies.compositor-macros]
path = "./src/macros"

[dependencies.json-tree]

git = "https://github.com/Sammy99jsp/json-tree.git"
features = ["jsonc"]

[dependencies.smithay]

Expand Down
73 changes: 42 additions & 31 deletions src/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,65 @@ use lazy_static::lazy_static;

pub(crate) use json_comments::StripComments;

use json_tree::{Index, TokenContent};
use json_tree::{Index,};
use serde::Deserialize;

use crate::{CONST::{
CONFIG_FOLDER,
CONFIG_FILE,
}, config::errors::UnexpectedToken};

use super::sections::{keybinds::Keybinds, section::ConfigurationSection};
use super::sections::{keybinds::Keybinds};

lazy_static! {
pub static ref PATH : String = CONFIG_FOLDER.join(*CONFIG_FILE).to_string_lossy().to_string();
pub static ref PATH : String = CONFIG_FOLDER.join(*CONFIG_FILE).to_string_lossy().to_string();
}
static mut INDEX : Option<Index> = None;

static mut _INDEX : Option<Index> = None;

static mut CONFIG : Option<Config> = None;

#[derive(Deserialize, Debug)]
pub struct Config {
keybinds: <Keybinds as ConfigurationSection>::Raw,
pub keybinds: Keybinds
}

impl Config {

pub fn path() -> String {
PATH.to_string()
}

///
/// Returns the config file's JSON index.
///
pub fn index<'a>() -> &'a Index {
unsafe {
_INDEX.as_ref().unwrap()
INDEX.as_ref().unwrap()
}
}

///
/// Returns the Global Configuration Object.
///
pub fn config<'a>() -> &'a Self {
unsafe {
CONFIG.as_ref().unwrap()
}
}
pub fn from_file() -> Result<Config, Box<dyn Error>> {


///
/// Loads the config.
///
/// THIS FUNCTION SHOULD BE NEAR THE TOP OF `main.rs`
///
pub fn load() -> Result<(), Box<dyn Error>> {
let path = PATH.to_string();
fs::create_dir_all(*CONFIG_FOLDER)
.expect("Error while creating the AvdanOS config directory!");

// TODO: If config file not found, either download config
// or use a pre-bundled copy.
let file: File = fs::OpenOptions::new()
.read(true).write(true).create(true)
.open(&path)?;
Expand Down Expand Up @@ -91,32 +117,17 @@ impl Config {
index
};

let mut parsed: Config = serde_json::from_reader(stripped)?;


unsafe {
_INDEX = Some(src_map);
INDEX = Some(src_map);
}

let result = Keybinds::parse(
Keybinds::traceable(
Some(true)
),
&parsed.keybinds,
Self::index()
);

match result {
Ok(k) => {
return Ok(parsed)
},
Err(errs) => {
for err in errs {
println!("{}\n", err);
}
let o = serde_json::from_reader(stripped)?;


unsafe {
CONFIG = Some(o);
};

panic!()
}
}
Ok(())
}
}
2 changes: 1 addition & 1 deletion src/config/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use compositor_macros::AvError;
use json_tree::{ParserError, TokenContent};
use crate::core::error::{TraceableError, AvError, Traceable};

use super::config::{self, Config};
use super::config;


///
Expand Down
8 changes: 8 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pub mod config;
pub mod sections;
pub mod section;
pub mod templating;
pub mod errors;

pub use config::Config;
pub use section::ConfigurationSection;
196 changes: 196 additions & 0 deletions src/config/section.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
use std::collections::HashMap;

use colored::Colorize;
use compositor_macros::{AvError, location, description};
use json_tree::{JSONPath, };

use crate::{core::error::{TraceableError, Traceable, AvError}, config::{config::{self, Config}, templating::{r#macro::SignatureMismatchError, AvMacro, avvalue::AvValue}}, Nadva::error::compare_errors};

pub trait ConfigurationSection : Sized {
///
/// The absolute path to this section as a str.
///
const PATH : &'static str;

///
/// Returns the absolute path to this section.
/// Can be used in finding location of data.
///
fn path() -> JSONPath {
Self::PATH.to_string().try_into().unwrap()
}

///
/// Returns this section's traceable.
///
fn traceable(key : Option<bool>) -> Traceable {
let loc = Config::index().get(&Self::path()).unwrap();
Traceable::combine(&config::PATH.to_string(), loc, key)
}

fn from_map(declared : HashMap<AvMacro, AvValue>, raw : HashMap<String, serde_json::Value>) -> HashMap<AvMacro, AvValue> {
let path : JSONPath = Self::path();

let res = raw.iter().map(|(k, v)| {
// Parse as a macro.
let p = path.push(k.clone());
let loc = Config::index().get(&p).unwrap();
let k_pos = Traceable::combine(&Config::path(), loc, Some(true));
let v_pos = Traceable::combine(&Config::path(), loc, Some(false));

(
AvMacro::parse(
k_pos.clone(),
k.clone()
),
v,
k_pos,
v_pos
)
});

// Syntactically invalid macros.

let mut errors : Vec<(Box<dyn TraceableError>, Traceable)> = vec![];

res
.clone()
.filter(|(k, _, _, _)| k.is_err())
.for_each(|(k, _, p, _)| {
let n = k.unwrap_err();

for err in n {
errors.push((err, p.clone()));
}
}
);


// Syntactically Valid macros
let defined = res
.filter(|(k, _, _, _)| k.is_ok())
.map(|(k, v, p1, p2)| (k.unwrap(), v, p1, p2));


let mut output : HashMap<AvMacro, AvValue> = HashMap::new();


let mut found_macros : Vec<usize> = vec![];
// Look up the valid macros against our declared HashMap.

for (declared_m, default_v) in declared {
let defined_m = defined.clone().position(|(m, _, _, _)| m.identifier() == declared_m.identifier());

let (avmacro, avvalue) = match defined_m {
None => {
// Macro not in user's config,
// use default
// (and possibly issue a warning).

// TODO: @Sammy99jsp add 'not found' warning.


errors.push((
Box::new(
MacroMissing(Self::traceable(Some(false)), declared_m.identifier(), Self::path())
),
Self::traceable(Some(false))
));
(declared_m, default_v)
},
Some(i) => {
found_macros.push(i);
let (m, v, p, p_v) = defined.clone().nth(i).unwrap();

// Check if the macro's signature matches our defined one.
let sig_check = declared_m.has_signature(&m);

if let Err((delta, vec)) = sig_check {
errors.push((
Box::new(
SignatureMismatchError(
p.clone(),
m.identifier(),
(delta, vec.iter().map(|e| (*e).clone()).collect())
)
) as Box<dyn TraceableError>,
p
));

(declared_m, default_v)
} else {
// VALUE CHECKS
// Now check the value's type against the default's
match default_v
.parse_same_type(p_v.clone(), v.clone())
{
Err(e) => {
errors.push((e, p));
(declared_m, default_v)
},
Ok(val) => {
// Last value check:
// Check if value is consistent with macro
match val.consistent_with_macro(p_v.clone(), &m) {
Ok(()) => (declared_m, val),
Err(e) => {
errors.push((e, p));
(declared_m, default_v)
},
}
}
}
}

}
};

output.insert(avmacro, avvalue);
}

// User defined macros which were not found in our declaration.
let not_found : Vec<_> = defined
.enumerate()
.filter(|(i, _)| !found_macros.contains(i))
.map(|(_, e)| e)
.collect();

for (m, _, p1, _) in not_found {
errors.push(
(
Box::new(
MacroNotFound(p1.clone(), m.identifier(), Self::path())
) as Box<dyn TraceableError>,
p1
)
)
}

errors
.sort_by(|(a, _), (b, _)|
compare_errors(a, b).unwrap()
);

for (err, _) in errors {
println!("{}", err);
}

output
}
}

#[AvError(TraceableError, CONFIG_MACRO_NOT_FOUND, "Config: Macro Not Found")]
pub struct MacroNotFound(pub Traceable, pub String, pub JSONPath);

impl TraceableError for MacroNotFound {
location!(&self.0);
description!(("The macro `{}` is not defined in this section (`{}`) -- we've used the default.", self.1.blue(), self.2));
}

#[AvError(TraceableError, CONFIG_MACRO_MISSING, "Config: Macro Missing")]
pub struct MacroMissing(pub Traceable, pub String, pub JSONPath);

impl TraceableError for MacroMissing {
location!(&self.0);
description!(("The macro `{}` wasn't found in {}.", self.1.blue(), self.2));
}
Loading