use std::{
collections::HashMap,
fmt::{Display, Formatter},
path::PathBuf,
};
#[cfg(feature = "python_bindings")]
use pyo3::{
prelude::*,
types::{PyDict, PyList},
};
use serde::Deserialize;
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(
feature = "python_bindings",
pyclass(frozen, get_all, module = "decomp_settings")
)]
pub struct DecompmeOpts {
pub preset: usize,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(
feature = "python_bindings",
pyclass(frozen, get_all, module = "decomp_settings")
)]
pub struct PermuterOpts {
pub decompme_compilers: HashMap<String, String>,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(
feature = "python_bindings",
pyclass(frozen, get_all, module = "decomp_settings")
)]
pub struct FrogressVersionOpts {
pub version: String,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(
feature = "python_bindings",
pyclass(frozen, get_all, module = "decomp_settings")
)]
pub struct FrogressOpts {
pub project: String,
pub versions: HashMap<String, FrogressVersionOpts>,
}
#[derive(Clone, Debug, Deserialize)]
#[cfg_attr(
feature = "python_bindings",
pyclass(frozen, module = "decomp_settings")
)]
pub struct AnyOpts(serde_yaml::Value);
impl AnyOpts {
pub fn into_inner(self) -> serde_yaml::Value {
self.0
}
}
#[cfg_attr(feature = "python_bindings", pymethods)]
impl ToolOpts {
#[cfg(feature = "python_bindings")]
pub fn raw(&self, py: Python<'_>) -> PyResult<PyObject> {
match self {
ToolOpts::Other(x) => Ok(x.into_pyobject(py)?.unbind()),
_ => Ok(py.None()),
}
}
}
#[cfg(feature = "python_bindings")]
impl<'py> IntoPyObject<'py> for &AnyOpts {
type Target = PyAny;
type Output = Bound<'py, PyAny>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
let py_object: PyObject = value_to_object(&self.0, py)?;
Ok(py_object.into_bound(py))
}
}
#[cfg(feature = "python_bindings")]
fn value_to_object(val: &serde_yaml::Value, py: Python<'_>) -> PyResult<PyObject> {
use pyo3::IntoPyObjectExt;
match val {
serde_yaml::Value::Null => Ok(py.None()),
serde_yaml::Value::Bool(b) => b.into_py_any(py),
serde_yaml::Value::Number(n) => {
if let Some(i) = n.as_i64() {
i.into_py_any(py)
} else if let Some(u) = n.as_u64() {
u.into_py_any(py)
} else if let Some(f) = n.as_f64() {
f.into_py_any(py)
} else {
Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
"Failed to convert YAML number to Python number",
))
}
}
serde_yaml::Value::String(s) => s.into_py_any(py),
serde_yaml::Value::Sequence(seq) => {
let list = PyList::empty(py);
for item_val in seq {
list.append(value_to_object(item_val, py)?)?;
}
Ok(list.into())
}
serde_yaml::Value::Mapping(map) => {
let dict = PyDict::new(py);
for (key_val, value_val) in map {
let py_key = match key_val.as_str() {
Some(s) => s.into_py_any(py),
None => value_to_object(key_val, py),
}?;
let py_value = value_to_object(value_val, py)?;
dict.set_item(py_key, py_value)?;
}
dict.into_py_any(py)
}
serde_yaml::Value::Tagged(tagged_value) => value_to_object(&tagged_value.value, py),
}
}
#[derive(Clone, Debug, Deserialize)]
#[cfg_attr(
feature = "python_bindings",
pyclass(frozen, get_all, module = "decomp_settings")
)]
#[serde(untagged)]
pub enum ToolOpts {
Decompme(DecompmeOpts),
Permuter(PermuterOpts),
Frogress(FrogressOpts),
Other(AnyOpts),
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(
feature = "python_bindings",
pyclass(frozen, get_all, module = "decomp_settings")
)]
pub struct Version {
pub name: String,
pub fullname: String,
pub sha1: Option<String>,
pub paths: VersionPaths,
}
impl Display for Version {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self.fullname,))
}
}
#[derive(Clone, Debug, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(
feature = "python_bindings",
pyclass(frozen, get_all, module = "decomp_settings")
)]
pub struct VersionPaths {
pub target: PathBuf,
pub build_dir: PathBuf,
pub map: PathBuf,
pub compiled_target: PathBuf,
pub elf: Option<PathBuf>,
pub expected_dir: Option<PathBuf>,
pub asm: Option<PathBuf>,
pub nonmatchings: Option<PathBuf>,
pub compressed_target: Option<PathBuf>,
pub compressed_compiled_target: Option<PathBuf>,
}
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(
feature = "python_bindings",
pyclass(frozen, get_all, module = "decomp_settings")
)]
pub struct Config {
pub name: String,
pub repo: Option<String>,
pub website: Option<String>,
pub discord: Option<String>,
pub platform: String, pub build_system: Option<String>, pub versions: Vec<Version>,
pub tools: Option<HashMap<String, ToolOpts>>,
}
#[cfg_attr(feature = "python_bindings", pymethods)]
impl Config {
pub fn get_version_by_name(&self, version: &str) -> Option<Version> {
self.versions.iter().find(|v| v.name == version).cloned()
}
}
impl Display for Config {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self.name))
}
}