Skip to content

Commit

Permalink
finish plugin system simple implementation, writing docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Pistonight committed Oct 7, 2023
1 parent 0d5adbf commit 4e925e7
Show file tree
Hide file tree
Showing 25 changed files with 397 additions and 305 deletions.
2 changes: 1 addition & 1 deletion compiler-core/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ pub async fn compile(root_resource: &Resource, setting: &Setting) -> CompilerOut
let ms = metrics.comp_done();
info!("comp phase done in {ms}ms");

let comp_doc = run_plugins(comp_doc);
let comp_doc = run_plugins(comp_doc, &comp_meta.plugins).await;
let ms = metrics.plug_done();
info!("plug phase done in {ms}ms");

Expand Down
5 changes: 5 additions & 0 deletions compiler-core/src/comp/comp_doc.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use celerctypes::DocDiagnostic;
use celerctypes::{DocPoorText, RouteMetadata};
use serde::{Deserialize, Serialize};

Expand All @@ -21,6 +22,8 @@ pub struct CompDoc {
pub preface: Vec<Vec<DocPoorText>>,
/// The route
pub route: Vec<CompSection>,
/// Overall diagnostics (that don't apply to any line)
pub diagnostics: Vec<DocDiagnostic>,
}

impl Compiler {
Expand Down Expand Up @@ -50,6 +53,7 @@ impl Compiler {
project: self.project,
preface,
route: route_vec,
diagnostics: vec![],
},
self.meta,
))
Expand Down Expand Up @@ -118,6 +122,7 @@ impl Compiler {
route: vec![self.create_empty_section_for_error(errors).await],
project: self.project,
preface: vec![],
diagnostics: vec![],
},
self.meta,
)
Expand Down
1 change: 1 addition & 0 deletions compiler-core/src/comp/compiler_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ impl CompilerBuilder {
meta: CompilerMetadata {
presets: self.presets,
default_icon_priority: self.default_icon_priority,
..Default::default()
},
color: self.color,
coord: self.coord,
Expand Down
14 changes: 13 additions & 1 deletion compiler-core/src/exec/exec_doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ impl CompDoc {
project: self.project,
preface: self.preface,
route: sections,
diagnostics: self.diagnostics,
})
}
}
Expand All @@ -27,10 +28,11 @@ impl CompDoc {
mod test {
use celerctypes::{
DocPoorText, ExecLine, ExecMapSection, ExecSection, GameCoord, MapLine, MapMetadata,
RouteMetadata,
RouteMetadata, DocDiagnostic,
};

use crate::comp::{CompLine, CompMovement, CompSection};
use crate::lang::parse_poor;

use super::*;

Expand All @@ -44,15 +46,25 @@ mod test {

let test_preface = vec![vec![DocPoorText::Text("test".to_string())]];

let test_diagnostics = vec![
DocDiagnostic {
msg: parse_poor("test msg"),
msg_type: "test".to_string(),
source: "test".to_string(),
}
];

let test_doc = CompDoc {
project: test_metadata.clone(),
preface: test_preface.clone(),
diagnostics: test_diagnostics.clone(),
..Default::default()
};

let exec_doc = test_doc.exec().await.unwrap();
assert_eq!(exec_doc.project, test_metadata);
assert_eq!(exec_doc.preface, test_preface);
assert_eq!(exec_doc.diagnostics, test_diagnostics);
}

#[tokio::test]
Expand Down
9 changes: 6 additions & 3 deletions compiler-core/src/pack/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ pub enum PackerError {
#[error("Error when parsing structured data in file {0}: {1}")]
InvalidFormat(String, String),

#[error("Error when parsing file {0}: file is not UTF-8")]
InvalidUtf8(String),

#[error("")]
InvalidIcon,

Expand Down Expand Up @@ -106,6 +109,9 @@ pub enum PackerError {
)]
DuplicateMap(usize),

#[error("`{0}` is not a valid built-in plugin or reference to a plugin script")]
InvalidPlugin(String),

#[error("No map defined in project config")]
MissingMap,

Expand All @@ -115,9 +121,6 @@ pub enum PackerError {
#[error("{0}")]
NotImpl(String),

#[error("`{0}` is not a valid built-in plugin or reference to a plugin script")]
InvalidPlugin(String),

#[cfg(feature = "wasm")]
#[error("Wasm execution error: {0}")]
Wasm(#[from] WasmError),
Expand Down
195 changes: 107 additions & 88 deletions compiler-core/src/pack/pack_config.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use std::collections::HashMap;

use celerctypes::{DocTag, MapMetadata};
use serde_json::{Map, Value};
use serde_json::{Value, json};

use crate::api::Setting;
use crate::comp::prop;
use crate::json::{Cast, Coerce};
use crate::lang::Preset;
use crate::plug::{Plugin, PluginRuntime};
use crate::plug::{Plugin, PluginRuntime, BuiltInPlugin};
use crate::util::async_for;

use super::{pack_map, pack_presets, PackerError, PackerResult, Resource, Use, ValidUse};
Expand All @@ -30,18 +30,46 @@ pub async fn pack_config(
index: usize,
setting: &Setting,
) -> PackerResult<()> {
// Load and resolve top-level `use` properties
let config_value = match Use::from(config) {
Use::Invalid(path) => return Err(PackerError::InvalidUse(path)),
Use::NotUse(v) => v,
Use::Valid(valid_use) => load_config_from_use(project_resource, valid_use, index).await?,
};
match Use::try_from(config) {
Ok(Use::Invalid(path)) => return Err(PackerError::InvalidUse(path)),
Ok(Use::Valid(valid_use)) => {
// load a config from top-level use object
process_config_from_use(builder, project_resource, valid_use, index, setting).await
}
Err(v) => {
// load a config directly from the object
process_config(builder, project_resource, v, index, setting).await
}
}
}

/// Load a top-level `use`
async fn process_config_from_use(
builder: &mut ConfigBuilder,
project_resource: &Resource,
use_prop: ValidUse,
index: usize,
setting: &Setting,
) -> PackerResult<()> {
let config_resource = project_resource.resolve(&use_prop).await?;
let config = config_resource.load_structured().await?;
// process this config with the config resource context instead of the project context
// so `use`'s inside are resolved correctly
process_config(builder, &config_resource, config, index, setting).await
}

// Resolve `use`s inside the properties
let config_value = process_config(project_resource, config_value, index).await?;
/// Process a config, adding values to Builder and use the resource to resolve `use`'s
async fn process_config(
builder: &mut ConfigBuilder,
resource: &Resource,
config: Value,
index: usize,
setting: &Setting,
) -> PackerResult<()> {
let config = config.try_into_object().map_err(|_| PackerError::InvalidConfigType(index))?;

// add values to builder
async_for!((key, value) in config_value.into_iter(), {
async_for!((key, value) in config.into_iter(), {
match key.as_ref() {
prop::MAP => {
if builder.map.is_some() {
Expand All @@ -50,13 +78,7 @@ pub async fn pack_config(
builder.map = Some(pack_map(value, index).await?);
}
prop::ICONS => {
let icons = value.try_into_object().map_err(|_| PackerError::InvalidConfigProperty(index, prop::ICONS.to_string()))?;
async_for!((key, value) in icons.into_iter(), {
if value.is_array() || value.is_object() {
return Err(PackerError::InvalidConfigProperty(index, format!("{}.{}", prop::ICONS, key)));
}
builder.icons.insert(key, value.coerce_to_string());
})?;
process_icons_config(builder, resource, value, index).await?;
}
prop::TAGS => {
let tags = value.try_into_object().map_err(|_| PackerError::InvalidConfigProperty(index, prop::TAGS.to_string()))?;
Expand All @@ -75,103 +97,100 @@ pub async fn pack_config(
let priority = value.try_coerce_to_i64().ok_or_else(|| PackerError::InvalidConfigProperty(index, prop::DEFAULT_ICON_PRIORITY.to_string()))?;
builder.default_icon_priority = Some(priority);
}
prop::PLUGINS => {
process_plugins_config(builder, resource, value, index).await?;
}
_ => return Err(PackerError::UnusedConfigProperty(index, key)),
}
})?;

Ok(())
}

/// Load a top-level `use`
async fn load_config_from_use(
project_resource: &Resource,
use_prop: ValidUse,
index: usize,
) -> PackerResult<Value> {
let config_resource = project_resource.resolve(&use_prop).await?;
let config = config_resource.load_structured().await?;
// Calling process_config here
// because any `use` inside the config needs to be resolved by the config resource
// not the project resource
let config = process_config(&config_resource, config, index).await?;
Ok(Value::Object(config))
}

/// Process a config and resolve all `use`s inside
async fn process_config(
resource: &Resource,
config: Value,
index: usize,
) -> PackerResult<Map<String, Value>> {
let mut config_obj = match config {
Value::Object(obj) => obj,
_ => return Err(PackerError::InvalidConfigType(index)),
};

if let Some(icons) = config_obj.get_mut(prop::ICONS) {
process_icons_config(resource, icons).await?;
}


Ok(config_obj)
}

/// Process the `icons` property
///
/// Resolves `use`'s using the resource context and add the icon URLs to the builder
async fn process_icons_config(
builder: &mut ConfigBuilder,
resource: &Resource,
icons: &mut Value,
icons: Value,
index: usize,
) -> PackerResult<()> {
let icons = match icons.as_object_mut() {
Some(obj) => obj,
// just returning ok here
// the error will be caught later
_ => return Ok(()),
};

async_for!(value in icons.values_mut(), {
let v = value.take();
match Use::from(v) {
Use::Invalid(path) => return Err(PackerError::InvalidUse(path)),
Use::NotUse(v) => {
*value = v;
let icons = icons.try_into_object().map_err(|_| PackerError::InvalidConfigProperty(index, prop::ICONS.to_string()))?;

async_for!((key, v) in icons.into_iter(), {
match Use::try_from(v) {
Err(v) => {
// not a use, just a icon url
if v.is_array() || v.is_object() {
return Err(PackerError::InvalidConfigProperty(index, format!("{}.{}", prop::ICONS, key)));
}
builder.icons.insert(key, v.coerce_to_string());
}
Use::Valid(valid_use) => {
Ok(Use::Invalid(path)) => return Err(PackerError::InvalidUse(path)),
Ok(Use::Valid(valid_use)) => {
let icon_resource = resource.resolve(&valid_use).await?;
let image_url = icon_resource.load_image_url().await?;
*value = Value::String(image_url);
builder.icons.insert(key, image_url);
}
}
})?;

Ok(())
}

/// Process the `plugins` property
///
/// Resolves `use`'s using the resource context and add the plugins to the builder
async fn process_plugins_config(
builder: &mut ConfigBuilder,
resource: &Resource,
plugins: &mut Value,
plugins: Value,
index: usize,
) -> PackerResult<()> {
let plugins = match plugins.as_array_mut() {
Some(x) => x,
// just returning ok here
// the error will be caught later
_ => return Ok(()),
};

async_for!(value in plugins.iter_mut(), {
let v = value.take();
match Use::from(v) {
Use::Invalid(path) => return Err(PackerError::InvalidUse(path)),
Use::NotUse(v) => {
*value = v;
}
Use::Valid(valid_use) => {
let icon_resource = resource.resolve(&valid_use).await?;
let image_url = icon_resource.load_image_url().await?;
*value = Value::String(image_url);
let plugins = plugins.try_into_array().map_err(|_| PackerError::InvalidConfigProperty(index, prop::PLUGINS.to_string()))?;

async_for!((i, v) in plugins.into_iter().enumerate(), {
let v = v.try_into_object().map_err(|_| PackerError::InvalidConfigProperty(index, format!("{}[{}]", prop::PLUGINS, i)))?;
let mut plugin = None;
let mut props = json!(null);
async_for!((key, value) in v.into_iter(), {
match key.as_ref() {
prop::USE => {
let use_path_string = value.coerce_to_string();
plugin = match serde_json::from_value::<BuiltInPlugin>(value) {
Ok(built_in) => Some(Plugin::BuiltIn(built_in)),
Err(_) => {
// it's a script path, parse as use
match Use::from(use_path_string) {
Use::Invalid(path) => return Err(PackerError::InvalidPlugin(path)),
Use::Valid(valid_use) => {
// load the script
let script_resource = resource.resolve(&valid_use).await?;
let script = script_resource.load_utf8().await?;
Some(Plugin::Script(script))
}
}
}
};
}
prop::WITH => {
props = value;
}
_ => return Err(PackerError::UnusedConfigProperty(index, format!("{}[{}].{}", prop::PLUGINS, i, key))),
}
}
})?;
let plugin = match plugin {
Some(v) => v,
None => return Err(PackerError::MissingConfigProperty(index, format!("{}[{}].{}", prop::PLUGINS, i, prop::USE))),
};
builder.plugins.push(PluginRuntime {
plugin,
props,
});
})?;

todo!()
Ok(())
}

1 change: 1 addition & 0 deletions compiler-core/src/pack/pack_project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub async fn pack_project(

let compiler_metadata = CompilerMetadata {
presets: builder.presets,
plugins: builder.plugins,
default_icon_priority: builder.default_icon_priority.unwrap_or(2),
};

Expand Down
Loading

0 comments on commit 4e925e7

Please sign in to comment.