🚧 wip: is somebody gonna match my tweaks
Signed-off-by: Pauline <git@ethanlibs.co>
This commit is contained in:
parent
bfaa6ceb3a
commit
5f64f4fed1
69 changed files with 1000 additions and 662 deletions
62
Cargo.lock
generated
62
Cargo.lock
generated
|
|
@ -10,9 +10,9 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
|||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.22.0"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
||||
checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
|
@ -458,17 +458,17 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
|||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.73"
|
||||
version = "0.3.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide 0.7.4",
|
||||
"miniz_oxide 0.8.0",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1870,9 +1870,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "3.2.0"
|
||||
version = "3.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b"
|
||||
checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
|
|
@ -2404,9 +2404,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||
checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64"
|
||||
|
||||
[[package]]
|
||||
name = "gio"
|
||||
|
|
@ -2904,9 +2904,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.7"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
|
||||
checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
|
|
@ -4350,7 +4350,6 @@ name = "onelauncher"
|
|||
version = "1.0.0-alpha.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compression",
|
||||
"async-recursion",
|
||||
"async-tungstenite",
|
||||
"async_zip",
|
||||
|
|
@ -4360,19 +4359,18 @@ dependencies = [
|
|||
"chrono",
|
||||
"dirs 5.0.1",
|
||||
"discord-rich-presence",
|
||||
"dunce",
|
||||
"flate2",
|
||||
"futures",
|
||||
"indicatif",
|
||||
"interpulse",
|
||||
"iota_stronghold",
|
||||
"lazy_static",
|
||||
"nom",
|
||||
"notify",
|
||||
"notify-debouncer-mini",
|
||||
"onelauncher_gamemode",
|
||||
"onelauncher_macros",
|
||||
"onelauncher_prisma",
|
||||
"onelauncher_utils",
|
||||
"p256",
|
||||
"paste",
|
||||
"rand 0.8.5",
|
||||
|
|
@ -4384,7 +4382,6 @@ dependencies = [
|
|||
"sha1_smol",
|
||||
"sha2 0.10.8",
|
||||
"specta 2.0.0-rc.20",
|
||||
"sys-info",
|
||||
"sysinfo",
|
||||
"tauri",
|
||||
"tauri-specta",
|
||||
|
|
@ -4483,6 +4480,31 @@ dependencies = [
|
|||
"webbrowser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "onelauncher_utils"
|
||||
version = "1.0.0-alpha.1"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-compression",
|
||||
"chrono",
|
||||
"dunce",
|
||||
"futures",
|
||||
"interpulse",
|
||||
"nom",
|
||||
"onelauncher_prisma",
|
||||
"pin-project-lite",
|
||||
"prisma-client-rust",
|
||||
"regex",
|
||||
"serde",
|
||||
"sys-info",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uuid 1.10.0",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.1"
|
||||
|
|
@ -6002,9 +6024,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.12"
|
||||
version = "0.23.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
|
||||
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
|
|
@ -6041,9 +6063,9 @@ checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
|
|||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.7"
|
||||
version = "0.102.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56"
|
||||
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ members = [
|
|||
"packages/macros", # onelauncher_macros -- utility procedural macros for onelauncher
|
||||
"packages/gamemode", # onelauncher_gamemode -- rust wrapper for feral gamemode
|
||||
"packages/prisma-cli", # onelauncher_prisma_cli -- rust wrapper for prisma cli
|
||||
"packages/prisma" # onelauncher_prisma -- rust client for prisma
|
||||
"packages/prisma", # onelauncher_prisma -- rust client for prisma
|
||||
"packages/utils" # onelauncher_utils -- standard utilities for use within all crates
|
||||
]
|
||||
|
||||
[workspace.package] # sets default [package] settings for workspace members
|
||||
|
|
@ -31,6 +32,7 @@ onelauncher_test = { path = "./apps/testing" }
|
|||
onelauncher_gamemode = { path = "./packages/gamemode" }
|
||||
onelauncher_prisma = { path = "./packages/prisma" }
|
||||
onelauncher_prisma_cli = { path = "./packages/prisma-cli" }
|
||||
onelauncher_utils = { path = "./packages/utils" }
|
||||
|
||||
# # ============= TAURI ============= # #
|
||||
# tauri uses latest crates.io rc channel
|
||||
|
|
@ -158,6 +160,8 @@ sha2 = { version = "0.10" }
|
|||
bytes = { version = "1.7" }
|
||||
|
||||
# # ============= UTILITY ============= # #
|
||||
async-channel = { version = "2.3" }
|
||||
pin-project-lite = { version = "0.2" }
|
||||
once_cell = { version = "1.19" }
|
||||
quote = { version = "1.0" }
|
||||
syn = { version = "2.0", features = [ "full" ] }
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { ChevronDownIcon, Download01Icon, HeartIcon } from '@untitled-theme/icons-solid';
|
||||
import { For, Match, Show, Switch, createSignal } from 'solid-js';
|
||||
import useBrowser from '~ui/hooks/useBrowser';
|
||||
import useSettings from '~ui/hooks/useSettings';
|
||||
import { abbreviateNumber } from '~utils';
|
||||
import { createSignal, For, Match, Show, Switch } from 'solid-js';
|
||||
import type { Providers, SearchResult } from '@onelauncher/client/bindings';
|
||||
import type { JSX } from 'solid-js';
|
||||
import Button from '../base/Button';
|
||||
import { abbreviateNumber } from '~utils';
|
||||
import useSettings from '~ui/hooks/useSettings';
|
||||
import useBrowser from '~ui/hooks/useBrowser';
|
||||
|
||||
interface SearchResultsContainerProps {
|
||||
provider: Providers;
|
||||
|
|
|
|||
|
|
@ -6,19 +6,19 @@ import {
|
|||
RefreshCw01Icon,
|
||||
SearchMdIcon,
|
||||
} from '@untitled-theme/icons-solid';
|
||||
import { For, Show, onMount } from 'solid-js';
|
||||
import type { Cluster } from '@onelauncher/client/bindings';
|
||||
import BannerBackground from '../../assets/images/header.png';
|
||||
import Button from '../components/base/Button';
|
||||
import TextField from '../components/base/TextField';
|
||||
import ClusterRoot from './cluster/ClusterRoot';
|
||||
import { bridge } from '~imports';
|
||||
|
||||
import ClusterCover from '~ui/components/game/ClusterCover';
|
||||
import { useClusterCreator } from '~ui/components/overlay/cluster/ClusterCreationModal';
|
||||
import { useLaunchCluster, useRecentCluster } from '~ui/hooks/useCluster';
|
||||
import useCommand from '~ui/hooks/useCommand';
|
||||
import { formatAsDuration, upperFirst } from '~utils';
|
||||
import { For, onMount, Show } from 'solid-js';
|
||||
|
||||
import type { Cluster } from '@onelauncher/client/bindings';
|
||||
import BannerBackground from '../../assets/images/header.png';
|
||||
import Button from '../components/base/Button';
|
||||
import TextField from '../components/base/TextField';
|
||||
import ClusterRoot from './cluster/ClusterRoot';
|
||||
|
||||
type GroupedClusters = Record<string, Cluster[]>;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { useBeforeLeave } from '@solidjs/router';
|
||||
import { type Accessor, Show } from 'solid-js';
|
||||
import type { Cluster } from '@onelauncher/client/bindings';
|
||||
import { GameSettings, JvmSettings, LauncherSettings, ProcessSettings, createSetting } from '../settings/game/SettingsMinecraft';
|
||||
import { bridge } from '~imports';
|
||||
import ScrollableContainer from '~ui/components/ScrollableContainer';
|
||||
import Sidebar from '~ui/components/Sidebar';
|
||||
import useClusterContext from '~ui/hooks/useCluster';
|
||||
import { tryResult } from '~ui/hooks/useCommand';
|
||||
import useSettings from '~ui/hooks/useSettings';
|
||||
import { type Accessor, Show } from 'solid-js';
|
||||
import type { Cluster } from '@onelauncher/client/bindings';
|
||||
import { createSetting, GameSettings, JvmSettings, LauncherSettings, ProcessSettings } from '../settings/game/SettingsMinecraft';
|
||||
|
||||
function ClusterSettings() {
|
||||
const [cluster] = useClusterContext();
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { InfoCircleIcon, Trash01Icon, UserPlus02Icon } from '@untitled-theme/icons-solid';
|
||||
import { For, Match, Show, Switch, createSignal, mergeProps } from 'solid-js';
|
||||
import Button from '~ui/components/base/Button';
|
||||
import Tooltip from '~ui/components/base/Tooltip';
|
||||
import PlayerHead from '~ui/components/game/PlayerHead';
|
||||
import useAccountController from '~ui/components/overlay/account/AddAccountModal';
|
||||
import ScrollableContainer from '~ui/components/ScrollableContainer';
|
||||
import Sidebar from '~ui/components/Sidebar';
|
||||
import { createSignal, For, Match, mergeProps, Show, Switch } from 'solid-js';
|
||||
|
||||
function SettingsAccounts() {
|
||||
const controller = useAccountController();
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { ActivityIcon, CpuChip01Icon, Database01Icon, EyeIcon, FilePlus02Icon, FileX02Icon, LayoutTopIcon, Maximize01Icon, ParagraphWrapIcon, VariableIcon, XIcon } from '@untitled-theme/icons-solid';
|
||||
import { type Accessor, For, type Setter, Show, createSignal, onMount, splitProps, untrack } from 'solid-js';
|
||||
import type { JavaVersion, JavaVersions, Memory, Resolution } from '@onelauncher/client/bindings';
|
||||
import Button from '~ui/components/base/Button';
|
||||
import TextField from '~ui/components/base/TextField';
|
||||
import Toggle from '~ui/components/base/Toggle';
|
||||
import Modal, { createModal } from '~ui/components/overlay/Modal';
|
||||
import ScrollableContainer from '~ui/components/ScrollableContainer';
|
||||
import BaseSettingsRow, { type SettingsRowProps } from '~ui/components/SettingsRow';
|
||||
import Sidebar from '~ui/components/Sidebar';
|
||||
import useSettings from '~ui/hooks/useSettings';
|
||||
import { asEnvVariables } from '~utils';
|
||||
import Modal, { createModal } from '~ui/components/overlay/Modal';
|
||||
import Button from '~ui/components/base/Button';
|
||||
import { type Accessor, createSignal, For, onMount, type Setter, Show, splitProps, untrack } from 'solid-js';
|
||||
import type { JavaVersion, JavaVersions, Memory, Resolution } from '@onelauncher/client/bindings';
|
||||
|
||||
function SettingsMinecraft() {
|
||||
return (
|
||||
|
|
@ -300,14 +300,14 @@ export function JvmSettings(props: {
|
|||
}) {
|
||||
const modal = createModal(controller => (
|
||||
<Modal.Simple
|
||||
title="Java Versions"
|
||||
buttons={[
|
||||
<Button
|
||||
onClick={controller.hide}
|
||||
buttonStyle="secondary"
|
||||
children="Close"
|
||||
onClick={controller.hide}
|
||||
/>,
|
||||
]}
|
||||
title="Java Versions"
|
||||
{...controller}
|
||||
>
|
||||
<div class="grid grid-cols-[80px_1fr] items-center gap-y-2">
|
||||
|
|
@ -318,10 +318,10 @@ export function JvmSettings(props: {
|
|||
<>
|
||||
<span class="capitalize">{prettified}</span>
|
||||
<TextField
|
||||
value={meta.path}
|
||||
onValidSubmit={(value) => {
|
||||
props.javaVersions?.set({ ...props.javaVersions.get(), [version]: { ...meta, path: value } });
|
||||
}}
|
||||
value={meta.path}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
@ -343,9 +343,9 @@ export function JvmSettings(props: {
|
|||
title="Version"
|
||||
>
|
||||
<Button
|
||||
onClick={modal.show}
|
||||
iconLeft={<Database01Icon />}
|
||||
children="Configure"
|
||||
iconLeft={<Database01Icon />}
|
||||
onClick={modal.show}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { useBeforeLeave } from '@solidjs/router';
|
||||
import { PackageIcon, Speedometer04Icon } from '@untitled-theme/icons-solid';
|
||||
import { For, createSignal } from 'solid-js';
|
||||
import SettingsRow from '../../../components/SettingsRow';
|
||||
import Dropdown from '~ui/components/base/Dropdown';
|
||||
import Toggle from '~ui/components/base/Toggle';
|
||||
import ScrollableContainer from '~ui/components/ScrollableContainer';
|
||||
import Sidebar from '~ui/components/Sidebar';
|
||||
import useSettings from '~ui/hooks/useSettings';
|
||||
import { BROWSER_VIEWS } from '~utils/browser';
|
||||
import { upperFirst } from '~utils';
|
||||
import { BROWSER_VIEWS } from '~utils/browser';
|
||||
import { createSignal, For } from 'solid-js';
|
||||
import SettingsRow from '../../../components/SettingsRow';
|
||||
|
||||
function SettingsAppearance() {
|
||||
const { settings, saveOnLeave } = useSettings();
|
||||
|
|
|
|||
|
|
@ -1,17 +1,4 @@
|
|||
import hljs from 'highlight.js/lib/core';
|
||||
// import javascript from 'highlight.js/lib/languages/javascript';
|
||||
// import python from 'highlight.js/lib/languages/python';
|
||||
// import lua from 'highlight.js/lib/languages/lua';
|
||||
// import java from 'highlight.js/lib/languages/java';
|
||||
// import kotlin from 'highlight.js/lib/languages/kotlin';
|
||||
// import scala from 'highlight.js/lib/languages/scala';
|
||||
// import groovy from 'highlight.js/lib/languages/groovy';
|
||||
// import gradle from 'highlight.js/lib/languages/gradle';
|
||||
// import json from 'highlight.js/lib/languages/json';
|
||||
// import ini from 'highlight.js/lib/languages/ini';
|
||||
// import yaml from 'highlight.js/lib/languages/yaml';
|
||||
// import xml from 'highlight.js/lib/languages/xml';
|
||||
// import properties from 'highlight.js/lib/languages/properties';
|
||||
import { configuredXss, md, noop } from './markdown';
|
||||
|
||||
function langFactory(lang: string) {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ cli = [ "dep:indicatif" ]
|
|||
# features[default]
|
||||
onelauncher_macros = { workspace = true }
|
||||
onelauncher_prisma = { workspace = true }
|
||||
onelauncher_utils = { workspace = true }
|
||||
interpulse = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
|
|
@ -50,7 +51,6 @@ async_zip = { workspace = true }
|
|||
discord-rich-presence = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
sysinfo = { workspace = true }
|
||||
sys-info = { workspace = true }
|
||||
whoami = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
async-tungstenite = { workspace = true }
|
||||
|
|
@ -58,15 +58,12 @@ async-recursion = { workspace = true }
|
|||
tokio-stream = { workspace = true }
|
||||
bytes = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
dunce = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
p256 = { workspace = true }
|
||||
byteorder = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
async-compression = { workspace = true }
|
||||
flate2 = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
nom = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
||||
# features[tauri]
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ generator client {
|
|||
model Settings {
|
||||
/// The unique ID associated with this Settings instance.
|
||||
id Int @id @unique @default(0)
|
||||
/// A OneLauncher theme managed by the core GUI.
|
||||
/// A OneLauncher theme managed by the core GUI. (system, light, dark, oled, mocha)
|
||||
theme String @default("dark")
|
||||
/// Whether or not ask for confirmation when closing the core GUI.
|
||||
hide_close_prompt Boolean @default(true)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
//! OneLauncher log management
|
||||
//! `OneLauncher` log management
|
||||
|
||||
use crate::data::{Credentials, Directories, MinecraftCredentials};
|
||||
use crate::prelude::ClusterPath;
|
||||
use crate::utils::http;
|
||||
use crate::utils::io::{self, IOError};
|
||||
use futures::TryFutureExt;
|
||||
use onelauncher_utils::io::{self, IOError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::time::SystemTime;
|
||||
use tokio::io::{AsyncReadExt, AsyncSeekExt};
|
||||
|
||||
// TODO: put this in the global store
|
||||
/// Core logging state and reader for OneLauncher.
|
||||
/// Core logging state and reader for `OneLauncher`.
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct LogManager {
|
||||
/// Type log type associated with this log file.
|
||||
|
|
@ -128,7 +128,7 @@ pub async fn delete_logs(cluster_path: ClusterPath) -> crate::Result<()> {
|
|||
let entry = entry.map_err(|e| IOError::with_path(e, &logs_folder))?;
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
io::remove_dir_all(&path).await?
|
||||
io::remove_dir_all(&path).await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,14 +170,13 @@ pub async fn get_live_log_cursor(
|
|||
mut cursor: u64,
|
||||
) -> crate::Result<LogCursor> {
|
||||
let cluster_path = cluster_path.cluster_path().await?;
|
||||
let state = crate::State::get().await?;
|
||||
let logs_folder = Directories::cluster_logs_dir(&cluster_path).await?;
|
||||
let path = logs_folder.join(log_file);
|
||||
if !path.exists() {
|
||||
return Ok(LogCursor {
|
||||
cursor: 0,
|
||||
new: false,
|
||||
output: LogOutput("".to_string()),
|
||||
output: LogOutput(String::new()),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -188,12 +187,13 @@ pub async fn get_live_log_cursor(
|
|||
.metadata()
|
||||
.await
|
||||
.map_err(|e| IOError::with_path(e, &path))?;
|
||||
let mut new = false;
|
||||
|
||||
if cursor > metadata.len() {
|
||||
let new = if cursor > metadata.len() {
|
||||
cursor = 0;
|
||||
new = true;
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut buf = Vec::new();
|
||||
file.seek(std::io::SeekFrom::Start(cursor))
|
||||
|
|
@ -205,7 +205,8 @@ pub async fn get_live_log_cursor(
|
|||
.await?;
|
||||
let output = String::from_utf8_lossy(&buf).to_string();
|
||||
let cursor = cursor + bytes_read as u64;
|
||||
let creds: Vec<MinecraftCredentials> = state
|
||||
let creds: Vec<MinecraftCredentials> = crate::State::get()
|
||||
.await?
|
||||
.users
|
||||
.read()
|
||||
.await
|
||||
|
|
@ -213,11 +214,11 @@ pub async fn get_live_log_cursor(
|
|||
.clone()
|
||||
.into_values()
|
||||
.collect();
|
||||
let output = LogOutput::censor_secrets(output, &creds, None);
|
||||
let output = LogOutput::censor_secrets(output, &creds, &None);
|
||||
Ok(LogCursor {
|
||||
cursor,
|
||||
new,
|
||||
output,
|
||||
new,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -262,13 +263,13 @@ pub async fn get_output_by_file(
|
|||
log_type: LogType,
|
||||
log_file: &str,
|
||||
) -> crate::Result<LogOutput> {
|
||||
let state = crate::State::get().await?;
|
||||
let logs_folder = match log_type {
|
||||
LogType::Info => Directories::cluster_logs_dir(cluster_path).await?,
|
||||
LogType::Crash => Directories::crash_reports_dir(cluster_path).await?,
|
||||
};
|
||||
let path = logs_folder.join(log_file);
|
||||
let credentials: Vec<MinecraftCredentials> = state
|
||||
let credentials: Vec<MinecraftCredentials> = crate::State::get()
|
||||
.await?
|
||||
.users
|
||||
.read()
|
||||
.await
|
||||
|
|
@ -279,7 +280,7 @@ pub async fn get_output_by_file(
|
|||
|
||||
let result = read_log_to_string(&path).await?;
|
||||
|
||||
return Ok(LogOutput::censor_secrets(result, &credentials, None));
|
||||
return Ok(LogOutput::censor_secrets(result, &credentials, &None));
|
||||
}
|
||||
|
||||
/// The log cursor used to parse logs passed into [`LogManager`].
|
||||
|
|
@ -303,14 +304,14 @@ impl LogOutput {
|
|||
pub fn censor_secrets(
|
||||
mut output: String,
|
||||
credentials: &Vec<MinecraftCredentials>,
|
||||
_credentials_store: Option<Credentials>,
|
||||
_credentials_store: &Option<Credentials>,
|
||||
) -> Self {
|
||||
let username = whoami::username();
|
||||
let realname = whoami::realname();
|
||||
output = output.replace(&format!("/{}/", username), "/{ENV_USERNAME}/");
|
||||
output = output.replace(&format!("\\{}\\", username), "\\{ENV_USERNAME}\\");
|
||||
output = output.replace(&format!("/{}/", realname), "/{ENV_REALNAME}/");
|
||||
output = output.replace(&format!("\\{}\\", realname), "\\{ENV_REALNAME}\\");
|
||||
output = output.replace(&format!("/{username}/"), "/{ENV_USERNAME}/");
|
||||
output = output.replace(&format!("\\{username}\\"), "\\{ENV_USERNAME}\\");
|
||||
output = output.replace(&format!("/{realname}/"), "/{ENV_REALNAME}/");
|
||||
output = output.replace(&format!("\\{realname}\\"), "\\{ENV_REALNAME}\\");
|
||||
|
||||
for cred in credentials {
|
||||
output = output.replace(&cred.access_token, "{MC_ACCESS_TOKEN}");
|
||||
|
|
@ -323,6 +324,15 @@ impl LogOutput {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct McLogsResponse {
|
||||
pub success: bool,
|
||||
#[serde(default)]
|
||||
pub id: Option<String>,
|
||||
#[serde(default)]
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// Upload a log to https://mclo.gs/. If successful, it returns the log id
|
||||
#[tracing::instrument]
|
||||
pub async fn upload_log(path: &ClusterPath, log: LogOutput) -> crate::Result<String> {
|
||||
|
|
@ -330,16 +340,7 @@ pub async fn upload_log(path: &ClusterPath, log: LogOutput) -> crate::Result<Str
|
|||
let mut form = HashMap::new();
|
||||
form.insert("content", log);
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct ResponseBody {
|
||||
pub success: bool,
|
||||
#[serde(default)]
|
||||
pub id: Option<String>,
|
||||
#[serde(default)]
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
let parsed = serde_json::from_slice::<ResponseBody>(
|
||||
let parsed = serde_json::from_slice::<McLogsResponse>(
|
||||
&http::REQWEST_CLIENT
|
||||
.post(format!("{}/log", crate::constants::MCLOGS_API_URL))
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
|
@ -351,11 +352,10 @@ pub async fn upload_log(path: &ClusterPath, log: LogOutput) -> crate::Result<Str
|
|||
)?;
|
||||
|
||||
if parsed.success {
|
||||
if let Some(id) = parsed.id {
|
||||
Ok(id)
|
||||
} else {
|
||||
Err(anyhow::anyhow!("failed to get log id from mclo.gs").into())
|
||||
}
|
||||
parsed.id.map_or_else(
|
||||
|| Err(anyhow::anyhow!("failed to get log id from mclo.gs").into()),
|
||||
Ok,
|
||||
)
|
||||
} else {
|
||||
Err(anyhow::anyhow!(
|
||||
"failed to upload log to mclo.gs: {}",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
#![allow(clippy::significant_drop_tightening)]
|
||||
|
||||
use crate::data::{Loader, ManagedPackage, ManagedVersion, PackageType};
|
||||
use crate::prelude::PackagePath;
|
||||
use crate::processor::Cluster;
|
||||
use crate::proxy::send::send_internet;
|
||||
use crate::store::{ClusterPath, ManagedVersionFile, Package, PackageMetadata};
|
||||
use crate::utils::{http, io};
|
||||
use crate::utils::http;
|
||||
use crate::{Result, State};
|
||||
use onelauncher_utils::io;
|
||||
// TODO: Implement proper error handling
|
||||
|
||||
/// Find a managed version using filters
|
||||
|
|
@ -23,7 +26,7 @@ pub async fn find_managed_version(
|
|||
let versions = provider
|
||||
.get_all_versions(
|
||||
&package.id,
|
||||
game_version.as_ref().map(|v| vec![v.to_owned()]).to_owned(),
|
||||
game_version.as_ref().map(|v| vec![v.to_owned()]).clone(),
|
||||
loader.map(|l| vec![l]).clone(),
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -45,7 +48,7 @@ pub async fn find_managed_version(
|
|||
|
||||
check_game_version && check_loader && check_package_version
|
||||
})
|
||||
.ok_or(anyhow::anyhow!("no matching version found"))
|
||||
.ok_or_else(|| anyhow::anyhow!("no matching version found"))
|
||||
.cloned()?)
|
||||
}
|
||||
|
||||
|
|
@ -72,14 +75,14 @@ pub async fn download_package(
|
|||
.await?;
|
||||
|
||||
let loader = loader.unwrap_or(cluster.meta.loader);
|
||||
let game_version = game_version.unwrap_or(cluster.meta.mc_version.clone());
|
||||
let game_version = game_version.unwrap_or_else(|| cluster.meta.mc_version.clone());
|
||||
|
||||
let managed_version =
|
||||
find_managed_version(package, Some(game_version), Some(loader), package_version).await?;
|
||||
|
||||
let file = managed_version
|
||||
.get_primary_file()
|
||||
.ok_or(anyhow::anyhow!("no primary file found"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("no primary file found"))?;
|
||||
tracing::info!(
|
||||
"downloading file '{}' version '{}'",
|
||||
file.file_name,
|
||||
|
|
@ -91,7 +94,7 @@ pub async fn download_package(
|
|||
.hashes
|
||||
.get("sha512")
|
||||
.unwrap_or(&"unknown".to_string())
|
||||
.to_owned(); // TODO: Figure out sha1
|
||||
.to_owned();
|
||||
|
||||
let package = Package {
|
||||
file_name: file.file_name.clone(),
|
||||
|
|
@ -122,7 +125,7 @@ async fn download_file(
|
|||
let state = State::get().await?;
|
||||
let bytes = http::fetch(
|
||||
&file.url,
|
||||
file.hashes.get("sha1").map(|s| s.as_str()),
|
||||
file.hashes.get("sha1").map(String::as_str),
|
||||
&state.fetch_semaphore,
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -135,6 +138,8 @@ async fn download_file(
|
|||
return Err(err);
|
||||
};
|
||||
|
||||
drop(state);
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +155,7 @@ pub async fn add_package(
|
|||
let mut manager = state.packages.write().await;
|
||||
let manager = manager
|
||||
.get_mut(cluster_path)
|
||||
.ok_or(anyhow::anyhow!("cluster not found in packages map"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("cluster not found in packages map"))?;
|
||||
|
||||
manager
|
||||
.add_package(package_path, package, package_type)
|
||||
|
|
@ -170,7 +175,7 @@ pub async fn remove_package(
|
|||
let mut manager = state.packages.write().await;
|
||||
let manager = manager
|
||||
.get_mut(cluster_path)
|
||||
.ok_or(anyhow::anyhow!("cluster not found in packages map"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("cluster not found in packages map"))?;
|
||||
|
||||
manager.remove_package(package_path, package_type).await?;
|
||||
|
||||
|
|
@ -188,14 +193,14 @@ pub async fn get_package(
|
|||
let manager = state.packages.read().await;
|
||||
let manager = manager
|
||||
.get(cluster_path)
|
||||
.ok_or(anyhow::anyhow!("cluster not found in packages map"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("cluster not found in packages map"))?;
|
||||
|
||||
Ok(manager
|
||||
.get(package_type)
|
||||
.packages
|
||||
.get(package_path)
|
||||
.cloned()
|
||||
.ok_or(anyhow::anyhow!("package not found"))?)
|
||||
.ok_or_else(|| anyhow::anyhow!("package not found"))?)
|
||||
}
|
||||
|
||||
/// Get packages from a cluster.
|
||||
|
|
@ -208,7 +213,7 @@ pub async fn get_packages(
|
|||
let manager = state.packages.read().await;
|
||||
let manager = manager
|
||||
.get(cluster_path)
|
||||
.ok_or(anyhow::anyhow!("cluster not found in packages map"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("cluster not found in packages map"))?;
|
||||
|
||||
Ok(manager
|
||||
.get(package_type)
|
||||
|
|
@ -225,7 +230,7 @@ pub async fn sync_packages(cluster_path: &ClusterPath) -> Result<()> {
|
|||
let mut manager = state.packages.write().await;
|
||||
let manager = manager
|
||||
.get_mut(cluster_path)
|
||||
.ok_or(anyhow::anyhow!("cluster not found in packages map"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("cluster not found in packages map"))?;
|
||||
|
||||
manager.sync_packages(&state.directories).await;
|
||||
|
||||
|
|
@ -242,7 +247,7 @@ pub async fn sync_packages_by_type(
|
|||
let mut manager = state.packages.write().await;
|
||||
let manager = manager
|
||||
.get_mut(cluster_path)
|
||||
.ok_or(anyhow::anyhow!("cluster not found in packages map"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("cluster not found in packages map"))?;
|
||||
|
||||
manager
|
||||
.sync_packages_by_type(&state.directories, package_type)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::utils::io;
|
||||
use crate::Result;
|
||||
use onelauncher_utils::io;
|
||||
|
||||
use crate::store::ClusterPath;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::utils::io;
|
||||
use crate::Result;
|
||||
use onelauncher_utils::io;
|
||||
|
||||
use crate::store::ClusterPath;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ use crate::package::from::CreatePackCluster;
|
|||
use crate::proxy::send::send_cluster;
|
||||
use crate::proxy::ClusterPayloadType;
|
||||
pub use crate::store::{Cluster, ClusterPath, JavaOptions, PackageData, State};
|
||||
use crate::utils::io::{self, canonicalize};
|
||||
use crate::{cluster, package};
|
||||
use interpulse::api::modded::LoaderVersion;
|
||||
use onelauncher_utils::io::{self, canonicalize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Creates a [`Cluster`] and adds it to the memory [`State`].
|
||||
|
|
@ -60,10 +60,10 @@ pub async fn create_cluster(
|
|||
"creating cluster at path {}",
|
||||
&canonicalize(&path)?.display()
|
||||
);
|
||||
let loader = if mod_loader != Loader::Vanilla {
|
||||
get_loader_version(mc_version.clone(), mod_loader, loader_version).await?
|
||||
} else {
|
||||
let loader = if mod_loader == Loader::Vanilla {
|
||||
None
|
||||
} else {
|
||||
get_loader_version(mc_version.clone(), mod_loader, loader_version).await?
|
||||
};
|
||||
|
||||
let mut cluster = Cluster::new(uuid, name, mc_version).await?;
|
||||
|
|
@ -101,8 +101,10 @@ pub async fn create_cluster(
|
|||
.await?;
|
||||
|
||||
{
|
||||
let mut clusters = state.clusters.write().await;
|
||||
clusters
|
||||
state
|
||||
.clusters
|
||||
.write()
|
||||
.await
|
||||
.insert(cluster.clone(), skip_watch.unwrap_or_default())
|
||||
.await?;
|
||||
}
|
||||
|
|
@ -129,7 +131,7 @@ pub async fn create_cluster(
|
|||
match result {
|
||||
Ok(cluster) => Ok(cluster),
|
||||
Err(err) => {
|
||||
let _ = crate::api::cluster::remove(&cluster.cluster_path()).await;
|
||||
let _ = cluster::remove(&cluster.cluster_path()).await;
|
||||
|
||||
Err(err)
|
||||
}
|
||||
|
|
@ -170,11 +172,10 @@ pub async fn create_cluster_from_duplicate(from: ClusterPath) -> crate::Result<C
|
|||
)
|
||||
.await?;
|
||||
|
||||
let state = State::get().await?;
|
||||
let copied = package::import::copy_minecraft(
|
||||
cluster_path.clone(),
|
||||
from.full_path().await?,
|
||||
&state.io_semaphore,
|
||||
&State::get().await?.io_semaphore,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -221,8 +222,8 @@ pub(crate) async fn get_loader_version(
|
|||
"stable" => it.stable,
|
||||
id => {
|
||||
it.id == *id
|
||||
|| format!("{}-{}", mc_version, id) == it.id
|
||||
|| format!("{}-{}-{}", mc_version, id, mc_version) == it.id
|
||||
|| format!("{mc_version}-{id}") == it.id
|
||||
|| format!("{mc_version}-{id}-{mc_version}") == it.id
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -237,7 +238,7 @@ pub(crate) async fn get_loader_version(
|
|||
|
||||
let loader_meta = loader_meta
|
||||
.to_owned()
|
||||
.ok_or(anyhow::anyhow!("couldn't get game versions"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("couldn't get game versions"))?;
|
||||
|
||||
let loaders = &loader_meta
|
||||
.game_versions
|
||||
|
|
@ -256,10 +257,12 @@ pub(crate) async fn get_loader_version(
|
|||
.iter()
|
||||
.find(|&it| filter(it))
|
||||
.cloned()
|
||||
.or(if version == "stable" {
|
||||
loaders.iter().next().cloned()
|
||||
} else {
|
||||
None
|
||||
.or_else(|| {
|
||||
if version == "stable" {
|
||||
loaders.iter().next().cloned()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| CreateClusterError::InvalidLoaderVersion(version, loader.to_string()))?;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//! **OneLauncher Cluster**
|
||||
//! **`OneLauncher` Cluster**
|
||||
//!
|
||||
//! API for creating our managed Minecraft instances, Clusters.
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ use crate::proxy::ClusterPayloadType;
|
|||
pub use crate::store::{Cluster, JavaOptions, State};
|
||||
use crate::store::{MinecraftCredentials, ProcessorChild};
|
||||
|
||||
use crate::utils::io::{self, IOError};
|
||||
use onelauncher_utils::io::{self, IOError};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
|
|
@ -66,7 +66,7 @@ pub async fn list_grouped() -> crate::Result<HashMap<String, Vec<Cluster>>> {
|
|||
.meta
|
||||
.group
|
||||
.clone()
|
||||
.unwrap_or(String::from("Ungrouped"));
|
||||
.unwrap_or_else(|| String::from("Ungrouped"));
|
||||
if let Some(items) = map.get_mut(&group) {
|
||||
items.push(cluster);
|
||||
} else {
|
||||
|
|
@ -141,7 +141,7 @@ pub async fn run_credentials(
|
|||
if let Some(command) = cmd.next() {
|
||||
let full_path = path.full_path().await?;
|
||||
let result = Command::new(command)
|
||||
.args(cmd.collect::<Vec<&str>>())
|
||||
.args(cmd)
|
||||
.current_dir(&full_path)
|
||||
.spawn()
|
||||
.map_err(|e| IOError::with_path(e, &full_path))?
|
||||
|
|
@ -359,8 +359,8 @@ pub async fn get_optimal_java_version(path: &ClusterPath) -> crate::Result<Optio
|
|||
let metadata = state.metadata.read().await;
|
||||
let minecraft_metadata = metadata
|
||||
.minecraft
|
||||
.to_owned()
|
||||
.ok_or(anyhow::anyhow!("couldn't get minecraft metadata"))?;
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow::anyhow!("couldn't get minecraft metadata"))?;
|
||||
|
||||
let version = minecraft_metadata
|
||||
.versions
|
||||
|
|
@ -421,6 +421,7 @@ pub async fn update_playtime(path: &ClusterPath) -> crate::Result<()> {
|
|||
}
|
||||
|
||||
/// Sanitize a user-inputted [`Cluster`] name.
|
||||
#[must_use]
|
||||
pub fn sanitize_cluster_name(input: &str) -> String {
|
||||
input.replace(['/', '\\', '?', '*', ':', '\'', '\"', '|', '<', '>'], "_")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::path::PathBuf;
|
|||
|
||||
use crate::api::prelude::InternetPayload;
|
||||
use crate::proxy::send::{send_internet, send_message};
|
||||
use crate::utils::io;
|
||||
use onelauncher_utils::io;
|
||||
|
||||
pub async fn parse_uri(cmd: &str) -> crate::Result<()> {
|
||||
let cmd = handle_cmd(cmd).await?;
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ use serde::Deserialize;
|
|||
|
||||
use crate::proxy::send::{init_ingress, send_ingress};
|
||||
use crate::utils::http::{fetch_advanced, fetch_json};
|
||||
use crate::utils::io::{self, IOError};
|
||||
use crate::utils::java::{self, get_java_version, JavaVersion};
|
||||
use crate::State;
|
||||
use onelauncher_utils::io::{self, IOError};
|
||||
|
||||
pub async fn filter_java_version(java_version: Option<u32>) -> crate::Result<Vec<JavaVersion>> {
|
||||
let java = java::locate_java().await?;
|
||||
|
|
@ -15,11 +15,7 @@ pub async fn filter_java_version(java_version: Option<u32>) -> crate::Result<Vec
|
|||
java.into_iter()
|
||||
.filter(|j| {
|
||||
let jre_version = get_java_version(&j.version);
|
||||
if let Ok(jre_version) = jre_version {
|
||||
jre_version.1 == java_version
|
||||
} else {
|
||||
false
|
||||
}
|
||||
jre_version.map_or(false, |jre_version| jre_version.1 == java_version)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
|
|
@ -27,6 +23,13 @@ pub async fn filter_java_version(java_version: Option<u32>) -> crate::Result<Vec
|
|||
})
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct JavaPackage {
|
||||
pub download_url: String,
|
||||
pub name: PathBuf,
|
||||
}
|
||||
|
||||
// TODO: support more than just zulu ?
|
||||
#[onelauncher_macros::memory]
|
||||
pub async fn install_java(java_version: u32) -> crate::Result<PathBuf> {
|
||||
let state = State::get().await?;
|
||||
|
|
@ -40,14 +43,8 @@ pub async fn install_java(java_version: u32) -> crate::Result<PathBuf> {
|
|||
)
|
||||
.await?;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Package {
|
||||
pub download_url: String,
|
||||
pub name: PathBuf,
|
||||
}
|
||||
|
||||
send_ingress(&ingress, 0.0, Some("fetching java api")).await?;
|
||||
let packages = fetch_json::<Vec<Package>>(Method::GET, &format!(
|
||||
let packages = fetch_json::<Vec<JavaPackage>>(Method::GET, &format!(
|
||||
"https://api.azul.com/metadata/v1/zulu/packages?arch={}&java_version={}&os={}&archive_type=zip&javafx_bundled=false&java_package_type=jre&page_size=1",
|
||||
std::env::consts::ARCH, java_version, std::env::consts::OS,
|
||||
), None, None, &state.fetch_semaphore).await?;
|
||||
|
|
@ -93,11 +90,11 @@ pub async fn install_java(java_version: u32) -> crate::Result<PathBuf> {
|
|||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
base_path = base_path
|
||||
.join(format!("zulu-{}.jre", java_version))
|
||||
.join(format!("zulu-{java_version}.jre"))
|
||||
.join("Contents")
|
||||
.join("Home")
|
||||
.join("bin")
|
||||
.join("java")
|
||||
.join("java");
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
|
|
@ -122,9 +119,8 @@ pub async fn check_java(path: PathBuf) -> crate::Result<Option<JavaVersion>> {
|
|||
}
|
||||
|
||||
pub async fn test_java(path: PathBuf, major: u32) -> crate::Result<bool> {
|
||||
let jvm = match java::check_java_instance(&path).await {
|
||||
Some(jvm) => jvm,
|
||||
None => return Ok(false),
|
||||
let Some(jvm) = java::check_java_instance(&path).await else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
let (maj, _) = get_java_version(&jvm.version)?;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ pub use interpulse::api::minecraft::VersionManifest;
|
|||
pub use interpulse::api::modded::Manifest;
|
||||
|
||||
// TODO: better error handling (prisma?)
|
||||
// TODO: fix this lmao
|
||||
|
||||
/// Get a [`VersionManifest`] for all available Minecraft versions.
|
||||
#[tracing::instrument]
|
||||
|
|
@ -16,7 +17,7 @@ pub async fn get_minecraft_versions() -> crate::Result<VersionManifest> {
|
|||
.await
|
||||
.minecraft
|
||||
.clone()
|
||||
.ok_or(anyhow::anyhow!("couldn't get minecraft metadata"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("couldn't get minecraft metadata"))?;
|
||||
|
||||
Ok(meta)
|
||||
}
|
||||
|
|
@ -31,7 +32,7 @@ pub async fn get_fabric_versions() -> crate::Result<Manifest> {
|
|||
.await
|
||||
.fabric
|
||||
.clone()
|
||||
.ok_or(anyhow::anyhow!("couldn't get fabric metadata"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("couldn't get fabric metadata"))?;
|
||||
|
||||
Ok(meta)
|
||||
}
|
||||
|
|
@ -46,7 +47,7 @@ pub async fn get_quilt_versions() -> crate::Result<Manifest> {
|
|||
.await
|
||||
.quilt
|
||||
.clone()
|
||||
.ok_or(anyhow::anyhow!("couldn't get quilt metadata"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("couldn't get quilt metadata"))?;
|
||||
|
||||
Ok(meta)
|
||||
}
|
||||
|
|
@ -61,7 +62,7 @@ pub async fn get_forge_versions() -> crate::Result<Manifest> {
|
|||
.await
|
||||
.forge
|
||||
.clone()
|
||||
.ok_or(anyhow::anyhow!("couldn't get forge metadata"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("couldn't get forge metadata"))?;
|
||||
|
||||
Ok(meta)
|
||||
}
|
||||
|
|
@ -76,7 +77,7 @@ pub async fn get_neoforge_versions() -> crate::Result<Manifest> {
|
|||
.await
|
||||
.neoforge
|
||||
.clone()
|
||||
.ok_or(anyhow::anyhow!("couldn't get neoforge metadata"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("couldn't get neoforge metadata"))?;
|
||||
|
||||
Ok(meta)
|
||||
}
|
||||
|
|
@ -91,7 +92,7 @@ pub async fn get_neoforge_versions() -> crate::Result<Manifest> {
|
|||
// .await
|
||||
// .legacy_fabric
|
||||
// .clone()
|
||||
// .ok_or(anyhow!("missing legacyfabric metadata"))?;
|
||||
// .ok_or_else(|| anyhow!("missing legacyfabric metadata"))?;
|
||||
|
||||
// Ok(meta)
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//! **OneLauncher API**
|
||||
//! **`OneLauncher` API**
|
||||
//!
|
||||
//! API for interacting with our Rust core.
|
||||
|
||||
|
|
@ -26,7 +26,6 @@ pub mod prelude {
|
|||
pub use crate::data::*;
|
||||
pub use crate::proxy::InternetPayload;
|
||||
pub use crate::store::{ClusterPath, JavaVersions, PackageDependency, PackagePath};
|
||||
pub use crate::utils::io::{canonicalize, IOError};
|
||||
pub use crate::utils::java::JavaVersion;
|
||||
pub use crate::{java, metadata, minecraft, package, processor, settings, State};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! **OneLauncher Content Package**
|
||||
//! **`OneLauncher` Content Package**
|
||||
//!
|
||||
//! Utilities for searching and downloading content packages to OneLauncher.
|
||||
//! Utilities for searching and downloading content packages to `OneLauncher`.
|
||||
|
||||
use modrinth::{Facet, FacetOperation};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -27,19 +27,22 @@ impl std::fmt::Display for Providers {
|
|||
|
||||
impl Providers {
|
||||
/// Get the name of the provider
|
||||
pub fn name(&self) -> &str {
|
||||
#[must_use]
|
||||
pub const fn name(&self) -> &str {
|
||||
match self {
|
||||
Providers::Modrinth => "Modrinth",
|
||||
Self::Modrinth => "Modrinth",
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the URL of the provider
|
||||
pub fn url(&self) -> &str {
|
||||
#[must_use]
|
||||
pub const fn url(&self) -> &str {
|
||||
match self {
|
||||
Providers::Modrinth => "https://modrinth.com",
|
||||
Self::Modrinth => "https://modrinth.com",
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn search(
|
||||
&self,
|
||||
query: Option<String>,
|
||||
|
|
@ -52,7 +55,7 @@ impl Providers {
|
|||
open_source: Option<bool>,
|
||||
) -> Result<ProviderSearchResults> {
|
||||
match self {
|
||||
Providers::Modrinth => modrinth::search(
|
||||
Self::Modrinth => modrinth::search(
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
|
|
@ -112,17 +115,17 @@ impl Providers {
|
|||
|
||||
pub async fn list(&self) -> Result<Vec<ManagedPackage>> {
|
||||
Ok(match self {
|
||||
Providers::Modrinth => modrinth::list(),
|
||||
Self::Modrinth => modrinth::list(),
|
||||
}
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|p| p.into())
|
||||
.map(Into::into)
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub async fn get(&self, slug_or_id: &str) -> Result<ManagedPackage> {
|
||||
Ok(match self {
|
||||
Providers::Modrinth => modrinth::get(slug_or_id),
|
||||
Self::Modrinth => modrinth::get(slug_or_id),
|
||||
}
|
||||
.await?
|
||||
.into())
|
||||
|
|
@ -130,11 +133,11 @@ impl Providers {
|
|||
|
||||
pub async fn get_multiple(&self, slug_or_ids: &[String]) -> Result<Vec<ManagedPackage>> {
|
||||
Ok(match self {
|
||||
Providers::Modrinth => modrinth::get_multiple(slug_or_ids),
|
||||
Self::Modrinth => modrinth::get_multiple(slug_or_ids),
|
||||
}
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|p| p.into())
|
||||
.map(Into::into)
|
||||
.collect())
|
||||
}
|
||||
|
||||
|
|
@ -145,27 +148,27 @@ impl Providers {
|
|||
loaders: Option<Vec<Loader>>,
|
||||
) -> Result<Vec<ManagedVersion>> {
|
||||
Ok(match self {
|
||||
Providers::Modrinth => modrinth::get_all_versions(project_id, game_versions, loaders),
|
||||
Self::Modrinth => modrinth::get_all_versions(project_id, game_versions, loaders),
|
||||
}
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|p| p.into())
|
||||
.map(Into::into)
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub async fn get_versions(&self, versions: Vec<String>) -> Result<Vec<ManagedVersion>> {
|
||||
Ok(match self {
|
||||
Providers::Modrinth => modrinth::get_versions(versions),
|
||||
Self::Modrinth => modrinth::get_versions(versions),
|
||||
}
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|p| p.into())
|
||||
.map(Into::into)
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub async fn get_version(&self, version_id: &str) -> Result<ManagedVersion> {
|
||||
Ok(match self {
|
||||
Providers::Modrinth => modrinth::get_version(version_id),
|
||||
Self::Modrinth => modrinth::get_version(version_id),
|
||||
}
|
||||
.await?
|
||||
.into())
|
||||
|
|
@ -173,7 +176,7 @@ impl Providers {
|
|||
|
||||
pub async fn get_authors(&self, author: &Author) -> Result<Vec<ManagedUser>> {
|
||||
match self {
|
||||
Providers::Modrinth => modrinth::get_authors(author),
|
||||
Self::Modrinth => modrinth::get_authors(author),
|
||||
}
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use crate::data::{Loader, ManagedPackage, ManagedUser, ManagedVersion, PackageType};
|
||||
|
|
@ -14,7 +15,7 @@ use serde_json::Value;
|
|||
|
||||
use super::Providers;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ModrinthPackage {
|
||||
pub slug: String,
|
||||
pub title: String,
|
||||
|
|
@ -61,9 +62,9 @@ pub struct ModrinthPackage {
|
|||
}
|
||||
|
||||
impl From<ModrinthPackage> for ManagedPackage {
|
||||
fn from(value: ModrinthPackage) -> ManagedPackage {
|
||||
ManagedPackage {
|
||||
provider: super::Providers::Modrinth,
|
||||
fn from(value: ModrinthPackage) -> Self {
|
||||
Self {
|
||||
provider: Providers::Modrinth,
|
||||
id: value.id,
|
||||
title: value.title,
|
||||
description: value.description,
|
||||
|
|
@ -93,14 +94,14 @@ impl From<ModrinthPackage> for ManagedPackage {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct DonationUrl {
|
||||
pub id: String,
|
||||
pub platform: String,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Gallery {
|
||||
pub url: String,
|
||||
pub featured: bool,
|
||||
|
|
@ -119,7 +120,7 @@ struct SearchResults {
|
|||
total_hits: u32,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ModrinthVersion {
|
||||
pub game_versions: Vec<String>,
|
||||
pub loaders: Vec<String>,
|
||||
|
|
@ -140,7 +141,7 @@ pub struct ModrinthVersion {
|
|||
pub dependencies: Vec<Value>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct File {
|
||||
pub url: String,
|
||||
pub filename: String,
|
||||
|
|
@ -151,20 +152,20 @@ pub struct File {
|
|||
|
||||
impl From<File> for ManagedVersionFile {
|
||||
fn from(value: File) -> Self {
|
||||
ManagedVersionFile {
|
||||
Self {
|
||||
url: value.url,
|
||||
file_name: value.filename,
|
||||
primary: value.primary,
|
||||
size: value.size,
|
||||
file_type: value.file_type,
|
||||
hashes: Default::default(), // TODO
|
||||
hashes: HashMap::default(), // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ModrinthVersion> for ManagedVersion {
|
||||
fn from(value: ModrinthVersion) -> Self {
|
||||
ManagedVersion {
|
||||
Self {
|
||||
id: value.id,
|
||||
package_id: value.project_id,
|
||||
author: value.author_id,
|
||||
|
|
@ -179,7 +180,7 @@ impl From<ModrinthVersion> for ManagedVersion {
|
|||
downloads: value.downloads,
|
||||
version_type: value.version_type,
|
||||
|
||||
files: value.files.into_iter().map(|f| f.into()).collect(),
|
||||
files: value.files.into_iter().map(Into::into).collect(),
|
||||
deps: vec![], // TODO [`ManagedDependency`]?
|
||||
game_versions: value.game_versions,
|
||||
loaders: value
|
||||
|
|
@ -204,12 +205,12 @@ pub enum FacetOperation {
|
|||
impl Display for FacetOperation {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
FacetOperation::NotEq => "!=",
|
||||
FacetOperation::LargeEq => ">=",
|
||||
FacetOperation::Large => ">",
|
||||
FacetOperation::SmallEq => "<=",
|
||||
FacetOperation::Small => "<",
|
||||
FacetOperation::Eq => "=",
|
||||
Self::NotEq => "!=",
|
||||
Self::LargeEq => ">=",
|
||||
Self::Large => ">",
|
||||
Self::SmallEq => "<=",
|
||||
Self::Small => "<",
|
||||
Self::Eq => "=",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -217,7 +218,7 @@ impl Display for FacetOperation {
|
|||
pub struct Facet(pub String, pub FacetOperation, pub String);
|
||||
|
||||
impl Display for Facet {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(format!("{}{}{}", self.0, self.1, self.2).as_str())
|
||||
}
|
||||
}
|
||||
|
|
@ -227,8 +228,8 @@ pub struct FacetBuilder {
|
|||
}
|
||||
|
||||
impl FacetBuilder {
|
||||
pub fn builder() -> Self {
|
||||
FacetBuilder { facets: vec![] }
|
||||
pub const fn builder() -> Self {
|
||||
Self { facets: vec![] }
|
||||
}
|
||||
|
||||
pub fn and(&mut self, facet: Facet) -> &Self {
|
||||
|
|
@ -253,7 +254,7 @@ impl FacetBuilder {
|
|||
if i != 0 {
|
||||
stringified.push(',');
|
||||
}
|
||||
stringified.push_str(format!("\"{}\"", f.to_string()).as_str());
|
||||
stringified.push_str(format!("\"{f}\"").as_str());
|
||||
}
|
||||
stringified.push(']');
|
||||
|
||||
|
|
@ -293,7 +294,7 @@ pub async fn get_multiple(ids: &[String]) -> Result<Vec<ModrinthPackage>> {
|
|||
format_url!(
|
||||
"/projects?ids=[{}]",
|
||||
ids.iter()
|
||||
.map(|id| format!("\"{}\"", id))
|
||||
.map(|id| format!("\"{id}\""))
|
||||
.collect::<Vec<String>>()
|
||||
.join(",")
|
||||
)
|
||||
|
|
@ -335,9 +336,9 @@ where
|
|||
limit.unwrap_or(10),
|
||||
offset.unwrap_or(0),
|
||||
if facets.is_empty() {
|
||||
"".to_string()
|
||||
String::new()
|
||||
} else {
|
||||
format!("&facets=[{}]", facets)
|
||||
format!("&facets=[{facets}]")
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
|
|
@ -456,7 +457,7 @@ pub async fn get_all_versions(
|
|||
"game_versions",
|
||||
&game_versions
|
||||
.iter()
|
||||
.map(|v| format!("\"{}\"", v.to_string()))
|
||||
.map(|v| format!("\"{v}\""))
|
||||
.collect::<Vec<String>>()
|
||||
.join(","),
|
||||
);
|
||||
|
|
@ -469,7 +470,7 @@ pub async fn get_all_versions(
|
|||
"[{}]",
|
||||
&loaders
|
||||
.iter()
|
||||
.map(|l| format!("\"{}\"", l.to_string()))
|
||||
.map(|l| format!("\"{l}\""))
|
||||
.collect::<Vec<String>>()
|
||||
.join(",")
|
||||
)
|
||||
|
|
@ -494,7 +495,7 @@ pub async fn get_versions(versions: Vec<String>) -> Result<Vec<ModrinthVersion>>
|
|||
"/versions?ids=[{}]",
|
||||
versions
|
||||
.iter()
|
||||
.map(|v| format!("\"{}\"", v))
|
||||
.map(|v| format!("\"{v}\""))
|
||||
.collect::<Vec<String>>()
|
||||
.join(",")
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use crate::proxy::send::{init_ingress, send_ingress};
|
|||
use crate::proxy::IngressId;
|
||||
use crate::store::{ClusterStage, PackageSide};
|
||||
use crate::utils::http::{fetch, fetch_advanced, fetch_json, write_icon};
|
||||
use crate::utils::io;
|
||||
use crate::{IngressType, InnerPathLinux, State};
|
||||
use onelauncher_utils::io;
|
||||
use reqwest::Method;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -46,9 +46,9 @@ pub enum PackFileHash {
|
|||
impl From<String> for PackFileHash {
|
||||
fn from(s: String) -> Self {
|
||||
return match s.as_str() {
|
||||
"sha1" => PackFileHash::Sha1,
|
||||
"sha512" => PackFileHash::Sha512,
|
||||
_ => PackFileHash::Unknown(s),
|
||||
"sha1" => Self::Sha1,
|
||||
"sha512" => Self::Sha512,
|
||||
_ => Self::Unknown(s),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -120,7 +120,7 @@ pub struct CreatePackCluster {
|
|||
|
||||
impl Default for CreatePackCluster {
|
||||
fn default() -> Self {
|
||||
CreatePackCluster {
|
||||
Self {
|
||||
name: "Untitled".to_string(),
|
||||
mc_version: "1.20.4".to_string(),
|
||||
mod_loader: Loader::Vanilla,
|
||||
|
|
@ -150,6 +150,7 @@ pub struct CreatePackDescription {
|
|||
pub cluster_path: ClusterPath,
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_cluster_from_pack(location: CreatePackLocation) -> CreatePackCluster {
|
||||
match location {
|
||||
CreatePackLocation::FromModrinth {
|
||||
|
|
@ -320,11 +321,11 @@ pub async fn generate_pack_from_file(
|
|||
/// Sets generated cluster attributes to the pack attributes (using `cluster::edit`).
|
||||
/// This includes the pack name, icon, game version, loader version, and loader.
|
||||
#[onelauncher_macros::memory]
|
||||
pub async fn set_cluster_information(
|
||||
pub async fn set_cluster_information<S: ::std::hash::BuildHasher>(
|
||||
cluster_path: ClusterPath,
|
||||
description: &CreatePackDescription,
|
||||
backup_name: &str,
|
||||
deps: &HashMap<PackDependency, String>,
|
||||
deps: &HashMap<PackDependency, String, S>,
|
||||
ignore_lock: bool,
|
||||
) -> crate::Result<()> {
|
||||
let mut game_version: Option<&String> = None;
|
||||
|
|
@ -353,22 +354,19 @@ pub async fn set_cluster_information(
|
|||
}
|
||||
}
|
||||
|
||||
let game_version = if let Some(game_version) = game_version {
|
||||
game_version
|
||||
} else {
|
||||
let Some(game_version) = game_version else {
|
||||
return Err(anyhow::anyhow!("pack did not specify Minecraft version").into());
|
||||
};
|
||||
|
||||
let mod_loader = mod_loader.unwrap_or(Loader::Vanilla);
|
||||
let loader_version = if mod_loader != Loader::Vanilla {
|
||||
let loader_version = if mod_loader == Loader::Vanilla {
|
||||
None
|
||||
} else {
|
||||
crate::cluster::create::get_loader_version(
|
||||
game_version.clone(),
|
||||
mod_loader,
|
||||
loader_version.cloned(),
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
crate::api::cluster::edit(&cluster_path, |cl| {
|
||||
|
|
@ -384,10 +382,10 @@ pub async fn set_cluster_information(
|
|||
Some(PackageData {
|
||||
package_id,
|
||||
version_id,
|
||||
locked: if !ignore_lock {
|
||||
Some(true)
|
||||
} else {
|
||||
locked: if ignore_lock {
|
||||
cl.meta.package_data.as_ref().and_then(|x| x.locked)
|
||||
} else {
|
||||
Some(true)
|
||||
},
|
||||
})
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//! Launcher Import: ATLauncher
|
||||
//! Source Code available at https://github.com/ATLauncher/ATLauncher
|
||||
//! Launcher Import: `ATLauncher`
|
||||
//! Source Code available at <https://github.com/ATLauncher/ATLauncher>
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -10,7 +10,7 @@ use crate::package::import::{self, copy_minecraft};
|
|||
use crate::package::{self};
|
||||
use crate::prelude::{Cluster, ClusterPath, Loader};
|
||||
use crate::store::{ClusterStage, PackageData, State};
|
||||
use crate::utils::io;
|
||||
use onelauncher_utils::io;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
|
@ -97,7 +97,7 @@ pub struct ATLauncherMod {
|
|||
pub async fn is_valid_atlauncher(instance_folder: PathBuf) -> bool {
|
||||
let instance: String = io::read_to_string(&instance_folder.join("instance.json"))
|
||||
.await
|
||||
.unwrap_or("".to_string());
|
||||
.unwrap_or(String::new());
|
||||
let instance: Result<ATInstance, serde_json::Error> =
|
||||
serde_json::from_str::<ATInstance>(&instance);
|
||||
if let Err(e) = instance {
|
||||
|
|
@ -157,7 +157,7 @@ pub async fn import_atlauncher(
|
|||
cluster_path: cluster_path.clone(),
|
||||
};
|
||||
|
||||
let backup_name = format!("ATLauncher-{}", instance_folder);
|
||||
let backup_name = format!("ATLauncher-{instance_folder}");
|
||||
let minecraft_folder = atlauncher_instance_path;
|
||||
|
||||
import_atlauncher_unmanaged(
|
||||
|
|
@ -186,15 +186,15 @@ async fn import_atlauncher_unmanaged(
|
|||
let mod_loader: Loader = serde_json::from_str::<Loader>(&mod_loader)?;
|
||||
|
||||
let game_version = atinstance.id;
|
||||
let loader_version = if mod_loader != Loader::Vanilla {
|
||||
let loader_version = if mod_loader == Loader::Vanilla {
|
||||
None
|
||||
} else {
|
||||
crate::cluster::create::get_loader_version(
|
||||
game_version.clone(),
|
||||
mod_loader,
|
||||
Some(atinstance.launcher.loader_version.version.clone()),
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
crate::api::cluster::edit(&cluster_path, |cl| {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//! Launcher Import: CurseForge
|
||||
//! Launcher Import: `CurseForge`
|
||||
//! Closed Source, all this information is datamined unfortunately.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -8,7 +8,7 @@ use super::{cache_icon, copy_minecraft};
|
|||
use crate::prelude::{Cluster, ClusterPath, Loader};
|
||||
use crate::store::{ClusterStage, State};
|
||||
use crate::utils::http::{fetch, write_icon};
|
||||
use crate::utils::io;
|
||||
use onelauncher_utils::io;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
|
@ -37,12 +37,13 @@ pub async fn is_valid_curseforge(instance_folder: PathBuf) -> bool {
|
|||
let minecraftinstance: String =
|
||||
io::read_to_string(&instance_folder.join("minecraftinstance.json"))
|
||||
.await
|
||||
.unwrap_or("".to_string());
|
||||
.unwrap_or(String::new());
|
||||
let minecraftinstance: Result<MinecraftInstance, serde_json::Error> =
|
||||
serde_json::from_str::<MinecraftInstance>(&minecraftinstance);
|
||||
minecraftinstance.is_ok()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub async fn import_curseforge(
|
||||
// The path to the CurseForge instance
|
||||
curseforge_instance_folder: PathBuf,
|
||||
|
|
@ -58,8 +59,7 @@ pub async fn import_curseforge(
|
|||
"Curseforge-{}",
|
||||
curseforge_instance_folder
|
||||
.file_name()
|
||||
.map(|a| a.to_string_lossy().to_string())
|
||||
.unwrap_or("Unknown".to_string())
|
||||
.map_or("Unknown".to_string(), |a| a.to_string_lossy().to_string())
|
||||
);
|
||||
|
||||
let state = State::get().await?;
|
||||
|
|
@ -104,15 +104,15 @@ pub async fn import_curseforge(
|
|||
}
|
||||
|
||||
let mod_loader = mod_loader.unwrap_or(Loader::Vanilla);
|
||||
let loader_version = if mod_loader != Loader::Vanilla {
|
||||
let loader_version = if mod_loader == Loader::Vanilla {
|
||||
None
|
||||
} else {
|
||||
crate::cluster::create::get_loader_version(
|
||||
game_version.clone(),
|
||||
mod_loader,
|
||||
loader_version,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
crate::api::cluster::edit(&cluster_path, |cl| {
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
//! Launcher Import: FTB App
|
||||
//! Source Code available at https://github.com/FTBTeam/FTB-App
|
||||
//! Source Code available at <https://github.com/FTBTeam/FTB-App>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//! Launcher Import: GDLauncher (legacy)
|
||||
//! Source Code available at https://github.com/gorilla-devs/GDLauncher
|
||||
//! Launcher Import: `GDLauncher` (legacy)
|
||||
//! Source Code available at <https://github.com/gorilla-devs/GDLauncher>
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
|
@ -7,7 +7,7 @@ use std::path::PathBuf;
|
|||
use super::{cache_icon, copy_minecraft};
|
||||
use crate::prelude::{Cluster, ClusterPath, Loader};
|
||||
use crate::store::{ClusterStage, State};
|
||||
use crate::utils::io;
|
||||
use onelauncher_utils::io;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
|
@ -30,7 +30,7 @@ pub struct GDLauncherLoader {
|
|||
pub async fn is_valid_gdlauncher(instance_folder: PathBuf) -> bool {
|
||||
let config: String = io::read_to_string(&instance_folder.join("config.json"))
|
||||
.await
|
||||
.unwrap_or("".to_string());
|
||||
.unwrap_or(String::new());
|
||||
let config: Result<GDLauncherConfig, serde_json::Error> =
|
||||
serde_json::from_str::<GDLauncherConfig>(&config);
|
||||
config.is_ok()
|
||||
|
|
@ -48,8 +48,7 @@ pub async fn import_gdlauncher(
|
|||
"GDLauncher-{}",
|
||||
gdlauncher_instance_folder
|
||||
.file_name()
|
||||
.map(|a| a.to_string_lossy().to_string())
|
||||
.unwrap_or("Unknown".to_string())
|
||||
.map_or("Unknown".to_string(), |a| a.to_string_lossy().to_string())
|
||||
);
|
||||
|
||||
let icon = config
|
||||
|
|
@ -66,11 +65,11 @@ pub async fn import_gdlauncher(
|
|||
let mod_loader = config.loader.loader_type;
|
||||
let loader_version = config.loader.loader_version;
|
||||
|
||||
let loader_version = if mod_loader != Loader::Vanilla {
|
||||
let loader_version = if mod_loader == Loader::Vanilla {
|
||||
None
|
||||
} else {
|
||||
crate::cluster::create::get_loader_version(game_version.clone(), mod_loader, loader_version)
|
||||
.await?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
crate::api::cluster::edit(&cluster_path, |cl| {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::proxy::IngressId;
|
|||
|
||||
use crate::store::Clusters;
|
||||
use crate::utils::http::{self, IoSemaphore};
|
||||
use crate::utils::io::{self, IOError};
|
||||
use onelauncher_utils::io::{self, IOError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -24,19 +24,19 @@ pub mod tlauncher;
|
|||
#[cfg_attr(feature = "specta", derive(specta::Type))]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum ImportType {
|
||||
/// MultiMC based launchers
|
||||
/// `MultiMC` based launchers
|
||||
MultiMC,
|
||||
/// Prism Launcher has its own category because it has different logic (also is objectively better than mmc)
|
||||
PrismLauncher,
|
||||
/// GDLauncher
|
||||
/// `GDLauncher`
|
||||
GDLauncher,
|
||||
/// Curseforge's launcher
|
||||
Curseforge,
|
||||
/// ATLauncher
|
||||
/// `ATLauncher`
|
||||
ATLauncher,
|
||||
/// Modrinth app.
|
||||
Modrinth,
|
||||
/// TLauncher
|
||||
/// `TLauncher`
|
||||
TLauncher,
|
||||
/// FTB Launcher
|
||||
FTBLauncher,
|
||||
|
|
@ -50,16 +50,16 @@ pub enum ImportType {
|
|||
impl fmt::Display for ImportType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ImportType::MultiMC => write!(f, "MultiMC"),
|
||||
ImportType::ATLauncher => write!(f, "ATLauncher"),
|
||||
ImportType::Curseforge => write!(f, "Curseforge"),
|
||||
ImportType::GDLauncher => write!(f, "GDLauncher"),
|
||||
ImportType::Modrinth => write!(f, "Modrinth App"),
|
||||
ImportType::PrismLauncher => write!(f, "PrismLauncher"),
|
||||
ImportType::TLauncher => write!(f, "TLauncher"),
|
||||
ImportType::FTBLauncher => write!(f, "Feed The Beast"),
|
||||
ImportType::Technic => write!(f, "Technic"),
|
||||
ImportType::Unknown => write!(f, "Unknown"),
|
||||
Self::MultiMC => write!(f, "MultiMC"),
|
||||
Self::ATLauncher => write!(f, "ATLauncher"),
|
||||
Self::Curseforge => write!(f, "Curseforge"),
|
||||
Self::GDLauncher => write!(f, "GDLauncher"),
|
||||
Self::Modrinth => write!(f, "Modrinth App"),
|
||||
Self::PrismLauncher => write!(f, "PrismLauncher"),
|
||||
Self::TLauncher => write!(f, "TLauncher"),
|
||||
Self::FTBLauncher => write!(f, "Feed The Beast"),
|
||||
Self::Technic => write!(f, "Technic"),
|
||||
Self::Unknown => write!(f, "Unknown"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -77,9 +77,9 @@ pub async fn import_instances(import: ImportType, path: PathBuf) -> crate::Resul
|
|||
.unwrap_or_else(|| "instances".to_string())
|
||||
}
|
||||
ImportType::Modrinth => "profiles".to_string(),
|
||||
ImportType::TLauncher => "instances".to_string(),
|
||||
ImportType::FTBLauncher => "instances".to_string(),
|
||||
ImportType::Technic => "instances".to_string(),
|
||||
ImportType::TLauncher => "tinstances".to_string(),
|
||||
ImportType::FTBLauncher => "ftbinstances".to_string(),
|
||||
ImportType::Technic => "tecinstances".to_string(),
|
||||
_ => return Err(anyhow::anyhow!("launcher type unknown, cant import").into()),
|
||||
};
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ pub async fn import_instance(
|
|||
};
|
||||
|
||||
match result {
|
||||
Ok(_) => {}
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
tracing::warn!("failed to import modpack: {:?}", e);
|
||||
let _ = crate::api::cluster::remove(&cluster_path).await;
|
||||
|
|
@ -167,6 +167,7 @@ pub async fn import_instance(
|
|||
}
|
||||
|
||||
/// returns the default path for a given [`ImportType`].
|
||||
#[must_use]
|
||||
pub fn default_launcher_path(r#type: ImportType) -> Option<PathBuf> {
|
||||
let path = match r#type {
|
||||
ImportType::MultiMC => None, // MultiMC data is in it's application directory
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
//! Launcher Import: Modrinth App
|
||||
//! Source Code available at https://github.com/modrinth/code
|
||||
//! Source Code available at <https://github.com/modrinth/code>
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::prelude::ClusterPath;
|
||||
use crate::utils::io;
|
||||
use onelauncher_utils::io;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ModrinthProfile {}
|
||||
|
|
@ -13,7 +13,7 @@ pub struct ModrinthProfile {}
|
|||
pub async fn is_valid_modrinth(instance_folder: PathBuf) -> bool {
|
||||
let config: String = io::read_to_string(&instance_folder.join("profile.json"))
|
||||
.await
|
||||
.unwrap_or("".to_string());
|
||||
.unwrap_or(String::new());
|
||||
let config: Result<ModrinthProfile, serde_json::Error> =
|
||||
serde_json::from_str::<ModrinthProfile>(&config);
|
||||
config.is_ok()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! Launcher Import: MultiMC & Prism Launcher
|
||||
//! Source Code available at https://github.com/PrismLauncher/PrismLauncher
|
||||
//! Source Code available at https://github.com/MultiMC/Launcher
|
||||
//! Launcher Import: `MultiMC` & Prism Launcher
|
||||
//! Source Code available at <https://github.com/PrismLauncher/PrismLauncher>
|
||||
//! Source Code available at <https://github.com/MultiMC/Launcher>
|
||||
|
||||
use serde::{de, Deserialize, Serialize};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -8,7 +8,7 @@ use std::path::{Path, PathBuf};
|
|||
use crate::package::from::{self, CreatePackDescription, PackDependency};
|
||||
use crate::package::import::{self, copy_minecraft};
|
||||
use crate::prelude::{Cluster, ClusterPath, State};
|
||||
use crate::utils::io;
|
||||
use onelauncher_utils::io;
|
||||
|
||||
// instance.cfg: https://github.com/PrismLauncher/PrismLauncher/blob/develop/launcher/minecraft/MinecraftInstance.cpp
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
@ -53,15 +53,14 @@ fn deserialize_optional_bool<'de, D>(deserializer: D) -> Result<Option<bool>, D:
|
|||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
let s = Option::<String>::deserialize(deserializer)?;
|
||||
match s {
|
||||
Some(string) => match string.as_str() {
|
||||
Option::<String>::deserialize(deserializer)?.map_or_else(
|
||||
|| Ok(None),
|
||||
|string| match string.as_str() {
|
||||
"true" => Ok(Some(true)),
|
||||
"false" => Ok(Some(false)),
|
||||
_ => Err(de::Error::custom("expected 'true' or 'false'")),
|
||||
},
|
||||
None => Ok(None),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
@ -139,10 +138,8 @@ pub struct MMCLauncher {
|
|||
pub async fn is_valid_multibased(instance_folder: PathBuf) -> bool {
|
||||
let instance_cfg = instance_folder.join("instance.cfg");
|
||||
let mmc_pack = instance_folder.join("mmc-pack.json");
|
||||
|
||||
let mmc_pack = match io::read_to_string(&mmc_pack).await {
|
||||
Ok(mmc_pack) => mmc_pack,
|
||||
Err(_) => return false,
|
||||
let Ok(mmc_pack) = io::read_to_string(&mmc_pack).await else {
|
||||
return false;
|
||||
};
|
||||
|
||||
load_instance_cfg(&instance_cfg).await.is_ok()
|
||||
|
|
@ -217,7 +214,7 @@ pub async fn import_mmc(
|
|||
)
|
||||
.await?;
|
||||
}
|
||||
Some(MMCManagedPackType::Flame) | Some(MMCManagedPackType::ATLauncher) => {
|
||||
Some(MMCManagedPackType::Flame | MMCManagedPackType::ATLauncher) => {
|
||||
// Same as unmanaged, but with 'minecraft' folder instead of '.minecraft'
|
||||
let minecraft_folder = mmc_base_path
|
||||
.join("instances")
|
||||
|
|
@ -313,7 +310,7 @@ async fn import_mmc_unmanaged(
|
|||
})
|
||||
.collect();
|
||||
|
||||
from::set_cluster_information(
|
||||
from::set_cluster_information::<std::hash::RandomState>(
|
||||
cluster_path.clone(),
|
||||
&description,
|
||||
&backup_name,
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
//! Launcher Import: Technic Launcher
|
||||
//! Source Code available at https://github.com/TechnicPack/LauncherV3
|
||||
//! Source Code available at <https://github.com/TechnicPack/LauncherV3>
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
//! Launcher Import: TLauncher
|
||||
//! Launcher Import: `TLauncher`
|
||||
//! Closed source (technically) ;3
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! **OneLauncher Package**
|
||||
//! **`OneLauncher` Package**
|
||||
//!
|
||||
//! Utilities for importing packages from and to OneLauncher.
|
||||
//! Utilities for importing packages from and to `OneLauncher`.
|
||||
|
||||
pub mod content;
|
||||
pub mod from;
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ use crate::proxy::ingress_try_for_each;
|
|||
use crate::proxy::send::{init_or_edit_ingress, send_ingress};
|
||||
use crate::store::{ClusterStage, Clusters, PackageSide};
|
||||
use crate::utils::http::{fetch_from_mirrors, write};
|
||||
use crate::utils::io;
|
||||
use crate::{cluster, IngressType, State};
|
||||
use async_zip::base::read::seek::ZipFileReader;
|
||||
use onelauncher_utils::io;
|
||||
|
||||
use futures::StreamExt;
|
||||
use std::io::Cursor;
|
||||
use std::path::{Component, PathBuf};
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ pub async fn install_zipped_mrpack(
|
|||
match result {
|
||||
Ok(cluster) => Ok(cluster),
|
||||
Err(err) => {
|
||||
let _ = crate::api::cluster::remove(&cluster_path).await;
|
||||
let _ = cluster::remove(&cluster_path).await;
|
||||
|
||||
Err(err)
|
||||
}
|
||||
|
|
@ -121,7 +122,6 @@ pub async fn install_zipped_mrpack_files(
|
|||
.await?;
|
||||
|
||||
let num_files = pack.files.len();
|
||||
use futures::StreamExt;
|
||||
ingress_try_for_each(
|
||||
futures::stream::iter(pack.files.into_iter()).map(Ok::<PackFile, crate::Error>),
|
||||
None,
|
||||
|
|
@ -136,8 +136,7 @@ pub async fn install_zipped_mrpack_files(
|
|||
if let Some(env) = pack.env {
|
||||
if env
|
||||
.get(&EnvType::Client)
|
||||
.map(|x| x == &PackageSide::Unsupported)
|
||||
.unwrap_or(false)
|
||||
.is_some_and(|x| x == &PackageSide::Unsupported)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
|
@ -208,8 +207,8 @@ pub async fn install_zipped_mrpack_files(
|
|||
|
||||
send_ingress(
|
||||
&ingress,
|
||||
30.0 / total_len as f64,
|
||||
Some(&format!("extracting override {}/{}", index, total_len)),
|
||||
30.0 / f64::from(total_len),
|
||||
Some(&format!("extracting override {index}/{total_len}")),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
|
@ -259,7 +258,7 @@ pub async fn remove_all_related_files(
|
|||
return Err(anyhow::anyhow!("pack doesn't support Minecraft").into());
|
||||
}
|
||||
|
||||
crate::api::cluster::edit(&cluster_path, |cl| {
|
||||
cluster::edit(&cluster_path, |cl| {
|
||||
cl.stage = ClusterStage::PackDownloading;
|
||||
async { Ok(()) }
|
||||
})
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ pub async fn send_ingress(
|
|||
{
|
||||
ingress.cli.set_message(
|
||||
message
|
||||
.map(|x| x.to_string())
|
||||
.map(ToString::to_string)
|
||||
.unwrap_or(ingress.message.clone()),
|
||||
);
|
||||
ingress
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use crate::proxy::send::{init_ingress, send_ingress};
|
||||
use crate::store::{Clusters, Directories, Settings};
|
||||
use crate::State;
|
||||
use onelauncher_utils::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
|
|
@ -91,7 +92,7 @@ pub async fn set_directory(new: PathBuf) -> crate::Result<()> {
|
|||
state_write.watcher = RwLock::new(fs_watcher);
|
||||
|
||||
tracing::trace!("collecting all config files to be transfered");
|
||||
let mut cfg_entries = crate::utils::io::read_dir(&old).await?;
|
||||
let mut cfg_entries = io::read_dir(&old).await?;
|
||||
let across_filesystems = is_different_fs(&old, &new);
|
||||
let mut entries = vec![];
|
||||
let mut cleanable = vec![];
|
||||
|
|
@ -99,7 +100,7 @@ pub async fn set_directory(new: PathBuf) -> crate::Result<()> {
|
|||
while let Some(e) = cfg_entries
|
||||
.next_entry()
|
||||
.await
|
||||
.map_err(|err| crate::utils::io::IOError::with_path(err, &old))?
|
||||
.map_err(|err| io::IOError::with_path(err, &old))?
|
||||
{
|
||||
let epath = e.path();
|
||||
if let Some(file_name) = epath.file_name() {
|
||||
|
|
@ -127,7 +128,7 @@ pub async fn set_directory(new: PathBuf) -> crate::Result<()> {
|
|||
if across_filesystems {
|
||||
crate::utils::http::copy(&epath, &newpath, io_semaphore).await?;
|
||||
} else {
|
||||
crate::utils::io::rename(epath.clone(), newpath.clone()).await?;
|
||||
io::rename(epath.clone(), newpath.clone()).await?;
|
||||
}
|
||||
|
||||
send_ingress(&ingress, 80.0 * (1.0 / nentries), None).await?;
|
||||
|
|
@ -161,7 +162,7 @@ pub async fn set_directory(new: PathBuf) -> crate::Result<()> {
|
|||
tracing::trace!("deleting old config files");
|
||||
}
|
||||
for ce in cleanable {
|
||||
crate::utils::io::remove_dir_all(ce).await?;
|
||||
io::remove_dir_all(ce).await?;
|
||||
send_ingress(&ingress, 10.0 * (1.0 / cleanable_nentries as f64), None).await?;
|
||||
}
|
||||
|
||||
|
|
@ -181,18 +182,18 @@ pub async fn set_directory(new: PathBuf) -> crate::Result<()> {
|
|||
}
|
||||
|
||||
/// checks if two paths are on different data storage devices
|
||||
fn is_different_fs(a: &Path, b: &Path) -> bool {
|
||||
let roota = a.components().next();
|
||||
let rootb = b.components().next();
|
||||
roota != rootb
|
||||
fn is_different_fs(first: &Path, second: &Path) -> bool {
|
||||
let first_root = first.components().next();
|
||||
let second_root = second.components().next();
|
||||
first_root != second_root
|
||||
}
|
||||
|
||||
/// checks if a [`PathBuf`] is writable or locked.
|
||||
pub async fn is_writable(path: PathBuf) -> crate::Result<bool> {
|
||||
let tmp = path.join(".tmp");
|
||||
match crate::utils::io::write(tmp.clone(), "test").await {
|
||||
Ok(_) => {
|
||||
crate::utils::io::remove_file(tmp).await?;
|
||||
match io::write(tmp.clone(), "test").await {
|
||||
Ok(()) => {
|
||||
io::remove_file(tmp).await?;
|
||||
Ok(true)
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//! **OneLauncher Constants**
|
||||
//! **`OneLauncher` Constants**
|
||||
//!
|
||||
//! Public constant variables and strings that are used throughout the launcher.
|
||||
//! Can be shared publically, any secrets should be stored in [`crate::store::credentials`].
|
||||
|
|
@ -39,7 +39,7 @@ pub const MODRINTH_V3_API_URL: &str = "https://api.modrinth.com/v3";
|
|||
pub const CURSEFORGE_API_URL: &str = "https://api.curseforge.com/";
|
||||
/// Our metadata API base url.
|
||||
pub const METADATA_API_URL: &str = "https://meta.polyfrost.org";
|
||||
/// https://mclo.gs/ API base url.
|
||||
/// <https://mclo.gs>/ API base url.
|
||||
pub const MCLOGS_API_URL: &str = "https://api.mclo.gs/1";
|
||||
|
||||
// =========== Paths ===========
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! **OneLauncher Errors**
|
||||
//! **`OneLauncher` Errors**
|
||||
//!
|
||||
//! Public wrappers around common OneLauncher errors.
|
||||
//! Public wrappers around common `OneLauncher` errors.
|
||||
|
||||
use tracing_error::InstrumentError;
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ pub enum ErrorKind {
|
|||
#[error("failed to watch file {0}")]
|
||||
NotifyError(#[from] notify::Error),
|
||||
|
||||
/// Wrapper around [`regex::Error`] to handle RegExp errors.
|
||||
/// Wrapper around [`regex::Error`] to handle `RegExp` errors.
|
||||
#[error("string verification with regex failed: {0}")]
|
||||
RegexError(#[from] regex::Error),
|
||||
|
||||
|
|
@ -99,11 +99,11 @@ pub enum ErrorKind {
|
|||
#[error("checksum {0} did not match {1}!")]
|
||||
HashError(String, String),
|
||||
|
||||
/// Wrapper around [`crate::utils::io::IOError`] to handle wrapped std::io errors.
|
||||
/// Wrapper around [`onelauncher_utils::io::IOError`] to handle wrapped `std::io` errors.
|
||||
#[error("error handling IO operations: {0}")]
|
||||
IOError(#[from] crate::utils::io::IOError),
|
||||
IOError(#[from] onelauncher_utils::io::IOError),
|
||||
|
||||
/// Wrapper around [`std::io::Error`] to handle non-wrapped std::io errors.
|
||||
/// Wrapper around [`std::io::Error`] to handle non-wrapped `std::io` errors.
|
||||
#[error("error handling standard IO operations: {0}")]
|
||||
StdIOError(#[from] std::io::Error),
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ use interpulse::utils::get_path_from_artifact;
|
|||
|
||||
use crate::constants::DUMMY_REPLACE_NEWLINE;
|
||||
use crate::store::{Memory, MinecraftCredentials, Resolution};
|
||||
use crate::utils::io::IOError;
|
||||
use crate::utils::platform::classpath_separator;
|
||||
use onelauncher_utils::io::IOError;
|
||||
use onelauncher_utils::platform::classpath_separator;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn java_arguments(
|
||||
|
|
@ -45,7 +45,7 @@ pub fn java_arguments(
|
|||
} else {
|
||||
parsed.push(format!(
|
||||
"-Djava.library.path={}",
|
||||
dunce::canonicalize(natives_path)
|
||||
onelauncher_utils::io::canonicalize(natives_path)
|
||||
.map_err(|_| anyhow::anyhow!(
|
||||
"specified natives path {} not found",
|
||||
natives_path.to_string_lossy()
|
||||
|
|
@ -146,12 +146,12 @@ pub fn processor_arguments<T: AsRef<str>>(
|
|||
)?
|
||||
} else {
|
||||
entry.client.clone()
|
||||
})
|
||||
});
|
||||
}
|
||||
} else if arg.as_ref().starts_with('[') {
|
||||
parsed.push(get_library(libraries_path, a, true)?)
|
||||
parsed.push(get_library(libraries_path, a, true)?);
|
||||
} else {
|
||||
parsed.push(arg.as_ref().to_string())
|
||||
parsed.push(arg.as_ref().to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -214,36 +214,15 @@ fn parse_minecraft_argument(
|
|||
.replace("${assets_index_name}", asset_index)
|
||||
.replace(
|
||||
"${game_directory}",
|
||||
&dunce::canonicalize(game_directory)
|
||||
.map_err(|_| {
|
||||
anyhow::anyhow!(
|
||||
"game directory {} doesn't exist",
|
||||
game_directory.to_string_lossy()
|
||||
)
|
||||
})?
|
||||
.to_string_lossy(),
|
||||
&onelauncher_utils::io::canonicalize(game_directory)?.to_string_lossy(),
|
||||
)
|
||||
.replace(
|
||||
"${assets_root}",
|
||||
&dunce::canonicalize(assets_directory)
|
||||
.map_err(|_| {
|
||||
anyhow::anyhow!(
|
||||
"assets directory {} doesn't exist",
|
||||
assets_directory.to_string_lossy()
|
||||
)
|
||||
})?
|
||||
.to_string_lossy(),
|
||||
&onelauncher_utils::io::canonicalize(assets_directory)?.to_string_lossy(),
|
||||
)
|
||||
.replace(
|
||||
"${game_assets}",
|
||||
&dunce::canonicalize(assets_directory)
|
||||
.map_err(|_| {
|
||||
anyhow::anyhow!(
|
||||
"assets directory {} doesn't exist",
|
||||
assets_directory.to_string_lossy()
|
||||
)
|
||||
})?
|
||||
.to_string_lossy(),
|
||||
&onelauncher_utils::io::canonicalize(assets_directory)?.to_string_lossy(),
|
||||
)
|
||||
.replace("${version_type}", version_type.as_str())
|
||||
.replace("${resolution_width}", &resolution.0.to_string())
|
||||
|
|
@ -263,25 +242,11 @@ fn parse_java_argument(
|
|||
Ok(argument
|
||||
.replace(
|
||||
"${natives_directory}",
|
||||
&dunce::canonicalize(natives_path)
|
||||
.map_err(|_| {
|
||||
anyhow::anyhow!(
|
||||
"natives path {} doesn't exist",
|
||||
natives_path.to_string_lossy()
|
||||
)
|
||||
})?
|
||||
.to_string_lossy(),
|
||||
&onelauncher_utils::io::canonicalize(natives_path)?.to_string_lossy(),
|
||||
)
|
||||
.replace(
|
||||
"${library_directory}",
|
||||
&dunce::canonicalize(libraries_path)
|
||||
.map_err(|_| {
|
||||
anyhow::anyhow!(
|
||||
"libraries path {} doesn't eixst",
|
||||
libraries_path.to_string_lossy()
|
||||
)
|
||||
})?
|
||||
.to_string_lossy(),
|
||||
&onelauncher_utils::io::canonicalize(libraries_path)?.to_string_lossy(),
|
||||
)
|
||||
.replace("${classpath_seperator}", classpath_separator(java_arch))
|
||||
.replace("${launcher_name}", crate::constants::NAME)
|
||||
|
|
@ -315,13 +280,7 @@ pub fn classpaths(
|
|||
.collect::<Result<HashSet<_>, _>>()?;
|
||||
|
||||
classpaths.insert(
|
||||
dunce::canonicalize(client_path)
|
||||
.map_err(|_| {
|
||||
anyhow::anyhow!(
|
||||
"specified classpath {} not found",
|
||||
client_path.to_string_lossy()
|
||||
)
|
||||
})?
|
||||
onelauncher_utils::io::canonicalize(client_path)?
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
);
|
||||
|
|
@ -357,8 +316,7 @@ pub fn get_library(
|
|||
return Ok(path.to_string_lossy().to_string());
|
||||
}
|
||||
|
||||
let path = &dunce::canonicalize(&path)
|
||||
.map_err(|_| anyhow::anyhow!("library file {} not found", path.to_string_lossy()))?;
|
||||
let path = &onelauncher_utils::io::canonicalize(&path)?;
|
||||
|
||||
Ok(path.to_string_lossy().to_string())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ use crate::proxy::send::send_ingress;
|
|||
use crate::proxy::utils::ingress_try_for_each;
|
||||
use crate::proxy::IngressId;
|
||||
use crate::store::State;
|
||||
use crate::utils::http::*;
|
||||
use crate::utils::io;
|
||||
use crate::utils::platform::OsExt;
|
||||
use crate::utils::http::{fetch, fetch_json, write};
|
||||
use onelauncher_utils::io;
|
||||
use onelauncher_utils::platform::OsExt;
|
||||
|
||||
#[tracing::instrument(skip(st, version))]
|
||||
pub async fn download_minecraft(
|
||||
|
|
@ -28,12 +28,7 @@ pub async fn download_minecraft(
|
|||
) -> crate::Result<()> {
|
||||
tracing::info!("downloading minecraft version {}", version.id);
|
||||
let assets_index = download_assets_index(st, version, Some(ingress), force).await?;
|
||||
let processors = if version
|
||||
.processors
|
||||
.as_ref()
|
||||
.map(|p| !p.is_empty())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let processors = if version.processors.as_ref().is_some_and(|p| !p.is_empty()) {
|
||||
25.0
|
||||
} else {
|
||||
40.0
|
||||
|
|
@ -294,11 +289,11 @@ pub async fn download_libraries(
|
|||
let reader = std::io::Cursor::new(&data);
|
||||
if let Ok(mut archive) = zip::ZipArchive::new(reader) {
|
||||
match archive.extract(st.directories.version_natives_dir(version).await) {
|
||||
Ok(_) => tracing::debug!("downloaded native {}", &lib.name),
|
||||
Ok(()) => tracing::debug!("downloaded native {}", &lib.name),
|
||||
Err(err) => tracing::error!("failed to download native {}: {}", &lib.name, err)
|
||||
}
|
||||
} else {
|
||||
tracing::error!("failed to extract native {}", &lib.name)
|
||||
tracing::error!("failed to extract native {}", &lib.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//! **OneLauncher Game**
|
||||
//! **`OneLauncher` Game**
|
||||
//!
|
||||
//! Manages, installs, and launches the core Minecraft game.
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ use crate::proxy::{IngressId, IngressType};
|
|||
use crate::store::{
|
||||
self as st, Cluster, ClusterStage, MinecraftCredentials, ProcessorChild, State,
|
||||
};
|
||||
use crate::utils::io::{self, IOError};
|
||||
use onelauncher_utils::io::{self, IOError};
|
||||
|
||||
use chrono::Utc;
|
||||
use interpulse as ip;
|
||||
|
|
@ -74,7 +74,7 @@ pub async fn install_minecraft(
|
|||
let instance_path = &io::canonicalize(cluster.get_full_path().await?)?;
|
||||
let metadata = state.metadata.read().await;
|
||||
|
||||
let version_manifest = metadata.minecraft.to_owned();
|
||||
let version_manifest = metadata.minecraft.clone();
|
||||
let versions = version_manifest
|
||||
.ok_or(anyhow::anyhow!("couldn't get minecraft manifest"))?
|
||||
.versions;
|
||||
|
|
@ -108,8 +108,7 @@ pub async fn install_minecraft(
|
|||
let key = version_info
|
||||
.java_version
|
||||
.as_ref()
|
||||
.map(|it| it.major_version)
|
||||
.unwrap_or(8);
|
||||
.map_or(8, |it| it.major_version);
|
||||
let (java_version, set_java) =
|
||||
if let Some(java_version) = java_version_from_cluster(cluster, &version_info).await? {
|
||||
(std::path::PathBuf::from(java_version.path), false)
|
||||
|
|
@ -177,8 +176,8 @@ pub async fn install_minecraft(
|
|||
}
|
||||
}
|
||||
|
||||
let cp = ref_owned!(cp = processor.classpath.clone() => {
|
||||
cp.push(processor.jar.clone())
|
||||
let cp = onelauncher_utils::ref_owned!(cp = processor.classpath.clone() => {
|
||||
cp.push(processor.jar.clone());
|
||||
});
|
||||
|
||||
let child = Command::new(&java_version.path)
|
||||
|
|
@ -223,10 +222,7 @@ pub async fn install_minecraft(
|
|||
send_ingress(
|
||||
&ingress,
|
||||
30.0 / total_length as f64,
|
||||
Some(&format!(
|
||||
"running forge processor {}/{}",
|
||||
index, total_length
|
||||
)),
|
||||
Some(&format!("running forge processor {index}/{total_length}")),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
|
@ -273,7 +269,7 @@ pub async fn launch_minecraft(
|
|||
let instance_path = cluster.get_full_path().await?;
|
||||
let instance_path = &io::canonicalize(instance_path)?;
|
||||
|
||||
let version_manifest = metadata.minecraft.to_owned();
|
||||
let version_manifest = metadata.minecraft.clone();
|
||||
let versions = version_manifest
|
||||
.ok_or(anyhow::anyhow!("couldn't get minecraft manifest"))?
|
||||
.versions;
|
||||
|
|
@ -316,7 +312,9 @@ pub async fn launch_minecraft(
|
|||
.join(format!("{version_jar}.jar"));
|
||||
let args = version_info.arguments.clone().unwrap_or_default();
|
||||
let mut command = match wrapper {
|
||||
Some(hook) => ref_owned!(it = Command::new(hook) => {it.arg(&java_version.path)}),
|
||||
Some(hook) => {
|
||||
onelauncher_utils::ref_owned!(it = Command::new(hook) => {it.arg(&java_version.path)})
|
||||
}
|
||||
None => Command::new(&java_version.path),
|
||||
};
|
||||
let env_args = Vec::from(env_args);
|
||||
|
|
@ -335,7 +333,7 @@ pub async fn launch_minecraft(
|
|||
.args(
|
||||
arguments::java_arguments(
|
||||
args.get(&ip::api::minecraft::ArgumentType::Jvm)
|
||||
.map(|x| x.as_slice()),
|
||||
.map(Vec::as_slice),
|
||||
&state.directories.version_natives_dir(&version_jar).await,
|
||||
&state.directories.libraries_dir().await,
|
||||
&arguments::classpaths(
|
||||
|
|
@ -350,14 +348,13 @@ pub async fn launch_minecraft(
|
|||
Vec::from(java_args),
|
||||
&java_version.arch,
|
||||
)?
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>(),
|
||||
.into_iter(),
|
||||
)
|
||||
.arg(version_info.main_class.clone())
|
||||
.args(
|
||||
arguments::minecraft_arguments(
|
||||
args.get(&ip::api::minecraft::ArgumentType::Game)
|
||||
.map(|a| a.as_slice()),
|
||||
.map(Vec::as_slice),
|
||||
version_info.minecraft_arguments.as_deref(),
|
||||
credentials,
|
||||
&version.id,
|
||||
|
|
@ -368,8 +365,7 @@ pub async fn launch_minecraft(
|
|||
*resolution,
|
||||
&java_version.arch,
|
||||
)?
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>(),
|
||||
.into_iter(),
|
||||
)
|
||||
.current_dir(instance_path.clone());
|
||||
|
||||
|
|
@ -393,10 +389,10 @@ pub async fn launch_minecraft(
|
|||
for (key, value) in mc_options {
|
||||
let re = regex::Regex::new(&format!(r"(?m)^{}:.*$", regex::escape(key)))?;
|
||||
if !re.is_match(&options_string) {
|
||||
options_string.push_str(&format!("\n{}:{}", key, value));
|
||||
options_string.push_str(&format!("\n{key}:{value}"));
|
||||
} else {
|
||||
let replaced_string = re
|
||||
.replace_all(&options_string, &format!("{}:{}", key, value))
|
||||
.replace_all(&options_string, &format!("{key}:{value}"))
|
||||
.to_string();
|
||||
options_string = replaced_string;
|
||||
}
|
||||
|
|
@ -416,16 +412,10 @@ pub async fn launch_minecraft(
|
|||
let mut censors = HashMap::new();
|
||||
let username = whoami::username();
|
||||
let realname = whoami::realname();
|
||||
censors.insert(format!("/{}/", username), "/{ENV_USERNAME}/".to_string());
|
||||
censors.insert(
|
||||
format!("\\{}\\", username),
|
||||
"\\{ENV_USERNAME}\\".to_string(),
|
||||
);
|
||||
censors.insert(format!("/{}/", realname), "/{ENV_REALNAME}/".to_string());
|
||||
censors.insert(
|
||||
format!("\\{}\\", realname),
|
||||
"\\{ENV_REALNAME}\\".to_string(),
|
||||
);
|
||||
censors.insert(format!("/{username}/"), "/{ENV_USERNAME}/".to_string());
|
||||
censors.insert(format!("\\{username}\\"), "\\{ENV_USERNAME}\\".to_string());
|
||||
censors.insert(format!("/{realname}/"), "/{ENV_REALNAME}/".to_string());
|
||||
censors.insert(format!("\\{realname}\\"), "\\{ENV_REALNAME}\\".to_string());
|
||||
censors.insert(
|
||||
credentials.access_token.clone(),
|
||||
"{MC_ACCESS_TOKEN}".to_string(),
|
||||
|
|
@ -483,10 +473,10 @@ pub fn rules(rules: &[ip::api::minecraft::Rule], java_version: &str, updated: bo
|
|||
.iter()
|
||||
.all(|r| matches!(r.action, RuleAction::Disallow))
|
||||
{
|
||||
rule.push(Some(true))
|
||||
rule.push(Some(true));
|
||||
}
|
||||
|
||||
!(rule.iter().any(|r| r == &Some(false)) || rule.iter().all(|r| r.is_none()))
|
||||
!(rule.iter().any(|r| r == &Some(false)) || rule.iter().all(Option::is_none))
|
||||
}
|
||||
|
||||
/// Parses a Minecraft library feature or OS rule.
|
||||
|
|
@ -500,7 +490,7 @@ pub fn rule(rule: &ip::api::minecraft::Rule, java_version: &str, updated: bool)
|
|||
let result = match rule {
|
||||
Rule {
|
||||
os: Some(ref os), ..
|
||||
} => crate::utils::platform::os_rule(os, java_version, updated),
|
||||
} => onelauncher_utils::platform::os_rule(os, java_version, updated),
|
||||
Rule {
|
||||
features: Some(ref features),
|
||||
..
|
||||
|
|
@ -543,8 +533,7 @@ pub async fn java_version_from_cluster(
|
|||
let key = version_info
|
||||
.java_version
|
||||
.as_ref()
|
||||
.map(|x| x.major_version)
|
||||
.unwrap_or(8);
|
||||
.map_or(8, |x| x.major_version);
|
||||
|
||||
let state = State::get().await?;
|
||||
let settings = state.settings.read().await;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,43 @@
|
|||
//! # OneLauncher
|
||||
//! # `OneLauncher`
|
||||
//!
|
||||
//! A library used as a core for our launcher and Rust APIs.
|
||||
|
||||
#![warn(unused_import_braces)]
|
||||
#![warn(
|
||||
clippy::all,
|
||||
clippy::pedantic,
|
||||
clippy::correctness,
|
||||
clippy::perf,
|
||||
clippy::style,
|
||||
clippy::suspicious,
|
||||
clippy::complexity,
|
||||
clippy::nursery,
|
||||
clippy::unwrap_used,
|
||||
unused_qualifications,
|
||||
rust_2018_idioms,
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unused_allocation,
|
||||
clippy::unnecessary_cast,
|
||||
clippy::cast_lossless,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_possible_wrap,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::dbg_macro,
|
||||
unused_import_braces,
|
||||
clippy::deprecated_cfg_attr,
|
||||
clippy::separated_literal_suffix,
|
||||
deprecated
|
||||
)]
|
||||
#![forbid(deprecated_in_future)]
|
||||
#![deny(unused_must_use)]
|
||||
#![allow(
|
||||
clippy::missing_errors_doc,
|
||||
clippy::future_not_send,
|
||||
clippy::module_name_repetitions,
|
||||
clippy::struct_field_names,
|
||||
clippy::cast_precision_loss,
|
||||
clippy::significant_drop_tightening //tmp
|
||||
)]
|
||||
|
||||
#[macro_use]
|
||||
pub mod utils;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
//! **OneLauncher Logger**
|
||||
//! **`OneLauncher` Logger**
|
||||
//!
|
||||
//! Public utilities for [`tracing`] logging in OneLauncher.
|
||||
//! Public utilities for [`tracing`] logging in `OneLauncher`.
|
||||
//!
|
||||
//! - `RUST_LOG=xxx` is a variable which defines tracing configuration (`ERROR > WARN > INFO > DEBUG > TRACE`)
|
||||
//! - `RUST_LOG="onelauncher=trace"` will show **all** mesages from OneLauncher and not other dependencies.
|
||||
//! - `RUST_LOG="onelauncher=trace"` will show **all** mesages from `OneLauncher` and not other dependencies.
|
||||
//! - `RUST_LOG="info"` will show info, warn, and error logs.
|
||||
//!
|
||||
//! Errors returned to [`tauri`] are displayed as tracing error logs.
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
//! Tracing errors also provide Sentry metadata to be handled.
|
||||
//! To set the environment variable on `unix` you can use the following command: `RUST_LOG="xxx" {your command}`
|
||||
//!
|
||||
//! The default `RUST_LOG` is `onelauncher=show`, meaning only logs from OneLauncher will be displayed at the info, warn, and error levels.
|
||||
//! The default `RUST_LOG` is `onelauncher=show`, meaning only logs from `OneLauncher` will be displayed at the info, warn, and error levels.
|
||||
|
||||
use tracing_appender::non_blocking::WorkerGuard;
|
||||
|
||||
|
|
@ -19,6 +19,7 @@ use tracing_appender::non_blocking::WorkerGuard;
|
|||
|
||||
/// Initialize the global [`tracing`] logger in development.
|
||||
#[cfg(debug_assertions)]
|
||||
#[must_use]
|
||||
pub fn start_logger() -> Option<WorkerGuard> {
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ use crate::constants::CLUSTER_FILE;
|
|||
use crate::proxy::send::{send_cluster, send_message};
|
||||
use crate::proxy::ClusterPayloadType;
|
||||
use crate::utils::http::{write_icon, IoSemaphore};
|
||||
use crate::utils::io::{self, IOError};
|
||||
use crate::utils::java::JavaVersion;
|
||||
use crate::State;
|
||||
use onelauncher_utils::io::{self, IOError};
|
||||
|
||||
use super::{Directories, InitHooks, Memory, PackageType, Resolution};
|
||||
|
||||
/// Core Cluster state manager with a [`HashMap<ClusterPath, Cluster>`].
|
||||
pub(crate) struct Clusters(pub HashMap<ClusterPath, Cluster>);
|
||||
pub struct Clusters(pub HashMap<ClusterPath, Cluster>);
|
||||
|
||||
/// Core Cluster stages used in package logic.
|
||||
#[cfg_attr(feature = "specta", derive(specta::Type))]
|
||||
|
|
@ -40,7 +40,8 @@ pub enum ClusterStage {
|
|||
}
|
||||
|
||||
impl ClusterStage {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
#[must_use]
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match *self {
|
||||
Self::Installed => "installed",
|
||||
Self::Downloading => "downloading",
|
||||
|
|
@ -73,7 +74,7 @@ pub struct ClusterPath(pub PathBuf);
|
|||
impl ClusterPath {
|
||||
/// Get a new [`ClusterPath`] instance from an [`Into<PathBuf>`].
|
||||
pub fn new(path: impl Into<PathBuf>) -> Self {
|
||||
ClusterPath(path.into())
|
||||
Self(path.into())
|
||||
}
|
||||
|
||||
pub async fn find_by_uuid(uuid: Uuid) -> crate::Result<Self> {
|
||||
|
|
@ -106,7 +107,7 @@ impl ClusterPath {
|
|||
}
|
||||
|
||||
/// Validate the cluster and clone the current [`ClusterPath`].
|
||||
pub async fn cluster_path(&self) -> crate::Result<ClusterPath> {
|
||||
pub async fn cluster_path(&self) -> crate::Result<Self> {
|
||||
if let Some(c) = crate::cluster::get(self).await? {
|
||||
Ok(c.cluster_path())
|
||||
} else {
|
||||
|
|
@ -146,6 +147,7 @@ pub struct InnerPathLinux(pub String);
|
|||
|
||||
impl InnerPathLinux {
|
||||
/// Get the first 2 components of the inner path.
|
||||
#[must_use]
|
||||
pub fn get_components(&self) -> String {
|
||||
self.to_string()
|
||||
.split('/')
|
||||
|
|
@ -163,7 +165,7 @@ impl std::fmt::Display for InnerPathLinux {
|
|||
|
||||
impl From<RawPackagePath> for InnerPathLinux {
|
||||
fn from(value: RawPackagePath) -> Self {
|
||||
InnerPathLinux(value.0.replace('\\', "/"))
|
||||
Self(value.0.replace('\\', "/"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -174,7 +176,7 @@ struct RawPackagePath(pub String);
|
|||
|
||||
impl From<InnerPathLinux> for RawPackagePath {
|
||||
fn from(value: InnerPathLinux) -> Self {
|
||||
RawPackagePath(value.0)
|
||||
Self(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -210,6 +212,7 @@ impl PackagePath {
|
|||
}
|
||||
|
||||
/// Get the [`InnerPathLinux`] of a [`PackagePath`].
|
||||
#[must_use]
|
||||
pub fn inner_path(&self) -> InnerPathLinux {
|
||||
InnerPathLinux(
|
||||
self.0
|
||||
|
|
@ -220,9 +223,10 @@ impl PackagePath {
|
|||
)
|
||||
}
|
||||
|
||||
/// Create a new PackagePath from a relative path.
|
||||
/// Create a new `PackagePath` from a relative path.
|
||||
#[must_use]
|
||||
pub fn new(path: &Path) -> Self {
|
||||
PackagePath(PathBuf::from(path))
|
||||
Self(PathBuf::from(path))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -319,7 +323,8 @@ pub struct PackageData {
|
|||
pub locked: Option<bool>,
|
||||
}
|
||||
|
||||
pub fn default_locked() -> Option<bool> {
|
||||
#[must_use]
|
||||
pub const fn default_locked() -> Option<bool> {
|
||||
Some(true)
|
||||
}
|
||||
|
||||
|
|
@ -331,11 +336,11 @@ pub enum Loader {
|
|||
/// The default vanilla loader, no modding supported.
|
||||
#[default]
|
||||
Vanilla,
|
||||
/// The MinecraftForge Minecraft mod loader.
|
||||
/// The `MinecraftForge` Minecraft mod loader.
|
||||
Forge,
|
||||
/// The FabircMC Minecraft mod loader.
|
||||
/// The `FabircMC` Minecraft mod loader.
|
||||
Fabric,
|
||||
/// The NeoForge Minecraft mod loader.
|
||||
/// The `NeoForge` Minecraft mod loader.
|
||||
NeoForge,
|
||||
/// The Quilt Minecraft mod loader.
|
||||
Quilt,
|
||||
|
|
@ -344,7 +349,8 @@ pub enum Loader {
|
|||
}
|
||||
|
||||
impl Loader {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
#[must_use]
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match *self {
|
||||
Self::Vanilla => "vanilla",
|
||||
Self::Forge => "forge",
|
||||
|
|
@ -355,7 +361,8 @@ impl Loader {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn as_meta_str(&self) -> &'static str {
|
||||
#[must_use]
|
||||
pub const fn as_meta_str(&self) -> &'static str {
|
||||
match *self {
|
||||
Self::Vanilla => "vanilla",
|
||||
Self::Forge => "forge",
|
||||
|
|
@ -366,6 +373,7 @@ impl Loader {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from_string(val: &str) -> Self {
|
||||
match val {
|
||||
"vanilla" => Self::Vanilla,
|
||||
|
|
@ -377,7 +385,8 @@ impl Loader {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn supports_mods(&self) -> bool {
|
||||
#[must_use]
|
||||
pub const fn supports_mods(&self) -> bool {
|
||||
!matches!(self, Self::Vanilla)
|
||||
}
|
||||
}
|
||||
|
|
@ -463,6 +472,7 @@ impl Cluster {
|
|||
|
||||
/// Get the [`ClusterPath`] of the specified [`Cluster`].
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn cluster_path(&self) -> ClusterPath {
|
||||
ClusterPath::new(&self.path)
|
||||
}
|
||||
|
|
@ -645,7 +655,7 @@ impl Clusters {
|
|||
|
||||
{
|
||||
let clusters = state.clusters.read().await;
|
||||
for (cluster_path, cluster) in clusters.0.iter() {
|
||||
for (cluster_path, cluster) in &clusters.0 {
|
||||
if let Some(package_data) = &cluster.meta.package_data {
|
||||
if let Some(linked_package) = &package_data.package_id {
|
||||
updateable.push((cluster_path.clone(), linked_package.clone()));
|
||||
|
|
@ -679,7 +689,7 @@ impl Clusters {
|
|||
cluster.uuid,
|
||||
&cluster.cluster_path(),
|
||||
&cluster.meta.name,
|
||||
crate::proxy::ClusterPayloadType::Inserted,
|
||||
ClusterPayloadType::Inserted,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use super::Directories;
|
||||
use crate::utils::http::IoSemaphore;
|
||||
use iota_stronghold::{KeyProvider, SnapshotPath};
|
||||
use iota_stronghold::{KeyProvider, SnapshotPath, Stronghold};
|
||||
use serde::{Serialize, Serializer};
|
||||
use std::ops::Deref;
|
||||
use std::path::Path;
|
||||
|
|
@ -11,9 +11,9 @@ use zeroize::Zeroizing;
|
|||
/// A K/V encrypted store to handle digital secrets using IOTA Stronghold.
|
||||
#[derive(Debug)]
|
||||
pub struct Credentials {
|
||||
inner: iota_stronghold::Stronghold,
|
||||
path: iota_stronghold::SnapshotPath,
|
||||
keyprovider: iota_stronghold::KeyProvider,
|
||||
inner: Stronghold,
|
||||
path: SnapshotPath,
|
||||
keyprovider: KeyProvider,
|
||||
}
|
||||
|
||||
impl Credentials {
|
||||
|
|
@ -25,7 +25,7 @@ impl Credentials {
|
|||
_io_semaphore: &IoSemaphore,
|
||||
) -> crate::Result<Self> {
|
||||
let path = SnapshotPath::from_path(path);
|
||||
let stronghold = iota_stronghold::Stronghold::default();
|
||||
let stronghold = Stronghold::default();
|
||||
let keyprovider = KeyProvider::try_from(password).map_err(StrongholdError::MemoryError)?;
|
||||
if path.exists() {
|
||||
stronghold
|
||||
|
|
@ -46,13 +46,13 @@ impl Credentials {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &iota_stronghold::Stronghold {
|
||||
pub const fn inner(&self) -> &Stronghold {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Credentials {
|
||||
type Target = iota_stronghold::Stronghold;
|
||||
type Target = Stronghold;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ pub enum StrongholdError {
|
|||
}
|
||||
|
||||
impl Serialize for StrongholdError {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ pub struct Directories {
|
|||
|
||||
impl Directories {
|
||||
/// Bootstrap the initial settings directory on first startup.
|
||||
#[must_use]
|
||||
pub fn init_settings_dir() -> Option<PathBuf> {
|
||||
Self::env_path("ONELAUNCHER_CONFIG")
|
||||
.or_else(|| Some(dirs::data_dir()?.join(constants::NAME)))
|
||||
|
|
@ -76,6 +77,7 @@ impl Directories {
|
|||
|
||||
/// Bootstrap the core logs directory and get it as a [`PathBuf`].
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn logs_dir() -> Option<PathBuf> {
|
||||
Self::init_settings_dir().map(|d| d.join("logs"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ impl DiscordRPC {
|
|||
};
|
||||
|
||||
let client = Arc::new(RwLock::new(discord_ipc));
|
||||
Ok(DiscordRPC { client, connected })
|
||||
Ok(Self { client, connected })
|
||||
}
|
||||
|
||||
/// Set the Discord IPC activity with a message and try to reconnect if the initial connection fails.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::path::PathBuf;
|
|||
use crate::utils::java;
|
||||
use crate::utils::java::JavaVersion;
|
||||
|
||||
/// A HashMap of all located and installed available Java versions
|
||||
/// A `HashMap` of all located and installed available Java versions
|
||||
#[cfg_attr(feature = "specta", derive(specta::Type))]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct JavaVersions(HashMap<String, JavaVersion>);
|
||||
|
|
@ -19,9 +19,10 @@ impl Default for JavaVersions {
|
|||
}
|
||||
|
||||
impl JavaVersions {
|
||||
/// Create an empty JavaVersions `HashMap`.
|
||||
pub fn new() -> JavaVersions {
|
||||
JavaVersions(HashMap::new())
|
||||
/// Create an empty `JavaVersions` `HashMap`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self(HashMap::new())
|
||||
}
|
||||
|
||||
/// Inserts a key-value pair into the map.
|
||||
|
|
@ -35,6 +36,7 @@ impl JavaVersions {
|
|||
}
|
||||
|
||||
/// Returns a reference to the value corresponding to the key.
|
||||
#[must_use]
|
||||
pub fn get(&self, key: &String) -> Option<&JavaVersion> {
|
||||
self.0.get(key)
|
||||
}
|
||||
|
|
@ -45,18 +47,20 @@ impl JavaVersions {
|
|||
}
|
||||
|
||||
/// Returns the number of elements in the map.
|
||||
#[must_use]
|
||||
pub fn count(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
/// A collection visiting all keys in arbitrary order.
|
||||
#[must_use]
|
||||
pub fn keys(&self) -> Vec<String> {
|
||||
self.0.keys().cloned().collect()
|
||||
}
|
||||
|
||||
/// Validates all stored java versions.
|
||||
pub async fn validate(&self) -> bool {
|
||||
for (_, java) in self.0.iter() {
|
||||
for java in self.0.values() {
|
||||
let runtime = java::check_java_instance(PathBuf::from(&java.path).as_path()).await;
|
||||
if let Some(runtime) = runtime {
|
||||
if runtime.version != java.version {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::constants::METADATA_API_URL;
|
||||
use crate::utils::http::{fetch, read_json, write, FetchSemaphore, IoSemaphore};
|
||||
use crate::utils::io::copy;
|
||||
use crate::State;
|
||||
|
||||
use super::Directories;
|
||||
|
|
@ -20,7 +19,7 @@ pub struct Metadata {
|
|||
pub fabric: Option<ModdedManifest>,
|
||||
/// The [`ModdedManifest`] associated with core Quilt versions.
|
||||
pub quilt: Option<ModdedManifest>,
|
||||
/// The [`ModdedManifest`] associated with core NeoForge versions.
|
||||
/// The [`ModdedManifest`] associated with core `NeoForge` versions.
|
||||
pub neoforge: Option<ModdedManifest>,
|
||||
/// The [`ModdedManifest`] associated with core Forge versions.
|
||||
pub forge: Option<ModdedManifest>,
|
||||
|
|
@ -154,7 +153,7 @@ impl Metadata {
|
|||
let backup = dirs.caches_dir().await.join("metadata.json.bak");
|
||||
let mut should_write = false;
|
||||
|
||||
if let Ok(mut metadata_json) = read_json::<Metadata>(&path, io_semaphore).await {
|
||||
if let Ok(mut metadata_json) = read_json::<Self>(&path, io_semaphore).await {
|
||||
should_write = metadata_json.fetch_errored(fetch_semaphore).await?;
|
||||
|
||||
metadata = Some(metadata_json);
|
||||
|
|
@ -171,12 +170,12 @@ impl Metadata {
|
|||
match res {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
tracing::warn!("failed to fetch metadata: {err}")
|
||||
tracing::warn!("failed to fetch metadata: {err}");
|
||||
}
|
||||
}
|
||||
} else if let Ok(metadata_json) = read_json::<Metadata>(&backup, io_semaphore).await {
|
||||
} else if let Ok(metadata_json) = read_json::<Self>(&backup, io_semaphore).await {
|
||||
metadata = Some(metadata_json);
|
||||
copy(&backup, &path).await?;
|
||||
onelauncher_utils::io::copy(&backup, &path).await?;
|
||||
}
|
||||
|
||||
if should_write {
|
||||
|
|
@ -205,7 +204,7 @@ impl Metadata {
|
|||
pub async fn update() {
|
||||
let res = async {
|
||||
let state = State::get().await?;
|
||||
let fetch_data = Metadata::fetch(&state.fetch_semaphore).await?;
|
||||
let fetch_data = Self::fetch(&state.fetch_semaphore).await?;
|
||||
|
||||
let path = state.directories.caches_dir().await.join("metadata.json");
|
||||
let backup = state
|
||||
|
|
@ -215,7 +214,7 @@ impl Metadata {
|
|||
.join("metadata.json.bak");
|
||||
|
||||
if path.exists() {
|
||||
copy(&path, &backup).await?;
|
||||
onelauncher_utils::io::copy(&path, &backup).await?;
|
||||
}
|
||||
|
||||
write(
|
||||
|
|
@ -235,7 +234,7 @@ impl Metadata {
|
|||
match res {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
tracing::warn!("failed to update launcher metadata: {err}")
|
||||
tracing::warn!("failed to update launcher metadata: {err}");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -247,9 +246,8 @@ async fn fetch_version_manifest(
|
|||
) -> crate::Result<MinecraftManifest> {
|
||||
let url = url.unwrap_or(interpulse::api::minecraft::VERSION_MANIFEST_URL);
|
||||
Ok(serde_json::from_slice(
|
||||
&fetch(url, None, semaphore).await.map_err(|err| {
|
||||
&fetch(url, None, semaphore).await.inspect_err(|_| {
|
||||
tracing::error!("couldn't fetch version manifest at '{}'", url);
|
||||
err
|
||||
})?,
|
||||
)?)
|
||||
}
|
||||
|
|
@ -259,9 +257,8 @@ async fn fetch_modded_manifest(
|
|||
semaphore: &FetchSemaphore,
|
||||
) -> crate::Result<ModdedManifest> {
|
||||
Ok(serde_json::from_slice(
|
||||
&fetch(url, None, semaphore).await.map_err(|err| {
|
||||
&fetch(url, None, semaphore).await.inspect_err(|_| {
|
||||
tracing::error!("couldn't fetch modded manifest at '{}'", url);
|
||||
err
|
||||
})?,
|
||||
)?)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -880,7 +880,7 @@ async fn send_signed_request<T: serde::de::DeserializeOwned>(
|
|||
let auth = authorization.map_or(Vec::new(), |v| v.as_bytes().to_vec());
|
||||
let body = serde_json::to_vec(&raw_body)
|
||||
.map_err(|source| MinecraftAuthError::SerializeError { step, source })?;
|
||||
let time: u128 = { ((current_date.timestamp() as u128) + 11644473600) * 10000000 };
|
||||
let time: u128 = { ((current_date.timestamp() as u128) + 11_644_473_600) * 10_000_000 };
|
||||
|
||||
use byteorder::WriteBytesExt;
|
||||
let mut buffer = Vec::new();
|
||||
|
|
@ -896,7 +896,7 @@ async fn send_signed_request<T: serde::de::DeserializeOwned>(
|
|||
buffer
|
||||
.write_u8(0)
|
||||
.map_err(|source| MinecraftAuthError::SigningError { step, source })?;
|
||||
buffer.extend_from_slice("POST".as_bytes());
|
||||
buffer.extend_from_slice(b"POST");
|
||||
buffer
|
||||
.write_u8(0)
|
||||
.map_err(|source| MinecraftAuthError::SigningError { step, source })?;
|
||||
|
|
@ -975,8 +975,7 @@ fn get_date_header(headers: &reqwest::header::HeaderMap) -> DateTime<Utc> {
|
|||
.get(reqwest::header::DATE)
|
||||
.and_then(|x| x.to_str().ok())
|
||||
.and_then(|x| DateTime::parse_from_rfc2822(x).ok())
|
||||
.map(|x| x.with_timezone(&Utc))
|
||||
.unwrap_or(Utc::now())
|
||||
.map_or(Utc::now(), |x| x.with_timezone(&Utc))
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//! **OneLauncher Store**
|
||||
//! **`OneLauncher` Store**
|
||||
//!
|
||||
//! Core state and storage management for the launcher, managing all states.
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ pub use self::oneconfig::*;
|
|||
/// The static [`OnceCell<RwLock<State>>`] for storing the global runtime launcher state.
|
||||
static LAUNCHER_STATE: OnceCell<RwLock<State>> = OnceCell::const_new();
|
||||
|
||||
/// The public OneLauncher state structure.
|
||||
/// The public `OneLauncher` state structure.
|
||||
pub struct State {
|
||||
/// Handles a boolean to check if the launcher is connected to the internet
|
||||
pub offline: RwLock<bool>,
|
||||
|
|
@ -73,7 +73,7 @@ pub struct State {
|
|||
pub users: RwLock<MinecraftState>,
|
||||
/// Handles processes
|
||||
pub processor: RwLock<Processor>,
|
||||
/// Handles OneConfig integration with [`async-tungstenite`]
|
||||
/// Handles `OneConfig` integration with [`async-tungstenite`]
|
||||
pub oneconfig: RwLock<Option<OneConfig>>,
|
||||
/// Handles secure credential management with [`iota_stronghold`].
|
||||
pub credentials: RwLock<Option<Credentials>>,
|
||||
|
|
@ -113,7 +113,7 @@ impl State {
|
|||
/// Initializes the core OneLauncher state fully.
|
||||
#[tracing::instrument]
|
||||
#[onelauncher_macros::memory]
|
||||
async fn initialize() -> crate::Result<RwLock<State>> {
|
||||
async fn initialize() -> crate::Result<RwLock<Self>> {
|
||||
let ingress =
|
||||
init_ingress_internal(crate::IngressType::Initialize, 100.0, "initializing state")
|
||||
.await?;
|
||||
|
|
@ -195,7 +195,7 @@ impl State {
|
|||
/// Updates all data if we are connected to the internet.
|
||||
pub fn update() {
|
||||
tokio::task::spawn(async {
|
||||
if let Ok(state) = crate::State::get().await {
|
||||
if let Ok(state) = Self::get().await {
|
||||
if !*state.offline.read().await {
|
||||
let version_up = Clusters::update_versions();
|
||||
let meta_up = Metadata::update();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
//! OneConfig state using [`async_tungstenite`] to communicate to the OneConfig server
|
||||
//! `OneConfig` state using [`async_tungstenite`] to communicate to the `OneConfig` server
|
||||
|
||||
use super::ClusterPath;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Wrapper over an [`async_tungstenite::WebSocketStream`] to connect to the OneConfig server.
|
||||
/// Wrapper over an [`async_tungstenite::WebSocketStream`] to connect to the `OneConfig` server.
|
||||
pub struct OneConfig {
|
||||
/// The public tokio socket connection.
|
||||
pub socket: async_tungstenite::WebSocketStream<async_tungstenite::tokio::ConnectStream>,
|
||||
|
|
@ -22,7 +22,7 @@ pub struct ModUpdatePacket {
|
|||
}
|
||||
|
||||
impl OneConfig {
|
||||
/// Initializes a OneConfig socket connection on localhost port `4023`.
|
||||
/// Initializes a `OneConfig` socket connection on localhost port `4023`.
|
||||
pub async fn initialize(cluster: ClusterPath) -> crate::Result<Self> {
|
||||
let (socket, _) = async_tungstenite::tokio::connect_async(format!(
|
||||
"wss://localhost:4023/oneconfig/ws?cluster={cluster}"
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
use crate::package::content::Providers;
|
||||
use crate::store::Loader;
|
||||
use crate::utils::http::{write_icon, IoSemaphore};
|
||||
use crate::utils::io;
|
||||
use async_zip::tokio::read::fs::ZipFileReader;
|
||||
use chrono::{DateTime, Utc};
|
||||
use onelauncher_utils::io;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -34,58 +34,63 @@ pub enum PackageType {
|
|||
|
||||
impl PackageType {
|
||||
/// attempt to get a [`PackageType`] from a loader string.
|
||||
#[must_use]
|
||||
pub fn from_loader(loaders: Vec<String>) -> Option<Self> {
|
||||
if loaders
|
||||
.iter()
|
||||
.any(|x| ["fabric", "forge", "quilt"].contains(&&**x))
|
||||
{
|
||||
Some(PackageType::Mod)
|
||||
Some(Self::Mod)
|
||||
} else if loaders.iter().any(|x| x == "datapack") {
|
||||
Some(PackageType::DataPack)
|
||||
Some(Self::DataPack)
|
||||
} else if loaders.iter().any(|x| ["iris", "optifine"].contains(&&**x)) {
|
||||
Some(PackageType::ShaderPack)
|
||||
Some(Self::ShaderPack)
|
||||
} else if loaders
|
||||
.iter()
|
||||
.any(|x| ["vanilla", "canvas", "minecraft"].contains(&&**x))
|
||||
{
|
||||
Some(PackageType::ResourcePack)
|
||||
Some(Self::ResourcePack)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from_parent(path: PathBuf) -> Option<Self> {
|
||||
let path = path.parent()?.file_name()?;
|
||||
match path.to_str()? {
|
||||
"mods" => Some(PackageType::Mod),
|
||||
"datapacks" => Some(PackageType::DataPack),
|
||||
"resourcepacks" => Some(PackageType::ResourcePack),
|
||||
"shaderpacks" => Some(PackageType::ShaderPack),
|
||||
"modpack" => Some(PackageType::ModPack),
|
||||
"mods" => Some(Self::Mod),
|
||||
"datapacks" => Some(Self::DataPack),
|
||||
"resourcepacks" => Some(Self::ResourcePack),
|
||||
"shaderpacks" => Some(Self::ShaderPack),
|
||||
"modpack" => Some(Self::ModPack),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> &'static str {
|
||||
#[must_use]
|
||||
pub const fn get_name(&self) -> &'static str {
|
||||
match self {
|
||||
PackageType::Mod => "mod",
|
||||
PackageType::DataPack => "datapack",
|
||||
PackageType::ResourcePack => "resourcepack",
|
||||
PackageType::ShaderPack => "shaderpack",
|
||||
PackageType::ModPack => "modpack",
|
||||
Self::Mod => "mod",
|
||||
Self::DataPack => "datapack",
|
||||
Self::ResourcePack => "resourcepack",
|
||||
Self::ShaderPack => "shaderpack",
|
||||
Self::ModPack => "modpack",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_folder(&self) -> &'static str {
|
||||
#[must_use]
|
||||
pub const fn get_folder(&self) -> &'static str {
|
||||
match self {
|
||||
PackageType::Mod => "mods",
|
||||
PackageType::DataPack => "datapacks",
|
||||
PackageType::ResourcePack => "resourcepacks",
|
||||
PackageType::ShaderPack => "shaderpacks",
|
||||
PackageType::ModPack => "modpack",
|
||||
Self::Mod => "mods",
|
||||
Self::DataPack => "datapacks",
|
||||
Self::ResourcePack => "resourcepacks",
|
||||
Self::ShaderPack => "shaderpacks",
|
||||
Self::ModPack => "modpack",
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_meta(&self) -> PathBuf {
|
||||
PathBuf::from(format!(
|
||||
"{}/{}",
|
||||
|
|
@ -94,6 +99,7 @@ impl PackageType {
|
|||
))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_meta_file_name(&self) -> String {
|
||||
String::from(".packages.json")
|
||||
}
|
||||
|
|
@ -104,7 +110,7 @@ impl PackageType {
|
|||
}
|
||||
|
||||
Ok(match self {
|
||||
PackageType::Mod => {
|
||||
Self::Mod => {
|
||||
entry.file_type().await?.is_file()
|
||||
&& entry.path().extension() == Some("jar".as_ref())
|
||||
}
|
||||
|
|
@ -112,19 +118,19 @@ impl PackageType {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn iterator() -> impl Iterator<Item = PackageType> {
|
||||
pub fn iterator() -> impl Iterator<Item = Self> {
|
||||
[
|
||||
PackageType::Mod,
|
||||
PackageType::DataPack,
|
||||
PackageType::ResourcePack,
|
||||
PackageType::ShaderPack,
|
||||
Self::Mod,
|
||||
Self::DataPack,
|
||||
Self::ResourcePack,
|
||||
Self::ShaderPack,
|
||||
]
|
||||
.iter()
|
||||
.copied()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "specta", derive(specta::Type))]
|
||||
pub struct SearchResult {
|
||||
pub slug: String,
|
||||
|
|
@ -189,7 +195,7 @@ impl Package {
|
|||
|
||||
pub type PackagesMap = HashMap<PackagePath, Package>;
|
||||
|
||||
pub(crate) struct Packages {
|
||||
pub struct Packages {
|
||||
managers: HashMap<ClusterPath, PackageManager>,
|
||||
}
|
||||
|
||||
|
|
@ -218,6 +224,7 @@ impl Packages {
|
|||
Self { managers }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get(&self, cluster_path: &ClusterPath) -> Option<&PackageManager> {
|
||||
self.managers.get(cluster_path)
|
||||
}
|
||||
|
|
@ -235,6 +242,7 @@ pub struct PackagesMeta {
|
|||
}
|
||||
|
||||
impl PackagesMeta {
|
||||
#[must_use]
|
||||
pub fn new(package_type: PackageType) -> Self {
|
||||
Self {
|
||||
packages: PackagesMap::new(),
|
||||
|
|
@ -260,6 +268,7 @@ impl PackageManager {
|
|||
Ok(manager)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn new(cluster_path: ClusterPath) -> Self {
|
||||
Self {
|
||||
cluster_path,
|
||||
|
|
@ -282,7 +291,8 @@ impl PackageManager {
|
|||
.join(package_type.get_meta()))
|
||||
}
|
||||
|
||||
/// Get the PackagesMeta for a specific package type. Does not sync.
|
||||
/// Get the `PackagesMeta` for a specific package type. Does not sync.
|
||||
#[must_use]
|
||||
pub fn get(&self, package_type: PackageType) -> &PackagesMeta {
|
||||
match package_type {
|
||||
PackageType::Mod => &self.mods,
|
||||
|
|
@ -293,7 +303,7 @@ impl PackageManager {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the PackagesMeta for a specific package type. Does not sync.
|
||||
/// Get the `PackagesMeta` for a specific package type. Does not sync.
|
||||
fn get_mut(&mut self, package_type: PackageType) -> &mut PackagesMeta {
|
||||
match package_type {
|
||||
PackageType::Mod => &mut self.mods,
|
||||
|
|
@ -482,7 +492,7 @@ impl PackageManager {
|
|||
}
|
||||
|
||||
if packages.len() != packages_to_keep.len() {
|
||||
packages.retain(|pkg_path, _| packages_to_keep.contains(pkg_path))
|
||||
packages.retain(|pkg_path, _| packages_to_keep.contains(pkg_path));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -538,6 +548,7 @@ pub enum PackageMetadata {
|
|||
}
|
||||
|
||||
impl PackageMetadata {
|
||||
#[must_use]
|
||||
pub fn from_managed_package(package: ManagedPackage, version: ManagedVersion) -> Self {
|
||||
Self::Managed {
|
||||
package_id: package.id,
|
||||
|
|
@ -549,11 +560,12 @@ impl PackageMetadata {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_package_type(&self) -> Option<PackageType> {
|
||||
#[must_use]
|
||||
pub const fn get_package_type(&self) -> Option<PackageType> {
|
||||
match self {
|
||||
PackageMetadata::Managed { package_type, .. } => Some(*package_type),
|
||||
PackageMetadata::Mapped { package_type, .. } => *package_type,
|
||||
PackageMetadata::Unknown => None,
|
||||
Self::Managed { package_type, .. } => Some(*package_type),
|
||||
Self::Mapped { package_type, .. } => *package_type,
|
||||
Self::Unknown => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -576,7 +588,7 @@ pub struct ManagedUser {
|
|||
pub role: Option<String>,
|
||||
}
|
||||
|
||||
fn default_is_organization_user() -> bool {
|
||||
const fn default_is_organization_user() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
|
@ -623,7 +635,7 @@ pub struct ManagedPackage {
|
|||
}
|
||||
|
||||
#[cfg_attr(feature = "specta", derive(specta::Type))]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct License {
|
||||
#[serde(default)]
|
||||
pub id: String,
|
||||
|
|
@ -658,6 +670,7 @@ pub struct ManagedVersion {
|
|||
}
|
||||
|
||||
impl ManagedVersion {
|
||||
#[must_use]
|
||||
pub fn get_primary_file(&self) -> Option<&ManagedVersionFile> {
|
||||
self.files.iter().find(|f| f.primary)
|
||||
}
|
||||
|
|
@ -665,7 +678,7 @@ impl ManagedVersion {
|
|||
|
||||
/// Universal interface for managed package files.
|
||||
#[cfg_attr(feature = "specta", derive(specta::Type))]
|
||||
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
|
||||
pub struct ManagedVersionFile {
|
||||
pub url: String,
|
||||
pub file_name: String,
|
||||
|
|
@ -709,7 +722,7 @@ pub enum PackageSide {
|
|||
|
||||
/// The file type of a [`Package`].
|
||||
#[cfg_attr(feature = "specta", derive(specta::Type))]
|
||||
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PackageFile {
|
||||
RequiredPack,
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@ use crate::constants::PROCESSOR_FILE;
|
|||
use crate::proxy::send::send_process;
|
||||
use crate::proxy::ProcessPayloadType;
|
||||
use crate::utils::http::read_json;
|
||||
use crate::utils::io::IOError;
|
||||
use crate::State;
|
||||
use onelauncher_utils::io::IOError;
|
||||
|
||||
use super::{Cluster, ClusterPath};
|
||||
|
||||
/// Wrapper over a HashMap of PIDs to ProcessorChildren and unified apis
|
||||
/// Wrapper over a `HashMap` of PIDs to `ProcessorChildren` and unified apis
|
||||
pub struct Processor(HashMap<Uuid, Arc<RwLock<ProcessorChild>>>);
|
||||
|
||||
/// Manager for safe processes like ingress feeds which need to be safely shut down.
|
||||
|
|
@ -48,13 +48,14 @@ impl Default for IngressProcessor {
|
|||
|
||||
impl IngressProcessor {
|
||||
/// Initialize a new ingresss process manager.
|
||||
pub fn new() -> Self {
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
ingress_feeds: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an ingress safety process to the HashMap.
|
||||
/// Add an ingress safety process to the `HashMap`.
|
||||
pub async fn add_ingress(r#type: IngressProcessType, uuid: Uuid) -> crate::Result<Uuid> {
|
||||
let state = State::get().await?;
|
||||
let mut ingress_processor = state.ingress_processor.write().await;
|
||||
|
|
@ -67,7 +68,7 @@ impl IngressProcessor {
|
|||
Ok(uuid)
|
||||
}
|
||||
|
||||
/// Complete and retain an ingress safety process from the HashMap of ingress feeds.
|
||||
/// Complete and retain an ingress safety process from the `HashMap` of ingress feeds.
|
||||
pub async fn finish(r#type: IngressProcessType, uuid: Uuid) -> crate::Result<()> {
|
||||
let state = State::get().await?;
|
||||
let mut ingress_processor = state.ingress_processor.write().await;
|
||||
|
|
@ -127,15 +128,16 @@ pub struct ProcessorCache {
|
|||
}
|
||||
|
||||
impl ChildType {
|
||||
/// Get the PID of a specific ChildType process.
|
||||
/// Get the PID of a specific `ChildType` process.
|
||||
#[must_use]
|
||||
pub fn id(&self) -> Option<u32> {
|
||||
match self {
|
||||
ChildType::ChildProcess(child) => child.id(),
|
||||
ChildType::RescuedChild(pid) => Some(*pid),
|
||||
Self::ChildProcess(child) => child.id(),
|
||||
Self::RescuedChild(pid) => Some(*pid),
|
||||
}
|
||||
}
|
||||
|
||||
/// Store a cache of a specific ChildType.
|
||||
/// Store a cache of a specific `ChildType`.
|
||||
pub async fn cache(
|
||||
&self,
|
||||
uuid: Uuid,
|
||||
|
|
@ -144,11 +146,11 @@ impl ChildType {
|
|||
user: Option<Uuid>,
|
||||
) -> crate::Result<()> {
|
||||
let pid = match self {
|
||||
ChildType::ChildProcess(child) => child.id().unwrap_or(0),
|
||||
ChildType::RescuedChild(pid) => *pid,
|
||||
Self::ChildProcess(child) => child.id().unwrap_or(0),
|
||||
Self::RescuedChild(pid) => *pid,
|
||||
};
|
||||
|
||||
let state = crate::State::get().await?;
|
||||
let state = State::get().await?;
|
||||
let mut system = sysinfo::System::new();
|
||||
|
||||
system.refresh_processes(sysinfo::ProcessesToUpdate::All);
|
||||
|
|
@ -174,13 +176,9 @@ impl ChildType {
|
|||
};
|
||||
|
||||
let path = state.directories.caches_dir().await.join(PROCESSOR_FILE);
|
||||
let mut caches = if let Ok(proc_json) =
|
||||
read_json::<HashMap<Uuid, ProcessorCache>>(&path, &state.io_semaphore).await
|
||||
{
|
||||
proc_json
|
||||
} else {
|
||||
HashMap::new()
|
||||
};
|
||||
let mut caches = (read_json::<HashMap<Uuid, ProcessorCache>>(&path, &state.io_semaphore)
|
||||
.await)
|
||||
.unwrap_or_default();
|
||||
|
||||
caches.insert(uuid, cached_proccess);
|
||||
crate::utils::http::write(&path, &serde_json::to_vec(&caches)?, &state.io_semaphore)
|
||||
|
|
@ -191,15 +189,11 @@ impl ChildType {
|
|||
|
||||
/// Remove a child from the global store of processes.
|
||||
pub async fn remove(&self, uuid: Uuid) -> crate::Result<()> {
|
||||
let state = crate::State::get().await?;
|
||||
let state = State::get().await?;
|
||||
let path = state.directories.caches_dir().await.join(PROCESSOR_FILE);
|
||||
let mut caches = if let Ok(proc_json) =
|
||||
read_json::<HashMap<Uuid, ProcessorCache>>(&path, &state.io_semaphore).await
|
||||
{
|
||||
proc_json
|
||||
} else {
|
||||
HashMap::new()
|
||||
};
|
||||
let mut caches = (read_json::<HashMap<Uuid, ProcessorCache>>(&path, &state.io_semaphore)
|
||||
.await)
|
||||
.unwrap_or_default();
|
||||
caches.remove(&uuid);
|
||||
crate::utils::http::write(&path, &serde_json::to_vec(&caches)?, &state.io_semaphore)
|
||||
.await?;
|
||||
|
|
@ -210,8 +204,8 @@ impl ChildType {
|
|||
/// Kill a process in the global store of processes.
|
||||
pub async fn kill(&mut self) -> crate::Result<()> {
|
||||
match self {
|
||||
ChildType::ChildProcess(child) => Ok(child.kill().await.map_err(IOError::from)?),
|
||||
ChildType::RescuedChild(pid) => {
|
||||
Self::ChildProcess(child) => Ok(child.kill().await.map_err(IOError::from)?),
|
||||
Self::RescuedChild(pid) => {
|
||||
let mut system = sysinfo::System::new();
|
||||
if system.refresh_processes(sysinfo::ProcessesToUpdate::Some(&[
|
||||
sysinfo::Pid::from_u32(*pid),
|
||||
|
|
@ -230,11 +224,11 @@ impl ChildType {
|
|||
/// Wait for a process to complete.
|
||||
pub async fn try_wait(&mut self) -> crate::Result<Option<i32>> {
|
||||
match self {
|
||||
ChildType::ChildProcess(child) => Ok(child
|
||||
Self::ChildProcess(child) => Ok(child
|
||||
.try_wait()
|
||||
.map_err(IOError::from)?
|
||||
.map(|x| x.code().unwrap_or(0))),
|
||||
ChildType::RescuedChild(pid) => {
|
||||
Self::RescuedChild(pid) => {
|
||||
let mut system = sysinfo::System::new();
|
||||
if system.refresh_processes(sysinfo::ProcessesToUpdate::Some(&[
|
||||
sysinfo::Pid::from_u32(*pid),
|
||||
|
|
@ -279,18 +273,21 @@ pub struct ProcessorChild {
|
|||
|
||||
impl Processor {
|
||||
/// Get a new [`Processor`] instance.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Processor(HashMap::new())
|
||||
Self(HashMap::new())
|
||||
}
|
||||
|
||||
/// Returns a reference to the value corresponding to the key.
|
||||
#[must_use]
|
||||
pub fn get(&self, uuid: Uuid) -> Option<Arc<RwLock<ProcessorChild>>> {
|
||||
self.0.get(&uuid).cloned()
|
||||
}
|
||||
|
||||
/// An collection visiting all keys in arbitrary order.
|
||||
#[must_use]
|
||||
pub fn keys(&self) -> Vec<Uuid> {
|
||||
self.0.keys().cloned().collect()
|
||||
self.0.keys().copied().collect()
|
||||
}
|
||||
|
||||
/// Get all running processes.
|
||||
|
|
@ -390,13 +387,12 @@ impl Processor {
|
|||
|
||||
/// restore processes from the cache.
|
||||
pub async fn restore(&mut self) -> crate::Result<()> {
|
||||
let state = crate::State::get().await?;
|
||||
let state = State::get().await?;
|
||||
let processor_path = state.directories.caches_dir().await.join(PROCESSOR_FILE);
|
||||
let mut processor_caches = if let Ok(processes_json) =
|
||||
read_json::<HashMap<uuid::Uuid, ProcessorCache>>(&processor_path, &state.io_semaphore)
|
||||
.await
|
||||
read_json::<HashMap<Uuid, ProcessorCache>>(&processor_path, &state.io_semaphore).await
|
||||
{
|
||||
let zeros = HashMap::<uuid::Uuid, ProcessorCache>::new();
|
||||
let zeros = HashMap::<Uuid, ProcessorCache>::new();
|
||||
crate::utils::http::write(
|
||||
&processor_path,
|
||||
&serde_json::to_vec(&zeros)?,
|
||||
|
|
@ -469,7 +465,7 @@ impl Processor {
|
|||
} {
|
||||
if let Some(line) = line {
|
||||
let mut censored = line.clone();
|
||||
for (key, value) in censors.iter() {
|
||||
for (key, value) in &censors {
|
||||
censored = censored.replace(key, value);
|
||||
}
|
||||
|
||||
|
|
@ -521,7 +517,7 @@ impl Processor {
|
|||
&mut self,
|
||||
cache: ProcessorCache,
|
||||
) -> crate::Result<Arc<RwLock<ProcessorChild>>> {
|
||||
let _state = crate::State::get().await?;
|
||||
let _state = State::get().await?;
|
||||
|
||||
// ensure located stray process matches with our pid recorded process
|
||||
{
|
||||
|
|
@ -680,7 +676,7 @@ impl Processor {
|
|||
});
|
||||
|
||||
tokio::spawn(async {
|
||||
let state = match crate::State::get().await {
|
||||
let state = match State::get().await {
|
||||
Ok(state) => state,
|
||||
Err(err) => {
|
||||
tracing::warn!("failed to get state: {}", err);
|
||||
|
|
@ -717,8 +713,7 @@ impl Processor {
|
|||
let mut cmd = hook.split(' ');
|
||||
if let Some(c) = cmd.next() {
|
||||
let mut c = Command::new(c);
|
||||
c.args(cmd.collect::<Vec<&str>>())
|
||||
.current_dir(cluster_path.full_path().await?);
|
||||
c.args(cmd).current_dir(cluster_path.full_path().await?);
|
||||
Some(c)
|
||||
} else {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use super::{Directories, JavaVersions};
|
||||
use crate::constants::CURRENT_SETTINGS_FORMAT_VERSION;
|
||||
use crate::utils::io;
|
||||
use onelauncher_utils::io;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
|
|
@ -10,15 +10,15 @@ use std::path::{Path, PathBuf};
|
|||
#[cfg_attr(feature = "specta", derive(specta::Type))]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Settings {
|
||||
/// A OneLauncher [`Theme`] managed by the core GUI.
|
||||
/// A `OneLauncher` [`Theme`] managed by the core GUI.
|
||||
pub theme: Theme,
|
||||
/// A global browser list view for the OneLauncher GUI.
|
||||
/// A global browser list view for the `OneLauncher` GUI.
|
||||
#[serde(default)]
|
||||
pub browser_list_view: BrowserListView,
|
||||
/// Does not ask for confirmation when closing the OneLauncher GUI
|
||||
/// Does not ask for confirmation when closing the `OneLauncher` GUI
|
||||
#[serde(default)]
|
||||
pub hide_close_prompt: bool,
|
||||
/// Disables animations in the OneLauncher GUI
|
||||
/// Disables animations in the `OneLauncher` GUI
|
||||
#[serde(default)]
|
||||
pub disable_animations: bool,
|
||||
/// A global fullscreen Minecraft state.
|
||||
|
|
@ -78,7 +78,7 @@ impl Settings {
|
|||
let read_settings = io::read(&file)
|
||||
.await
|
||||
.map_err(|err| anyhow::anyhow!("error reading settings file: {0}", err).into())
|
||||
.and_then(|it| serde_json::from_slice::<Settings>(&it).map_err(crate::Error::from));
|
||||
.and_then(|it| serde_json::from_slice::<Self>(&it).map_err(crate::Error::from));
|
||||
|
||||
if let Err(ref err) = read_settings {
|
||||
tracing::error!("failed to load settings file: {err}.");
|
||||
|
|
@ -143,7 +143,7 @@ impl Settings {
|
|||
}
|
||||
}
|
||||
|
||||
/// OneLauncher browser list type.
|
||||
/// `OneLauncher` browser list type.
|
||||
#[cfg_attr(feature = "specta", derive(specta::Type))]
|
||||
#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
|
|
@ -154,7 +154,7 @@ pub enum BrowserListView {
|
|||
Preview,
|
||||
}
|
||||
|
||||
/// A OneLauncher theme managed by the GUI.
|
||||
/// A `OneLauncher` theme managed by the GUI.
|
||||
#[cfg_attr(feature = "specta", derive(specta::Type))]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
|
|
@ -164,7 +164,7 @@ pub enum Theme {
|
|||
/// A default Light theme.
|
||||
Light,
|
||||
/// OLED Dark Theme.
|
||||
Contrast,
|
||||
Oled,
|
||||
/// Cute and colorful theme.
|
||||
Cat,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
use std::ffi::OsStr;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
use bytes::Bytes;
|
||||
use reqwest::Method;
|
||||
|
|
@ -15,9 +14,9 @@ use tokio::sync::{RwLock, Semaphore};
|
|||
use crate::constants::FETCH_ATTEMPTS;
|
||||
use crate::proxy::send::send_ingress;
|
||||
use crate::proxy::IngressId;
|
||||
use crate::utils::io;
|
||||
|
||||
use super::io::IOError;
|
||||
use onelauncher_utils::io;
|
||||
use onelauncher_utils::io::IOError;
|
||||
|
||||
/// A [`Semaphore`] used for all I/O operations.
|
||||
#[derive(Debug)]
|
||||
|
|
@ -34,7 +33,7 @@ lazy_static::lazy_static! {
|
|||
"{}/{} (https://polyfrost.org)",
|
||||
crate::constants::NAME,
|
||||
crate::constants::VERSION,
|
||||
)).unwrap();
|
||||
)).expect("failed to build reqwest headers!");
|
||||
headers.insert(reqwest::header::USER_AGENT, header);
|
||||
reqwest::Client::builder()
|
||||
.tcp_keepalive(Some(std::time::Duration::from_secs(15)))
|
||||
|
|
@ -91,7 +90,7 @@ pub async fn fetch_advanced(
|
|||
let mut req = REQWEST_CLIENT.request(method.clone(), url);
|
||||
|
||||
if let Some(body) = json_body.clone() {
|
||||
req = req.json(&body)
|
||||
req = req.json(&body);
|
||||
}
|
||||
|
||||
if let Some(header) = header {
|
||||
|
|
@ -120,7 +119,7 @@ pub async fn fetch_advanced(
|
|||
.await?;
|
||||
}
|
||||
|
||||
Ok(bytes::Bytes::from(bytes))
|
||||
Ok(Bytes::from(bytes))
|
||||
} else {
|
||||
x.bytes().await
|
||||
}
|
||||
|
|
@ -134,14 +133,13 @@ pub async fn fetch_advanced(
|
|||
if &*hash != sha1 {
|
||||
if attempt <= 3 {
|
||||
continue;
|
||||
} else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"hash {0} does not match {1}",
|
||||
sha1.to_string(),
|
||||
hash
|
||||
)
|
||||
.into());
|
||||
}
|
||||
return Err(anyhow::anyhow!(
|
||||
"hash {0} does not match {1}",
|
||||
sha1.to_string(),
|
||||
hash
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +189,7 @@ pub async fn fetch_from_mirrors(
|
|||
pub async fn check_internet_connection(timeout: u64) -> bool {
|
||||
REQWEST_CLIENT
|
||||
.get("https://api.polyfrost.org/")
|
||||
.timeout(Duration::from_secs(timeout))
|
||||
.timeout(std::time::Duration::from_secs(timeout))
|
||||
.send()
|
||||
.await
|
||||
.is_ok()
|
||||
|
|
@ -255,8 +253,8 @@ pub async fn write<'a>(path: &Path, bytes: &[u8], semaphore: &IoSemaphore) -> cr
|
|||
|
||||
/// Copy a file from one [`AsRef<Path>`] to another.
|
||||
pub async fn copy(
|
||||
src: impl AsRef<std::path::Path>,
|
||||
dest: impl AsRef<std::path::Path>,
|
||||
src: impl AsRef<Path>,
|
||||
dest: impl AsRef<Path>,
|
||||
semaphore: &IoSemaphore,
|
||||
) -> crate::Result<()> {
|
||||
let src = src.as_ref();
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ use winreg::{
|
|||
RegKey,
|
||||
};
|
||||
|
||||
use super::io;
|
||||
use onelauncher_utils::io;
|
||||
|
||||
/// A struct representing a single version of the Java Runtime Environment.
|
||||
/// Use [`locate_java`] to get an array of all located java instances on the machine.
|
||||
///
|
||||
/// common java paths from https://github.com/PrismLauncher/PrismLauncher/blob/develop/launcher/java/JavaUtils.cpp under GPL 3.0
|
||||
/// common java paths from <https://github.com/PrismLauncher/PrismLauncher/blob/develop/launcher/java/JavaUtils.cpp> under GPL 3.0
|
||||
///
|
||||
/// java check functionality from https://github.com/modrinth/theseus/blob/master/theseus/src/util/jre.rs under MIT
|
||||
/// java check functionality from <https://github.com/modrinth/theseus/blob/master/theseus/src/util/jre.rs> under MIT
|
||||
#[cfg_attr(feature = "specta", derive(specta::Type))]
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Clone)]
|
||||
pub struct JavaVersion {
|
||||
|
|
@ -192,9 +192,13 @@ pub async fn locate_java() -> Result<Vec<JavaVersion>, JavaError> {
|
|||
#[onelauncher_macros::memory]
|
||||
async fn locate_installed_java() -> Result<HashSet<PathBuf>, JavaError> {
|
||||
Box::pin(async move {
|
||||
let state = State::get().await.map_err(|_| JavaError::MutexError)?;
|
||||
let mut paths = HashSet::new();
|
||||
let java_base_path = state.directories.java_dir().await;
|
||||
let java_base_path = State::get()
|
||||
.await
|
||||
.map_err(|_| JavaError::MutexError)?
|
||||
.directories
|
||||
.java_dir()
|
||||
.await;
|
||||
|
||||
if java_base_path.is_dir() {
|
||||
if let Ok(dir) = std::fs::read_dir(java_base_path) {
|
||||
|
|
@ -239,7 +243,7 @@ pub async fn check_java(paths: HashSet<PathBuf>) -> HashSet<JavaVersion> {
|
|||
|
||||
java_instances
|
||||
.into_iter()
|
||||
.flat_map(|p| p.ok())
|
||||
.filter_map(Result::ok)
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
|
|
@ -253,10 +257,10 @@ pub async fn check_java_instance(path: &Path) -> Option<JavaVersion> {
|
|||
let Ok(path) = io::canonicalize(path) else {
|
||||
return None;
|
||||
};
|
||||
let java = if path.file_name()?.to_str()? != JAVA_BIN {
|
||||
path.join(JAVA_BIN)
|
||||
} else {
|
||||
let java = if path.file_name()?.to_str()? == JAVA_BIN {
|
||||
path
|
||||
} else {
|
||||
path.join(JAVA_BIN)
|
||||
};
|
||||
if !java.exists() {
|
||||
return None;
|
||||
|
|
@ -277,7 +281,7 @@ pub async fn check_java_instance(path: &Path) -> Option<JavaVersion> {
|
|||
|
||||
let output = Command::new(&java)
|
||||
.arg("-cp")
|
||||
.arg(file_path.parent().unwrap())
|
||||
.arg(file_path.parent().expect("failed to get java parent"))
|
||||
.arg("JavaInfo")
|
||||
.env_remove("_JAVA_OPTIONS")
|
||||
.output()
|
||||
|
|
|
|||
|
|
@ -1,32 +1,10 @@
|
|||
//! **OneLauncher Utilities**
|
||||
//! Standard asynchronous utilities and wrappers for use in the launcher.
|
||||
//! **`OneLauncher` Core Utilities**
|
||||
//! Standard asynchronous utilities and wrappers for use within the launcher core.
|
||||
//!
|
||||
//! - [`io`]: Async wrapper around [`tokio::fs`] and [`std::io::Error`] for our error system.
|
||||
//! - [`http`]: Async extensions and wrappers around [`reqwest`] functions.
|
||||
//! - [`java`]: Async utilities for managing and downloading Java versions.
|
||||
//! - [`platform`]: Async utilities for managing OS-specific [`interpulse`] operations and rules.
|
||||
//! - [`watcher`]: Async utilities for watching files with [`notify`].
|
||||
//! - [`logging`]: Async utilities for log4j parsing with [`nom`].
|
||||
|
||||
pub mod http;
|
||||
pub mod io;
|
||||
pub mod java;
|
||||
pub mod logging;
|
||||
pub mod platform;
|
||||
pub mod watcher;
|
||||
|
||||
/// Simple macro that takes a mutable reference and inserts it into a codeblock closure
|
||||
/// as an owned reference.
|
||||
///
|
||||
/// mutable reference gets epically owned by free thinking macro!!!!! (not clickbait)
|
||||
/// im going insane insane insane insane insane insane insane insane insane
|
||||
macro_rules! ref_owned {
|
||||
($id:ident = $init:expr => $transform:block) => {{
|
||||
let mut it = $init;
|
||||
{
|
||||
let $id = &mut it;
|
||||
$transform;
|
||||
}
|
||||
it
|
||||
}};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,11 @@ pub async fn initialize_watcher() -> crate::Result<Debouncer<RecommendedWatcher>
|
|||
Duration::from_secs_f32(2.0),
|
||||
move |result: DebounceEventResult| {
|
||||
futures::executor::block_on(async {
|
||||
sender.send(result).await.unwrap();
|
||||
})
|
||||
sender
|
||||
.send(result)
|
||||
.await
|
||||
.expect("failed to send debouse event");
|
||||
});
|
||||
},
|
||||
)?;
|
||||
|
||||
|
|
@ -34,7 +37,7 @@ pub async fn initialize_watcher() -> crate::Result<Debouncer<RecommendedWatcher>
|
|||
Ok(mut events) => {
|
||||
let mut paths = Vec::new();
|
||||
events.sort_by(|a, b| a.path.cmp(&b.path));
|
||||
events.iter().for_each(|a| {
|
||||
for a in &events {
|
||||
let mut formatted = PathBuf::new();
|
||||
let mut components = a.path.components();
|
||||
let mut matched = false;
|
||||
|
|
@ -57,7 +60,7 @@ pub async fn initialize_watcher() -> crate::Result<Debouncer<RecommendedWatcher>
|
|||
if a.path
|
||||
.components()
|
||||
.any(|c| c.as_os_str() == crate::constants::CRASH_PATH)
|
||||
&& a.path.extension().map(|e| e == "txt").unwrap_or(false)
|
||||
&& a.path.extension().is_some_and(|e| e == "txt")
|
||||
{
|
||||
Cluster::handle_crash(cluster_path);
|
||||
} else if !paths.contains(&formatted) {
|
||||
|
|
@ -72,7 +75,7 @@ pub async fn initialize_watcher() -> crate::Result<Debouncer<RecommendedWatcher>
|
|||
Clusters::sync_cluster(cluster_path);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(err) => tracing::warn!("fs watching error: {err}"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,37 @@
|
|||
#![cfg(target_os = "linux")]
|
||||
#![warn(
|
||||
clippy::all,
|
||||
clippy::pedantic,
|
||||
clippy::correctness,
|
||||
clippy::perf,
|
||||
clippy::style,
|
||||
clippy::suspicious,
|
||||
clippy::complexity,
|
||||
clippy::nursery,
|
||||
clippy::unwrap_used,
|
||||
unused_qualifications,
|
||||
rust_2018_idioms,
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unused_allocation,
|
||||
clippy::unnecessary_cast,
|
||||
clippy::cast_lossless,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_possible_wrap,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::dbg_macro,
|
||||
unused_import_braces,
|
||||
clippy::deprecated_cfg_attr,
|
||||
clippy::separated_literal_suffix,
|
||||
deprecated
|
||||
)]
|
||||
#![forbid(deprecated_in_future)]
|
||||
#![deny(unused_must_use)]
|
||||
#![allow(
|
||||
clippy::missing_errors_doc,
|
||||
clippy::future_not_send,
|
||||
clippy::module_name_repetitions
|
||||
)]
|
||||
|
||||
use libc::pid_t;
|
||||
use std::ffi::c_int;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,36 @@
|
|||
extern crate proc_macro;
|
||||
#![warn(
|
||||
clippy::all,
|
||||
clippy::pedantic,
|
||||
clippy::correctness,
|
||||
clippy::perf,
|
||||
clippy::style,
|
||||
clippy::suspicious,
|
||||
clippy::complexity,
|
||||
clippy::nursery,
|
||||
clippy::unwrap_used,
|
||||
unused_qualifications,
|
||||
rust_2018_idioms,
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unused_allocation,
|
||||
clippy::unnecessary_cast,
|
||||
clippy::cast_lossless,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_possible_wrap,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::dbg_macro,
|
||||
unused_import_braces,
|
||||
clippy::deprecated_cfg_attr,
|
||||
clippy::separated_literal_suffix,
|
||||
deprecated
|
||||
)]
|
||||
#![forbid(deprecated_in_future)]
|
||||
#![deny(unused_must_use)]
|
||||
#![allow(
|
||||
clippy::missing_errors_doc,
|
||||
clippy::future_not_send,
|
||||
clippy::module_name_repetitions
|
||||
)]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
|
|
|
|||
|
|
@ -2,3 +2,14 @@
|
|||
#[allow(warnings, unused)]
|
||||
#[rustfmt::skip]
|
||||
pub mod prisma;
|
||||
|
||||
|
||||
pub async fn test_db() -> std::sync::Arc<prisma::PrismaClient> {
|
||||
std::sync::Arc::new(
|
||||
prisma::PrismaClient::_builder()
|
||||
.with_url(format!("file:/tmp/test-db-{}", uuid::Uuid::new_v4()))
|
||||
.build()
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
32
packages/utils/Cargo.toml
Normal file
32
packages/utils/Cargo.toml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
[package]
|
||||
name = "onelauncher_utils"
|
||||
description = "rust utils for onelauncher"
|
||||
version = { workspace = true }
|
||||
license = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
documentation = { workspace = true }
|
||||
readme = { workspace = true }
|
||||
homepage = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
onelauncher_prisma = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
async-channel = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
pin-project-lite = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
sys-info = { workspace = true }
|
||||
zip = { workspace = true }
|
||||
dunce = { workspace = true }
|
||||
async-compression = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
nom = { workspace = true }
|
||||
prisma-client-rust = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
interpulse = { workspace = true }
|
||||
|
|
@ -12,7 +12,7 @@ use tokio::task::spawn_blocking;
|
|||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum IOError {
|
||||
/// A wrapped [`std::io::Error`] along with the path involved in the error.
|
||||
#[error("{source}, path: {path}")]
|
||||
#[error("error acessing path: {source}, path: {path}")]
|
||||
IOErrorWrapper {
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
|
|
@ -26,14 +26,29 @@ pub enum IOError {
|
|||
IOError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
impl<P: AsRef<std::path::Path>> From<(P, std::io::Error)> for IOError {
|
||||
fn from((path, source): (P, std::io::Error)) -> Self {
|
||||
Self::IOErrorWrapper {
|
||||
source,
|
||||
path: path.as_ref().to_string_lossy().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("received a non UTF-8 path: <lossy_path='{}'>", .0.to_string_lossy())]
|
||||
pub struct NonUtf8PathError(pub Box<std::path::Path>);
|
||||
|
||||
impl IOError {
|
||||
/// Converts a [`std::io::Error`] into an [`IOError`].
|
||||
pub fn from(source: std::io::Error) -> Self {
|
||||
#[must_use]
|
||||
pub const fn from(source: std::io::Error) -> Self {
|
||||
Self::IOError(source)
|
||||
}
|
||||
|
||||
/// Converts a [`zip::result::ZipError`] into an [`IOError`].
|
||||
pub fn from_zip(source: zip::result::ZipError) -> Self {
|
||||
#[must_use]
|
||||
pub const fn from_zip(source: zip::result::ZipError) -> Self {
|
||||
Self::ZipError(source)
|
||||
}
|
||||
|
||||
82
packages/utils/src/lib.rs
Normal file
82
packages/utils/src/lib.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
//! **`OneLauncher` Utilities**
|
||||
//! Standard utilities for use within all Rust subprojects.
|
||||
//!
|
||||
//! - [`logging`]: Async utilities for log4j parsing with [`nom`].
|
||||
//! - [`platform`]: Async utilities for managing OS-specific [`interpulse`] operations and rules.
|
||||
//! - [`io`]: Async wrapper around [`tokio::fs`] and [`std::io::Error`] for our error system.
|
||||
|
||||
#![warn(
|
||||
clippy::all,
|
||||
clippy::pedantic,
|
||||
clippy::correctness,
|
||||
clippy::perf,
|
||||
clippy::style,
|
||||
clippy::suspicious,
|
||||
clippy::complexity,
|
||||
clippy::nursery,
|
||||
clippy::unwrap_used,
|
||||
unused_qualifications,
|
||||
rust_2018_idioms,
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unused_allocation,
|
||||
clippy::unnecessary_cast,
|
||||
clippy::cast_lossless,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_possible_wrap,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::dbg_macro,
|
||||
unused_import_braces,
|
||||
clippy::deprecated_cfg_attr,
|
||||
clippy::separated_literal_suffix,
|
||||
deprecated
|
||||
)]
|
||||
#![forbid(deprecated_in_future)]
|
||||
#![deny(unused_must_use)]
|
||||
#![allow(
|
||||
clippy::missing_errors_doc,
|
||||
clippy::future_not_send,
|
||||
clippy::module_name_repetitions
|
||||
)]
|
||||
|
||||
pub mod io;
|
||||
pub mod logging;
|
||||
pub mod platform;
|
||||
pub mod prisma;
|
||||
|
||||
/// Simple macro that takes a mutable reference and inserts it into a codeblock closure
|
||||
/// as an owned reference.
|
||||
///
|
||||
/// mutable reference gets epically owned by free thinking macro!!!!! (not clickbait)
|
||||
/// im going insane insane insane insane insane insane insane insane insane
|
||||
#[macro_export]
|
||||
macro_rules! ref_owned {
|
||||
($id:ident = $init:expr => $transform:block) => {{
|
||||
let mut it = $init;
|
||||
{
|
||||
let $id = &mut it;
|
||||
$transform;
|
||||
}
|
||||
it
|
||||
}};
|
||||
}
|
||||
|
||||
/// Combines an iterator of `T` and an iterator of [`Option<T>`],
|
||||
/// removing and ensuring the safety of any [`None`] values in the process.
|
||||
pub fn chain_iterator<T>(
|
||||
required: impl IntoIterator<Item = T>,
|
||||
optional: impl IntoIterator<Item = Option<T>>,
|
||||
) -> Vec<T> {
|
||||
required
|
||||
.into_iter()
|
||||
.map(Some)
|
||||
.chain(optional)
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn test_type_of<T>(_: T) -> &'static str {
|
||||
std::any::type_name::<T>()
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ fn whitespace<'a, O, E: ParseError<&'a str>>(
|
|||
delimited(multispace0, inner, multispace0)
|
||||
}
|
||||
|
||||
pub fn parse_logentry(input: &str) -> IResult<&str, LogEntry> {
|
||||
pub fn parse_logentry(input: &str) -> IResult<&str, LogEntry<'_>> {
|
||||
let (o, (attributes, _, message)) = preceded(
|
||||
multispace0,
|
||||
alt((
|
||||
|
|
@ -74,7 +74,7 @@ pub struct Attributes<'a> {
|
|||
pub thread_name: &'a str,
|
||||
}
|
||||
|
||||
fn attributes(input: &str) -> IResult<&str, Attributes> {
|
||||
fn attributes(input: &str) -> IResult<&str, Attributes<'_>> {
|
||||
let (o, attributes) = count(whitespace(attribute), 4)(input)?;
|
||||
macro_rules! extract_attribute {
|
||||
($field:ident) => {{
|
||||
|
|
@ -114,11 +114,11 @@ enum Attribute<'a> {
|
|||
ThreadName(&'a str),
|
||||
}
|
||||
|
||||
fn attribute(input: &str) -> IResult<&str, Attribute> {
|
||||
fn attribute(input: &str) -> IResult<&str, Attribute<'_>> {
|
||||
alt((attr_logger, attr_timestamp, attr_level, attr_thread))(input)
|
||||
}
|
||||
|
||||
fn attr_logger(input: &str) -> IResult<&str, Attribute> {
|
||||
fn attr_logger(input: &str) -> IResult<&str, Attribute<'_>> {
|
||||
map(
|
||||
separated_pair(
|
||||
tag("logger"),
|
||||
|
|
@ -129,7 +129,7 @@ fn attr_logger(input: &str) -> IResult<&str, Attribute> {
|
|||
)(input)
|
||||
}
|
||||
|
||||
fn attr_timestamp(input: &str) -> IResult<&str, Attribute> {
|
||||
fn attr_timestamp(input: &str) -> IResult<&str, Attribute<'_>> {
|
||||
map(
|
||||
separated_pair(
|
||||
tag("timestamp"),
|
||||
|
|
@ -140,7 +140,7 @@ fn attr_timestamp(input: &str) -> IResult<&str, Attribute> {
|
|||
)(input)
|
||||
}
|
||||
|
||||
fn attr_level(input: &str) -> IResult<&str, Attribute> {
|
||||
fn attr_level(input: &str) -> IResult<&str, Attribute<'_>> {
|
||||
map(
|
||||
separated_pair(
|
||||
tag("level"),
|
||||
|
|
@ -151,7 +151,7 @@ fn attr_level(input: &str) -> IResult<&str, Attribute> {
|
|||
)(input)
|
||||
}
|
||||
|
||||
fn attr_thread(input: &str) -> IResult<&str, Attribute> {
|
||||
fn attr_thread(input: &str) -> IResult<&str, Attribute<'_>> {
|
||||
map(
|
||||
separated_pair(
|
||||
tag("thread"),
|
||||
|
|
@ -185,7 +185,7 @@ fn message(input: &str) -> IResult<&str, &str> {
|
|||
)(input)
|
||||
}
|
||||
|
||||
fn plain_text(input: &str) -> IResult<&str, (Attributes, &str, &str)> {
|
||||
fn plain_text(input: &str) -> IResult<&str, (Attributes<'_>, &str, &str)> {
|
||||
map(take_until("<"), |text| {
|
||||
(
|
||||
Attributes {
|
||||
|
|
@ -201,15 +201,16 @@ fn plain_text(input: &str) -> IResult<&str, (Attributes, &str, &str)> {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_message() {
|
||||
message(
|
||||
r#"<log4j:Message>
|
||||
r"<log4j:Message>
|
||||
<![CDATA[200 Datafixer optimizations took 2000 years]]>
|
||||
</log4j:Message>"#,
|
||||
</log4j:Message>",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
|
@ -232,7 +233,7 @@ mod tests {
|
|||
Attributes {
|
||||
logger: "com.mojang.datafixers.DataFixerBuilder",
|
||||
level: LogLevel::Debug,
|
||||
timestamp: 3213819318239,
|
||||
timestamp: 3_213_819_318_239,
|
||||
thread_name: "Datafixer"
|
||||
}
|
||||
);
|
||||
|
|
@ -257,7 +258,7 @@ mod tests {
|
|||
#[test]
|
||||
fn parse_timestamp_attribute() {
|
||||
let (_, attr) = attr_timestamp(r#"timestamp="313213473127""#).unwrap();
|
||||
assert_eq!(attr, Attribute::Timestamp(313213473127));
|
||||
assert_eq!(attr, Attribute::Timestamp(313_213_473_127));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -296,7 +297,7 @@ mod tests {
|
|||
LogEntry {
|
||||
logger: "com.mojang.datafixers.DataFixerBuilder",
|
||||
level: LogLevel::Debug,
|
||||
timestamp: 3213712731731,
|
||||
timestamp: 3_213_712_731_731,
|
||||
thread_name: "Datafixer",
|
||||
message: "200 Datafixer optimizations took 2000 years",
|
||||
}
|
||||
|
|
@ -19,26 +19,26 @@ impl OsExt for Os {
|
|||
fn native_arch(java_arch: &str) -> Self {
|
||||
if std::env::consts::OS == "windows" {
|
||||
if java_arch == "aarch64" {
|
||||
Os::WindowsArm64
|
||||
Self::WindowsArm64
|
||||
} else {
|
||||
Os::Windows
|
||||
Self::Windows
|
||||
}
|
||||
} else if std::env::consts::OS == "linux" {
|
||||
if java_arch == "aarch64" {
|
||||
Os::LinuxArm64
|
||||
Self::LinuxArm64
|
||||
} else if java_arch == "arm" {
|
||||
Os::LinuxArm32
|
||||
Self::LinuxArm32
|
||||
} else {
|
||||
Os::Linux
|
||||
Self::Linux
|
||||
}
|
||||
} else if std::env::consts::OS == "macos" {
|
||||
if java_arch == "aarch64" {
|
||||
Os::OsxArm64
|
||||
Self::OsxArm64
|
||||
} else {
|
||||
Os::Osx
|
||||
Self::Osx
|
||||
}
|
||||
} else {
|
||||
Os::Unknown
|
||||
Self::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +53,7 @@ impl OsExt for Os {
|
|||
}
|
||||
|
||||
/// Handles an os-specific [`OsRule`], returning if the [`Os`] matches the rule.
|
||||
#[must_use]
|
||||
pub fn os_rule(rule: &OsRule, java_arch: &str, updated: bool) -> bool {
|
||||
let mut rule_match = true;
|
||||
|
||||
|
|
@ -78,6 +79,7 @@ pub fn os_rule(rule: &OsRule, java_arch: &str, updated: bool) -> bool {
|
|||
}
|
||||
|
||||
/// Gets the seperator between Java classpaths in metadata (`";"` on Windows, `";"` on others).
|
||||
#[must_use]
|
||||
pub fn classpath_separator(java_arch: &str) -> &'static str {
|
||||
match Os::native_arch(java_arch) {
|
||||
Os::Osx | Os::OsxArm64 | Os::Linux | Os::LinuxArm32 | Os::LinuxArm64 | Os::Unknown => ":",
|
||||
136
packages/utils/src/prisma.rs
Normal file
136
packages/utils/src/prisma.rs
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
use onelauncher_prisma::prisma::PrismaClient;
|
||||
use prisma_client_rust::migrations::{DbPushError, MigrateDeployError};
|
||||
use prisma_client_rust::NewClientError;
|
||||
|
||||
/// Represents an error that occured while opening and running migrations
|
||||
/// on the [`prisma_client_rust`] database.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum MigrationError {
|
||||
#[error("failed to initialize a new database connection: {0}")]
|
||||
NewClient(#[from] Box<NewClientError>),
|
||||
#[error("failed to migrate the database: {0}")]
|
||||
MigrateFailed(#[from] MigrateDeployError),
|
||||
#[cfg(debug_assertions)]
|
||||
#[error("failed to migrate the database: {0}")]
|
||||
DbPushFailed(#[from] DbPushError),
|
||||
}
|
||||
|
||||
/// Loads the database from the given path and migrates it to the latest schema.
|
||||
pub async fn load_and_migrate(db_url: &str) -> Result<PrismaClient, MigrationError> {
|
||||
let client = PrismaClient::_builder()
|
||||
.with_url(db_url.to_string())
|
||||
.build()
|
||||
.await
|
||||
.map_err(Box::new)?;
|
||||
client._migrate_deploy().await?;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let mut builder = client._db_push();
|
||||
if std::env::var("ONELAUNCHER_DO_NOT_USE_THIS")
|
||||
.map(|v| v == "true")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
builder = builder.accept_data_loss();
|
||||
}
|
||||
|
||||
if std::env::var("ONELAUNCHER_FORCE_RESET")
|
||||
.map(|v| v == "true")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
builder = builder.force_reset();
|
||||
}
|
||||
|
||||
let res = builder.await;
|
||||
|
||||
match res {
|
||||
Ok(_) => {}
|
||||
Err(e @ DbPushError::PossibleDataLoss(_)) => {
|
||||
eprintln!("pushing prisma schema may result in data loss. use `ONELAUNCHER_DO_NOT_USE_THIS=true` to force it.");
|
||||
Err(e)?;
|
||||
}
|
||||
Err(e) => Err(e)?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
/// Construct back an inode after storing it in database
|
||||
#[must_use]
|
||||
pub const fn inode_from_db(db_inode: &[u8]) -> u64 {
|
||||
u64::from_le_bytes([
|
||||
db_inode[0],
|
||||
db_inode[1],
|
||||
db_inode[2],
|
||||
db_inode[3],
|
||||
db_inode[4],
|
||||
db_inode[5],
|
||||
db_inode[6],
|
||||
db_inode[7],
|
||||
])
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn inode_to_db(inode: u64) -> Vec<u8> {
|
||||
inode.to_le_bytes().to_vec()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn size_in_bytes_from_db(db_size_in_bytes: &[u8]) -> u64 {
|
||||
u64::from_be_bytes([
|
||||
db_size_in_bytes[0],
|
||||
db_size_in_bytes[1],
|
||||
db_size_in_bytes[2],
|
||||
db_size_in_bytes[3],
|
||||
db_size_in_bytes[4],
|
||||
db_size_in_bytes[5],
|
||||
db_size_in_bytes[6],
|
||||
db_size_in_bytes[7],
|
||||
])
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn size_in_bytes_to_db(size: u64) -> Vec<u8> {
|
||||
size.to_be_bytes().to_vec()
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[error("missing database field {0}")]
|
||||
pub struct MissingFieldError(&'static str);
|
||||
|
||||
impl MissingFieldError {
|
||||
#[must_use]
|
||||
pub const fn new(value: &'static str) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait OptionalField: Sized {
|
||||
type Out;
|
||||
|
||||
fn transform(self) -> Option<Self::Out>;
|
||||
}
|
||||
|
||||
impl<T> OptionalField for Option<T> {
|
||||
type Out = T;
|
||||
|
||||
fn transform(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> OptionalField for &'a Option<T> {
|
||||
type Out = &'a T;
|
||||
|
||||
fn transform(self) -> Option<Self::Out> {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybe_missing<T: OptionalField>(
|
||||
data: T,
|
||||
field: &'static str,
|
||||
) -> Result<T::Out, MissingFieldError> {
|
||||
data.transform().ok_or(MissingFieldError(field))
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue