mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Add a Node.js wrapper library
The implementation is very similar to the Python wrapper.
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
use std::{
|
||||
path::Path,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use libloot::{error::DatabaseLockPoisonError, WriteMode};
|
||||
use libloot_ffi_errors::UnsupportedEnumValueError;
|
||||
use napi_derive::napi;
|
||||
|
||||
use crate::{
|
||||
error::VerboseError,
|
||||
metadata::{Group, Message, PluginMetadata},
|
||||
};
|
||||
|
||||
#[napi]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Database(Arc<RwLock<libloot::Database>>);
|
||||
|
||||
#[napi]
|
||||
impl Database {
|
||||
#[napi]
|
||||
pub fn load_masterlist(&self, path: String) -> Result<(), VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.load_masterlist(Path::new(&path))
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn load_masterlist_with_prelude(
|
||||
&self,
|
||||
masterlist_path: String,
|
||||
prelude_path: String,
|
||||
) -> Result<(), VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.load_masterlist_with_prelude(Path::new(&masterlist_path), Path::new(&prelude_path))
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn load_userlist(&self, path: String) -> Result<(), VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.load_userlist(Path::new(&path))
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn write_user_metadata(
|
||||
&self,
|
||||
output_path: String,
|
||||
overwrite: bool,
|
||||
) -> Result<(), VerboseError> {
|
||||
let write_mode = if overwrite {
|
||||
WriteMode::CreateOrTruncate
|
||||
} else {
|
||||
WriteMode::Create
|
||||
};
|
||||
|
||||
self.0
|
||||
.read()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.write_user_metadata(Path::new(&output_path), write_mode)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn write_minimal_list(
|
||||
&self,
|
||||
output_path: String,
|
||||
overwrite: bool,
|
||||
) -> Result<(), VerboseError> {
|
||||
let write_mode = if overwrite {
|
||||
WriteMode::CreateOrTruncate
|
||||
} else {
|
||||
WriteMode::Create
|
||||
};
|
||||
|
||||
self.0
|
||||
.read()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.write_minimal_list(Path::new(&output_path), write_mode)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn evaluate(&self, condition: String) -> Result<bool, VerboseError> {
|
||||
self.0
|
||||
.read()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.evaluate(&condition)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn known_bash_tags(&self) -> Result<Vec<String>, VerboseError> {
|
||||
Ok(self
|
||||
.0
|
||||
.read()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.known_bash_tags())
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn general_messages(
|
||||
&self,
|
||||
evaluate_conditions: bool,
|
||||
) -> Result<Vec<Message>, VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.general_messages(evaluate_conditions)
|
||||
.map(|v| v.into_iter().map(Into::into).collect())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn groups(&self, include_user_metadata: bool) -> Result<Vec<Group>, VerboseError> {
|
||||
Ok(self
|
||||
.0
|
||||
.read()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.groups(include_user_metadata)
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn user_groups(&self) -> Result<Vec<Group>, VerboseError> {
|
||||
Ok(self
|
||||
.0
|
||||
.read()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.user_groups()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Into::into)
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn set_user_groups(&self, groups: Vec<&Group>) -> Result<(), VerboseError> {
|
||||
let groups = groups.into_iter().cloned().map(Into::into).collect();
|
||||
self.0
|
||||
.write()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.set_user_groups(groups);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn groups_path(
|
||||
&self,
|
||||
from_group_name: String,
|
||||
to_group_name: String,
|
||||
) -> Result<Vec<Vertex>, VerboseError> {
|
||||
self.0
|
||||
.read()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.groups_path(&from_group_name, &to_group_name)
|
||||
.map(|v| v.into_iter().map(Into::into).collect())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn plugin_metadata(
|
||||
&self,
|
||||
plugin_name: String,
|
||||
include_user_metadata: bool,
|
||||
evaluate_conditions: bool,
|
||||
) -> Result<Option<PluginMetadata>, VerboseError> {
|
||||
self.0
|
||||
.read()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.plugin_metadata(&plugin_name, include_user_metadata, evaluate_conditions)
|
||||
.map(|p| p.map(Into::into))
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn plugin_user_metadata(
|
||||
&self,
|
||||
plugin_name: String,
|
||||
evaluate_conditions: bool,
|
||||
) -> Result<Option<PluginMetadata>, VerboseError> {
|
||||
self.0
|
||||
.read()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.plugin_user_metadata(&plugin_name, evaluate_conditions)
|
||||
.map(|p| p.map(Into::into))
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn set_plugin_user_metadata(
|
||||
&mut self,
|
||||
plugin_metadata: &PluginMetadata,
|
||||
) -> Result<(), VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.set_plugin_user_metadata(plugin_metadata.clone().into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn discard_plugin_user_metadata(&self, plugin: String) -> Result<(), VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.discard_plugin_user_metadata(&plugin);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn discard_all_user_metadata(&self) -> Result<(), VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(|_| DatabaseLockPoisonError)?
|
||||
.discard_all_user_metadata();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arc<RwLock<libloot::Database>>> for Database {
|
||||
fn from(value: Arc<RwLock<libloot::Database>>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct Vertex(libloot::Vertex);
|
||||
|
||||
#[napi]
|
||||
impl Vertex {
|
||||
#[napi(constructor)]
|
||||
pub fn new(name: String) -> Self {
|
||||
Self(libloot::Vertex::new(name))
|
||||
}
|
||||
|
||||
#[napi(getter)]
|
||||
pub fn name(&self) -> &str {
|
||||
self.0.name()
|
||||
}
|
||||
|
||||
#[napi(getter)]
|
||||
pub fn out_edge_type(&self) -> Result<Option<EdgeType>, VerboseError> {
|
||||
self.0
|
||||
.out_edge_type()
|
||||
.map(|e| e.try_into().map_err(Into::into))
|
||||
.transpose()
|
||||
}
|
||||
|
||||
#[napi(setter)]
|
||||
pub fn set_out_edge_type(&mut self, out_edge_type: EdgeType) {
|
||||
let out_edge_type = out_edge_type.into();
|
||||
self.0.set_out_edge_type(out_edge_type);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libloot::Vertex> for Vertex {
|
||||
fn from(value: libloot::Vertex) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vertex> for libloot::Vertex {
|
||||
fn from(value: Vertex) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum EdgeType {
|
||||
Hardcoded,
|
||||
MasterFlag,
|
||||
Master,
|
||||
MasterlistRequirement,
|
||||
UserRequirement,
|
||||
MasterlistLoadAfter,
|
||||
UserLoadAfter,
|
||||
MasterlistGroup,
|
||||
UserGroup,
|
||||
RecordOverlap,
|
||||
AssetOverlap,
|
||||
TieBreak,
|
||||
BlueprintMaster,
|
||||
}
|
||||
|
||||
impl TryFrom<libloot::EdgeType> for EdgeType {
|
||||
type Error = UnsupportedEnumValueError;
|
||||
|
||||
fn try_from(value: libloot::EdgeType) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
libloot::EdgeType::Hardcoded => Ok(EdgeType::Hardcoded),
|
||||
libloot::EdgeType::MasterFlag => Ok(EdgeType::MasterFlag),
|
||||
libloot::EdgeType::Master => Ok(EdgeType::Master),
|
||||
libloot::EdgeType::MasterlistRequirement => Ok(EdgeType::MasterlistRequirement),
|
||||
libloot::EdgeType::UserRequirement => Ok(EdgeType::UserRequirement),
|
||||
libloot::EdgeType::MasterlistLoadAfter => Ok(EdgeType::MasterlistLoadAfter),
|
||||
libloot::EdgeType::UserLoadAfter => Ok(EdgeType::UserLoadAfter),
|
||||
libloot::EdgeType::MasterlistGroup => Ok(EdgeType::MasterlistGroup),
|
||||
libloot::EdgeType::UserGroup => Ok(EdgeType::UserGroup),
|
||||
libloot::EdgeType::RecordOverlap => Ok(EdgeType::RecordOverlap),
|
||||
libloot::EdgeType::AssetOverlap => Ok(EdgeType::AssetOverlap),
|
||||
libloot::EdgeType::TieBreak => Ok(EdgeType::TieBreak),
|
||||
libloot::EdgeType::BlueprintMaster => Ok(EdgeType::BlueprintMaster),
|
||||
_ => Err(UnsupportedEnumValueError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EdgeType> for libloot::EdgeType {
|
||||
fn from(value: EdgeType) -> Self {
|
||||
match value {
|
||||
EdgeType::Hardcoded => libloot::EdgeType::Hardcoded,
|
||||
EdgeType::MasterFlag => libloot::EdgeType::MasterFlag,
|
||||
EdgeType::Master => libloot::EdgeType::Master,
|
||||
EdgeType::MasterlistRequirement => libloot::EdgeType::MasterlistRequirement,
|
||||
EdgeType::UserRequirement => libloot::EdgeType::UserRequirement,
|
||||
EdgeType::MasterlistLoadAfter => libloot::EdgeType::MasterlistLoadAfter,
|
||||
EdgeType::UserLoadAfter => libloot::EdgeType::UserLoadAfter,
|
||||
EdgeType::MasterlistGroup => libloot::EdgeType::MasterlistGroup,
|
||||
EdgeType::UserGroup => libloot::EdgeType::UserGroup,
|
||||
EdgeType::RecordOverlap => libloot::EdgeType::RecordOverlap,
|
||||
EdgeType::AssetOverlap => libloot::EdgeType::AssetOverlap,
|
||||
EdgeType::TieBreak => libloot::EdgeType::TieBreak,
|
||||
EdgeType::BlueprintMaster => libloot::EdgeType::BlueprintMaster,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use libloot::{
|
||||
error::{
|
||||
ConditionEvaluationError, DatabaseLockPoisonError, GameHandleCreationError,
|
||||
GroupsPathError, LoadOrderError, LoadOrderStateError, LoadPluginsError,
|
||||
MetadataRetrievalError, PluginDataError, SortPluginsError,
|
||||
},
|
||||
metadata::error::{
|
||||
LoadMetadataError, MultilingualMessageContentsError, RegexError, WriteMetadataError,
|
||||
},
|
||||
};
|
||||
use libloot_ffi_errors::{fmt_error_chain, SystemError, UnsupportedEnumValueError};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VerboseError(Box<dyn std::error::Error>);
|
||||
|
||||
impl std::fmt::Display for VerboseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
fmt_error_chain(self.0.as_ref(), f)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! box_from_error {
|
||||
( $from_type:ident, $to_type:ident ) => {
|
||||
impl From<$from_type> for $to_type {
|
||||
fn from(value: $from_type) -> Self {
|
||||
Self(Box::new(value))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
box_from_error!(GameHandleCreationError, VerboseError);
|
||||
box_from_error!(UnsupportedEnumValueError, VerboseError);
|
||||
box_from_error!(DatabaseLockPoisonError, VerboseError);
|
||||
box_from_error!(LoadPluginsError, VerboseError);
|
||||
box_from_error!(SortPluginsError, VerboseError);
|
||||
box_from_error!(LoadOrderStateError, VerboseError);
|
||||
box_from_error!(LoadOrderError, VerboseError);
|
||||
box_from_error!(LoadMetadataError, VerboseError);
|
||||
box_from_error!(WriteMetadataError, VerboseError);
|
||||
box_from_error!(ConditionEvaluationError, VerboseError);
|
||||
box_from_error!(GroupsPathError, VerboseError);
|
||||
box_from_error!(MetadataRetrievalError, VerboseError);
|
||||
box_from_error!(MultilingualMessageContentsError, VerboseError);
|
||||
box_from_error!(RegexError, VerboseError);
|
||||
|
||||
impl From<PluginDataError> for VerboseError {
|
||||
fn from(value: PluginDataError) -> Self {
|
||||
Self(Box::new(SystemError::from(value)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VerboseError> for napi::Error {
|
||||
fn from(value: VerboseError) -> Self {
|
||||
napi::Error::from_reason(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VerboseError> for napi::JsError {
|
||||
fn from(value: VerboseError) -> Self {
|
||||
napi::JsError::from(napi::Error::from(value))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
use std::path::Path;
|
||||
|
||||
use libloot_ffi_errors::UnsupportedEnumValueError;
|
||||
use napi_derive::napi;
|
||||
|
||||
use crate::{database::Database, error::VerboseError, plugin::Plugin};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[napi]
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum GameType {
|
||||
tes4,
|
||||
tes5,
|
||||
fo3,
|
||||
fonv,
|
||||
fo4,
|
||||
tes5se,
|
||||
fo4vr,
|
||||
tes5vr,
|
||||
tes3,
|
||||
starfield,
|
||||
openmw,
|
||||
}
|
||||
|
||||
impl TryFrom<libloot::GameType> for GameType {
|
||||
type Error = UnsupportedEnumValueError;
|
||||
|
||||
fn try_from(value: libloot::GameType) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
libloot::GameType::TES4 => Ok(GameType::tes4),
|
||||
libloot::GameType::TES5 => Ok(GameType::tes5),
|
||||
libloot::GameType::FO3 => Ok(GameType::fo3),
|
||||
libloot::GameType::FONV => Ok(GameType::fonv),
|
||||
libloot::GameType::FO4 => Ok(GameType::fo4),
|
||||
libloot::GameType::TES5SE => Ok(GameType::tes5se),
|
||||
libloot::GameType::FO4VR => Ok(GameType::fo4vr),
|
||||
libloot::GameType::TES5VR => Ok(GameType::tes5vr),
|
||||
libloot::GameType::TES3 => Ok(GameType::tes3),
|
||||
libloot::GameType::Starfield => Ok(GameType::starfield),
|
||||
libloot::GameType::OpenMW => Ok(GameType::openmw),
|
||||
_ => Err(UnsupportedEnumValueError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GameType> for libloot::GameType {
|
||||
fn from(value: GameType) -> Self {
|
||||
match value {
|
||||
GameType::tes4 => libloot::GameType::TES4,
|
||||
GameType::tes5 => libloot::GameType::TES5,
|
||||
GameType::fo3 => libloot::GameType::FO3,
|
||||
GameType::fonv => libloot::GameType::FONV,
|
||||
GameType::fo4 => libloot::GameType::FO4,
|
||||
GameType::tes5se => libloot::GameType::TES5SE,
|
||||
GameType::fo4vr => libloot::GameType::FO4VR,
|
||||
GameType::tes5vr => libloot::GameType::TES5VR,
|
||||
GameType::tes3 => libloot::GameType::TES3,
|
||||
GameType::starfield => libloot::GameType::Starfield,
|
||||
GameType::openmw => libloot::GameType::OpenMW,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
#[derive(Debug)]
|
||||
pub struct Game(libloot::Game);
|
||||
|
||||
#[napi]
|
||||
impl Game {
|
||||
#[napi(constructor)]
|
||||
pub fn new(
|
||||
game_type: GameType,
|
||||
game_path: String,
|
||||
local_path: Option<String>,
|
||||
) -> Result<Self, VerboseError> {
|
||||
match local_path {
|
||||
Some(local_path) => Ok(Game(libloot::Game::with_local_path(
|
||||
game_type.into(),
|
||||
Path::new(&game_path),
|
||||
Path::new(&local_path),
|
||||
)?)),
|
||||
None => Ok(Game(libloot::Game::new(
|
||||
game_type.into(),
|
||||
Path::new(&game_path),
|
||||
)?)),
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn game_type(&self) -> Result<GameType, VerboseError> {
|
||||
self.0.game_type().try_into().map_err(Into::into)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn additional_data_paths(&self) -> Vec<String> {
|
||||
self.0
|
||||
.additional_data_paths()
|
||||
.iter()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn set_additional_data_paths(&mut self, paths: Vec<String>) -> Result<(), VerboseError> {
|
||||
self.0.set_additional_data_paths(&as_paths(&paths))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn database(&self) -> Database {
|
||||
self.0.database().into()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn is_valid_plugin(&self, plugin_path: String) -> bool {
|
||||
self.0.is_valid_plugin(Path::new(&plugin_path))
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn load_plugins(&mut self, plugin_paths: Vec<String>) -> Result<(), VerboseError> {
|
||||
self.0.load_plugins(&as_paths(&plugin_paths))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn load_plugin_headers(&mut self, plugin_paths: Vec<String>) -> Result<(), VerboseError> {
|
||||
self.0.load_plugin_headers(&as_paths(&plugin_paths))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn clear_loaded_plugins(&mut self) {
|
||||
self.0.clear_loaded_plugins();
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn plugin(&self, plugin_name: String) -> Option<Plugin> {
|
||||
self.0.plugin(&plugin_name).map(Into::into)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn loaded_plugins(&self) -> Vec<Plugin> {
|
||||
self.0
|
||||
.loaded_plugins()
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn sort_plugins(&self, plugin_names: Vec<String>) -> Result<Vec<String>, VerboseError> {
|
||||
Ok(self.0.sort_plugins(&as_strs(&plugin_names))?)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn load_current_load_order_state(&mut self) -> Result<(), VerboseError> {
|
||||
self.0.load_current_load_order_state()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn is_load_order_ambiguous(&self) -> Result<bool, VerboseError> {
|
||||
Ok(self.0.is_load_order_ambiguous()?)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn active_plugins_file_path(&self) -> String {
|
||||
self.0
|
||||
.active_plugins_file_path()
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn is_plugin_active(&self, plugin_name: String) -> bool {
|
||||
self.0.is_plugin_active(&plugin_name)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn load_order(&self) -> Vec<&str> {
|
||||
self.0.load_order()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn set_load_order(&mut self, load_order: Vec<String>) -> Result<(), VerboseError> {
|
||||
self.0.set_load_order(&as_strs(&load_order))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn as_paths(pathbufs: &[String]) -> Vec<&Path> {
|
||||
pathbufs.iter().map(Path::new).collect()
|
||||
}
|
||||
|
||||
fn as_strs(strings: &[String]) -> Vec<&str> {
|
||||
strings.iter().map(String::as_ref).collect()
|
||||
}
|
||||
+100
-4
@@ -1,9 +1,105 @@
|
||||
#![deny(clippy::all)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate napi_derive;
|
||||
mod database;
|
||||
mod error;
|
||||
mod game;
|
||||
mod metadata;
|
||||
mod plugin;
|
||||
|
||||
use napi::{
|
||||
threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode},
|
||||
Either,
|
||||
};
|
||||
use napi_derive::napi;
|
||||
|
||||
pub use metadata::select_message_content;
|
||||
|
||||
#[napi]
|
||||
pub fn sum(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
pub fn is_compatible(major: u32, minor: u32, patch: u32) -> bool {
|
||||
libloot::is_compatible(major, minor, patch)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn libloot_revision() -> String {
|
||||
libloot::libloot_revision()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn libloot_version() -> String {
|
||||
libloot::libloot_version()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub const LIBLOOT_VERSION_MAJOR: u32 = libloot::LIBLOOT_VERSION_MAJOR;
|
||||
|
||||
#[napi]
|
||||
pub const LIBLOOT_VERSION_MINOR: u32 = libloot::LIBLOOT_VERSION_MINOR;
|
||||
|
||||
#[napi]
|
||||
pub const LIBLOOT_VERSION_PATCH: u32 = libloot::LIBLOOT_VERSION_PATCH;
|
||||
|
||||
#[napi]
|
||||
pub enum LogLevel {
|
||||
Trace,
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
Fatal,
|
||||
}
|
||||
|
||||
impl From<LogLevel> for libloot::LogLevel {
|
||||
fn from(value: LogLevel) -> Self {
|
||||
match value {
|
||||
LogLevel::Trace => libloot::LogLevel::Trace,
|
||||
LogLevel::Debug => libloot::LogLevel::Debug,
|
||||
LogLevel::Info => libloot::LogLevel::Info,
|
||||
LogLevel::Warning => libloot::LogLevel::Warning,
|
||||
LogLevel::Error => libloot::LogLevel::Error,
|
||||
LogLevel::Fatal => libloot::LogLevel::Fatal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libloot::LogLevel> for LogLevel {
|
||||
fn from(value: libloot::LogLevel) -> Self {
|
||||
match value {
|
||||
libloot::LogLevel::Trace => LogLevel::Trace,
|
||||
libloot::LogLevel::Debug => LogLevel::Debug,
|
||||
libloot::LogLevel::Info => LogLevel::Info,
|
||||
libloot::LogLevel::Warning => LogLevel::Warning,
|
||||
libloot::LogLevel::Error => LogLevel::Error,
|
||||
libloot::LogLevel::Fatal => LogLevel::Fatal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn set_log_level(level: LogLevel) {
|
||||
libloot::set_log_level(level.into());
|
||||
}
|
||||
|
||||
#[napi(ts_args_type = "callback: (logLevel: LogLevel, message: string) => void")]
|
||||
pub fn set_logging_callback(callback: napi::JsFunction) -> napi::Result<()> {
|
||||
let thread_safe_callback: ThreadsafeFunction<
|
||||
(libloot::LogLevel, String),
|
||||
ErrorStrategy::Fatal,
|
||||
> = callback.create_threadsafe_function(0, |ctx| {
|
||||
let (level, message): (libloot::LogLevel, String) = ctx.value;
|
||||
|
||||
Ok(vec![
|
||||
Either::A::<LogLevel, _>(level.into()),
|
||||
Either::B(message),
|
||||
])
|
||||
})?;
|
||||
|
||||
let rust_callback = move |level: libloot::LogLevel, message: &str| {
|
||||
thread_safe_callback.call(
|
||||
(level, message.to_owned()),
|
||||
ThreadsafeFunctionCallMode::Blocking,
|
||||
);
|
||||
};
|
||||
|
||||
libloot::set_logging_callback(rust_callback);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,104 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use napi_derive::napi;
|
||||
|
||||
use crate::error::VerboseError;
|
||||
|
||||
#[napi]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct Plugin(Arc<libloot::Plugin>);
|
||||
|
||||
#[napi]
|
||||
impl Plugin {
|
||||
#[napi]
|
||||
pub fn name(&self) -> &str {
|
||||
self.0.name()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn header_version(&self) -> Option<f32> {
|
||||
self.0.header_version()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn version(&self) -> Option<&str> {
|
||||
self.0.version()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn masters(&self) -> Result<Vec<String>, VerboseError> {
|
||||
Ok(self.0.masters()?)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn bash_tags(&self) -> Vec<String> {
|
||||
self.0.bash_tags().to_vec()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn crc(&self) -> Option<u32> {
|
||||
self.0.crc()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn is_master(&self) -> bool {
|
||||
self.0.is_master()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn is_light_plugin(&self) -> bool {
|
||||
self.0.is_light_plugin()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn is_medium_plugin(&self) -> bool {
|
||||
self.0.is_medium_plugin()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn is_update_plugin(&self) -> bool {
|
||||
self.0.is_update_plugin()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn is_blueprint_plugin(&self) -> bool {
|
||||
self.0.is_blueprint_plugin()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn is_valid_as_light_plugin(&self) -> Result<bool, VerboseError> {
|
||||
Ok(self.0.is_valid_as_light_plugin()?)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn is_valid_as_medium_plugin(&self) -> Result<bool, VerboseError> {
|
||||
Ok(self.0.is_valid_as_medium_plugin()?)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn is_valid_as_update_plugin(&self) -> Result<bool, VerboseError> {
|
||||
Ok(self.0.is_valid_as_update_plugin()?)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn loads_archive(&self) -> bool {
|
||||
self.0.loads_archive()
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn do_records_overlap(&self, plugin: &Plugin) -> Result<bool, VerboseError> {
|
||||
Ok(self.0.do_records_overlap(&plugin.0)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arc<libloot::Plugin>> for Plugin {
|
||||
fn from(value: Arc<libloot::Plugin>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user