Skip to content

Commit

Permalink
Merge branch 'development' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
froehlichA committed Apr 19, 2022
2 parents 9e0c939 + 722dd8f commit fc0b384
Show file tree
Hide file tree
Showing 37 changed files with 141 additions and 111 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
with:
nim-version: ${{ env.NIM_VERSION }}
- name: Build
run: nimble build -y -d:ssl -d:release
run: nimble build -d:release -y

test:
runs-on: ${{ matrix.os }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
with:
nim-version: ${{ env.NIM_VERSION }}
- name: Build
run: nimble build -y -d:ssl -d:release
run: nimble build -d:release -y
- name: Store artifacts
uses: actions/upload-artifact@v2
with:
Expand Down
9 changes: 0 additions & 9 deletions pax.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,3 @@ requires "zippy >= 0.6.2"

task test, "Test project":
exec "testament all"

task buildDev, "Build for usage during development":
exec "nimble build -d:ssl"

task buildProd, "Build for production":
exec "nimble build -d:ssl -d:release"

task buildCI, "Build for production on the CI machine":
exec "nimble build -d:ssl -d:release -y"
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ You'll need:
* A C compiler (depending on your operating system, one might be already installed)

Clone and `cd` into the repository - and you're good to go!\
Run `nimble buildDev` to build the application for development, or run `nimble buildProd` to create an optimized release build.\
Run `nimble build` to build the application for development, or run `nimble build -d:release` to create an optimized release build.\
Execute the program with `./pax` (on Linux) or `pax.exe` (on Windows).


Expand Down
26 changes: 13 additions & 13 deletions src/api/cfapi.nim
Original file line number Diff line number Diff line change
Expand Up @@ -22,35 +22,35 @@ const
type
CfApiError* = object of HttpRequestError

proc fetchAddonsByQuery*(query: string, category: Option[CfAddonGameCategory]): Future[JsonNode] {.async.} =
proc fetchAddonsByQuery*(query: string, category: Option[CfAddonGameCategory]): Future[seq[CfAddon]] {.async.} =
## retrieves all addons that match the given `query` search and `category`.
let encodedQuery = encodeUrl(query, usePlus = false)
var url = addonsBaseUrl & "/v1/mods/search?gameId=432&pageSize=50&sortField=6&sortOrder=desc&searchFilter=" & encodedQuery
if category.isSome:
url = url & "&classId=" & $ord(category.get())
return get(url.Url).await.parseJson["data"]
return get(url.Url).await.parseJson["data"].addonsFromForgeSvc

proc fetchAddon*(projectId: int): Future[JsonNode] {.async.} =
proc fetchAddon*(projectId: int): Future[CfAddon] {.async.} =
## get the addon with the given `projectId`.
let url = addonsBaseUrl & "/v1/mods/" & $projectId
try:
return get(url.Url).await.parseJson["data"]
return get(url.Url).await.parseJson["data"].addonFromForgeSvc
except HttpRequestError:
raise newException(CfApiError, "addon with project id '" & $projectId & "' not found.")

proc fetchAddons*(projectIds: seq[int]): Future[JsonNode] {.async.} =
proc fetchAddons*(projectIds: seq[int]): Future[seq[CfAddon]] {.async.} =
## get all addons with their given `projectId`.
let url = addonsBaseUrl & "/v1/mods/"
let body = %* { "modIds": projectIds }
try:
let addons = post(url.Url, $body).await.parseJson["data"]
let addons = post(url.Url, $body).await.parseJson["data"].addonsFromForgeSvc
if projectIds.len != addons.len:
raise newException(CfApiError, "one of the addons of project ids '" & $projectIds & "' was not found.")
return addons
except HttpRequestError:
raise newException(CfApiError, "one of the addons of project ids '" & $projectIds & "' was not found.")

proc fetchAddon*(slug: string): Future[JsonNode] {.async.} =
proc fetchAddon*(slug: string): Future[CfAddon] {.async.} =
## get the addon matching the `slug`.
let reqBody = %* {
"query": "{ addons(slug: \"" & slug & "\") { id }}"
Expand All @@ -62,30 +62,30 @@ proc fetchAddon*(slug: string): Future[JsonNode] {.async.} =
let projectId = addons[0]["id"].getInt()
return await fetchAddon(projectId)

proc fetchAddonFiles*(projectId: int): Future[JsonNode] {.async.} =
proc fetchAddonFiles*(projectId: int): Future[seq[CfAddonFile]] {.async.} =
## get all addon files associated with the given `projectId`.
let url = addonsBaseUrl & "/v1/mods/" & $projectId & "/files?pageSize=10000"
try:
return get(url.Url).await.parseJson["data"]
return get(url.Url).await.parseJson["data"].addonFilesFromForgeSvc
except HttpRequestError:
raise newException(CfApiError, "addon with project id '" & $projectId & "' not found.")

proc fetchAddonFiles*(fileIds: seq[int]): Future[JsonNode] {.async.} =
proc fetchAddonFiles*(fileIds: seq[int]): Future[seq[CfAddonFile]] {.async.} =
## get all addon files with their given `fileIds`.
let url = addonsBaseUrl & "/v1/mods/files"
let body = %* { "fileIds": fileIds }
try:
let files = post(url.Url, $body).await.parseJson["data"]
let files = post(url.Url, $body).await.parseJson["data"].addonFilesFromForgeSvc
if fileIds.len != files.len:
raise newException(CfApiError, "one of the addon files of file ids '" & $fileIds & "' was not found.")
return files
except HttpRequestError:
raise newException(CfApiError, "one of the addon files of file ids '" & $fileIds & "' was not found.")

proc fetchAddonFile*(projectId: int, fileId: int): Future[JsonNode] {.async.} =
proc fetchAddonFile*(projectId: int, fileId: int): Future[CfAddonFile] {.async.} =
## get the addon file with the given `fileId` & `projectId`.
let url = addonsBaseUrl & "/v1/mods/" & $projectId & "/files/" & $fileId
try:
return get(url.Url).await.parseJson["data"]
return get(url.Url).await.parseJson["data"].addonFileFromForgeSvc
except HttpRequestError:
raise newException(CfApiError, "addon with project & file id '" & $projectId & ':' & $fileId & "' not found.")
60 changes: 29 additions & 31 deletions src/api/cfcache.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,89 +6,87 @@
## and instead data from the local file system is returned.

import std/[json, options, os, times]
import cfcore

const
cacheDir* = getCacheDir("pax") ## the cache folder
addonCacheTime = 30.minutes ## how long an addon is cached
addonFileCacheTime = 1.days ## how long an addon file is cached

proc getAddonFilename*(projectId: int): string {.inline.} =
## get the filename of an addon in the cache.
return cacheDir / ("addon:" & $projectId)
return getCacheDir("pax") / ("addon:" & $projectId)

proc getAddonFileFilename*(fileId: int): string {.inline.} =
## get the filename of an addon file in the cache.
return cacheDir / ("file:" & $fileId)
return getCacheDir("pax") / ("file:" & $fileId)

proc putAddon*(json: JsonNode): void =
proc putAddon*(addon: CfAddon): void =
## put an addon in the cache.
let projectId = json["id"].getInt()
let filename = getAddonFilename(projectId)
let filename = getAddonFilename(addon.projectId)
try:
writeFile(filename, $json)
writeFile(filename, $addon.toJson)
except IOError:
discard

proc putAddons*(json: JsonNode): void =
proc putAddons*(addons: seq[CfAddon]): void =
## put multiple addons in the cache.
for elems in json.getElems():
putAddon(elems)
for addon in addons:
putAddon(addon)

proc putAddonFile*(json: JsonNode): void =
proc putAddonFile*(addonFile: CfAddonFile): void =
## put an addon file in the cache.
let fileId = json["id"].getInt()
let filename = getAddonFileFilename(fileId)
let filename = getAddonFileFilename(addonFile.fileId)
try:
writeFile(filename, $json)
writeFile(filename, $addonFile.toJson)
except IOError:
discard

proc putAddonFiles*(json: JsonNode): void =
proc putAddonFiles*(addonFiles: seq[CfAddonFile]): void =
## put multiple addons in the cache.
for elems in json.getElems():
putAddonFile(elems)
for addonFile in addonFiles:
putAddonFile(addonFile)

proc getAddon*(projectId: int): Option[JsonNode] =
proc getAddon*(projectId: int): Option[CfAddon] =
## retrieve an addon from cache.
let filename = getAddonFilename(projectId)
if not fileExists(filename):
return none[JsonNode]()
return none[CfAddon]()
let info = getFileInfo(filename)
if info.lastWriteTime + addonCacheTime > getTime():
let file = try:
readFile(filename)
except IOError:
return none[JsonNode]()
return some(file.parseJson)
return none[JsonNode]()
return none[CfAddon]()
return some(file.parseJson.addonFromForgeSvc)
return none[CfAddon]()

proc getAddonFile*(fileId: int): Option[JsonNode] =
proc getAddonFile*(fileId: int): Option[CfAddonFile] =
## retrieve an addon file from cache.
let filename = getAddonFileFilename(fileId)
if not fileExists(filename):
return none[JsonNode]()
return none[CfAddonFile]()
let info = getFileInfo(filename)
if info.lastWriteTime + addonFileCacheTime > getTime():
let file = try:
readFile(filename)
except IOError:
return none[JsonNode]()
return some(file.parseJson)
return none[JsonNode]()
return none[CfAddonFile]()
return some(file.parseJson.addonFileFromForgeSvc)
return none[CfAddonFile]()

proc clean*(): int =
## remove old files from the cache.
## returns the number of files cleared.
result = 0
for filename in walkFiles(cacheDir / "addon:*"):
for filename in walkFiles(getCacheDir("pax") / "addon:*"):
let info = getFileInfo(filename)
if info.lastWriteTime + addonCacheTime < getTime():
try:
removeFile(filename)
inc(result)
except IOError:
discard
for filename in walkFiles(cacheDir / "file:*"):
for filename in walkFiles(getCacheDir("pax") / "file:*"):
let info = getFileInfo(filename)
if info.lastWriteTime + addonFileCacheTime < getTime():
try:
Expand All @@ -100,8 +98,8 @@ proc clean*(): int =
proc purge*(): void =
## remove all files from the cache.
try:
removeDir(cacheDir)
createDir(cacheDir)
removeDir(getCacheDir("pax"))
createDir(getCacheDir("pax"))
except IOError:
discard

Expand Down
18 changes: 9 additions & 9 deletions src/api/cfclient.nim
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ proc fetchAddonsByQuery*(query: string, category: Option[CfAddonGameCategory]):
## retrieves all addons that match the given `query` search and `category`.
let data = await cfapi.fetchAddonsByQuery(query, category)
cfcache.putAddons(data)
return data.addonsFromForgeSvc
return data

proc fetchAddonsByQuery*(query: string, category: CfAddonGameCategory): Future[seq[CfAddon]] =
## retrieves all addons that match the given `query` search and `category`.
Expand All @@ -45,10 +45,10 @@ proc fetchAddon(projectId: int, lookupCache: bool): Future[CfAddon] {.async.} =
## get the addon with the given `projectId`.
if lookupCache:
withCachedAddon(addon, projectId):
return addon.addonFromForgeSvc
return addon
let data = await cfapi.fetchAddon(projectId)
cfcache.putAddon(data)
return data.addonFromForgeSvc
return data

proc fetchAddon*(projectId: int): Future[CfAddon] =
## get the addon with the given `projectId`.
Expand All @@ -61,7 +61,7 @@ proc fetchAddonsChunks(projectIds: seq[int]): Future[seq[CfAddon]] {.async.} =
try:
let data = await cfapi.fetchAddons(projectIds)
cfcache.putAddons(data)
return data.addonsFromForgeSvc
return data
except CfApiError:
# fallback to looking up the ids individually
return await all(projectIds.map((x) => fetchAddon(x, lookupCache = false)))
Expand Down Expand Up @@ -104,13 +104,13 @@ proc fetchAddon*(slug: string): Future[CfAddon] {.async.} =
## get the addon matching the `slug`.
let data = await cfapi.fetchAddon(slug)
cfcache.putAddon(data)
return data.addonFromForgeSvc
return data

proc fetchAddonFiles*(projectId: int): Future[seq[CfAddonFile]] {.async.} =
## get all addon files associated with the given `projectId`.
let data = await cfapi.fetchAddonFiles(projectId)
cfcache.putAddonFiles(data)
return data.addonFilesFromForgeSvc
return data

proc fetchAddonFilesChunks(fileIds: seq[int], fallback = true): Future[seq[CfAddonFile]] {.async.} =
## get all addons with their given `projectId`.
Expand All @@ -119,7 +119,7 @@ proc fetchAddonFilesChunks(fileIds: seq[int], fallback = true): Future[seq[CfAdd
try:
let data = await cfapi.fetchAddonFiles(fileIds)
cfcache.putAddonFiles(data)
return data.addonFilesFromForgeSvc
return data
except CfApiError as e:
# fallback to looking up the ids individually
if fallback:
Expand Down Expand Up @@ -161,8 +161,8 @@ proc fetchAddonFiles*(fileIds: seq[int], chunk = true): Future[seq[CfAddonFile]]
proc fetchAddonFile*(projectId: int, fileId: int): Future[CfAddonFile] {.async.} =
## get the addon file with the given `fileId` & `projectId`.
withCachedAddonFile(addonFile, fileId):
return addonFile.addonFileFromForgeSvc
return addonFile

let data = await cfapi.fetchAddonFile(projectId, fileId)
cfcache.putAddonFile(data)
return data.addonFileFromForgeSvc
return data
34 changes: 34 additions & 0 deletions src/api/cfcore.nim
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ converter addonFilesFromForgeSvc*(json: JsonNode): seq[CfAddonFile] =
## creates addon files from json retrieved by an forgesvc endpoint
return json.getElems().map(addonFileFromForgeSvc)

converter toJson*(file: CfAddonFile): JsonNode =
## creates json from an addon file
result = %* {
"id": file.fileId,
"displayName": file.name,
"releaseType": file.releaseType.ord,
"downloadUrl": file.downloadUrl,
"gameVersions": file.gameVersions.map((x) => $x),
"dependencies": file.dependencies.map((x) => %* {
"relationType": RequiredDependencyType,
"modId": x
})
}

converter addonFromForgeSvc*(json: JsonNode): CfAddon =
## creates an addon from json retrieved by an forgesvc endpoint
result = CfAddon()
Expand All @@ -83,6 +97,26 @@ converter addonsFromForgeSvc*(json: JsonNode): seq[CfAddon] =
## creates addons from json retrieved by an forgesvc endpoint
result = json.getElems().map(addonFromForgeSvc)

converter toJson*(addon: CfAddon): JsonNode =
result = %* {
"id": addon.projectId,
"name": addon.name,
"summary": addon.description,
"links": {
"websiteUrl": addon.websiteUrl
},
"authors": addon.authors.map((x) => %* {
"name": x
}),
"downloadCount": addon.downloads,
"gamePopularityRank": addon.popularity,
"latestFiles": addon.latestFiles.map((x) => x.toJson()),
"latestFilesIndexes": addon.gameVersionLatestFiles.map((x) => %* {
"gameVersion": x.version.`$`,
"fileId": x.fileId
})
}

proc isFabricCompatible*(file: CfAddonFile): bool =
## returns true if `file` is compatible with the fabric loader.
if "Fabric".Version in file.gameVersions:
Expand Down
1 change: 1 addition & 0 deletions src/nim.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-d:ssl
3 changes: 1 addition & 2 deletions src/pax.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import std/os
import therapist
import api/cfcache
import cmd/[add, cache, expo, impo, init, list, pin, remove, update, upgrade, version]
import term/[color, prompt]
import util/paxVersion
Expand Down Expand Up @@ -212,7 +211,7 @@ let spec = (
)

spec.parseOrHelp()
createDir(cfcache.cacheDir)
createDir(getCacheDir("pax"))

# GLOBAL OPTIONS
if commonArgs.yes.seen:
Expand Down

0 comments on commit fc0b384

Please sign in to comment.