Skip to content

Commit

Permalink
[enhancement] [bug] simulator URI
Browse files Browse the repository at this point in the history
  • Loading branch information
Keisuke Izumiya committed Nov 14, 2024
1 parent fd1a03e commit dd2f79e
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 165 deletions.
4 changes: 2 additions & 2 deletions src/pon2/app.nim
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
## Compile Options:
## | Option | Description | Default |
## | ------------------------------ | -------------------------------- | ------------------- |
## | `-d:pon2.path=<str>` | URI path of the web IDE. | `/pon2/` |
## | `-d:pon2.path=<str>` | URI path of the web simulator. | `/pon2/` |
## | `-d:pon2.workerfilename=<str>` | File name of the web worker. | `worker.min.js` |
## | `-d:pon2.assets.native=<str>` | Assets directory for native app. | `<Pon2Root>/assets` |
## | `-d:pon2.assets.web=<str>` | Assets directory for web app. | `./assets` |
Expand Down Expand Up @@ -70,7 +70,7 @@ export
simulator.moveOperatingPositionRight, simulator.moveOperatingPositionLeft,
simulator.rotateOperatingPositionRight, simulator.rotateOperatingPositionLeft,
simulator.forward, simulator.backward, simulator.reset, simulator.toUriQuery,
simulator.parseSimulator, simulator.operate
simulator.toUri, simulator.parseSimulator, simulator.operate
export solve.SolveAnswer, solve.solve

when defined(js):
Expand Down
89 changes: 3 additions & 86 deletions src/pon2/app/ide.nim
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
## This module implements the IDE.
##
## Compile Options:
## | Option | Description | Default |
## | -------------------- | ------------------------ | -------- |
## | `-d:pon2.path=<str>` | URI path of the web IDE. | `/pon2/` |
##
when defined(js):
## See also the [backend-specific documentation](./ide/web.html).
##
Expand All @@ -21,7 +16,7 @@ else:
{.experimental: "strictFuncs".}
{.experimental: "views".}

import std/[deques, sequtils, strformat, strutils, sugar, uri]
import std/[deques, sequtils, sugar, uri]
import ./[key, nazopuyo, simulator]
import ../core/[field, fqdn, nazopuyo, pairposition, puyopuyo, requirement]
import ../private/[misc]
Expand Down Expand Up @@ -58,11 +53,6 @@ type

progressBarData: tuple[now: Natural, total: Natural]

const IdeUriPath* {.define: "pon2.path".} = "/pon2/"

static:
doAssert IdeUriPath.startsWith '/'

# ------------------------------------------------
# Constructor
# ------------------------------------------------
Expand Down Expand Up @@ -312,85 +302,12 @@ proc prevAnswer*(self: Ide) {.inline.} =

func toUri*(self: Ide, withPositions = true, fqdn = Pon2): Uri {.inline.} =
## Returns the URI converted from the IDE.
result = initUri()
result.scheme =
case fqdn
of Pon2, Ishikawa: "https"
of Ips: "http"
result.hostname = $fqdn
result.query = self.simulator.toUriQuery(withPositions, fqdn)

# path
case fqdn
of Pon2:
result.path = IdeUriPath
of Ishikawa, Ips:
let modeChar =
case self.simulator.kind
of Regular:
case self.simulator.mode
of Play, PlayEditor: 's'
of Edit: 'e'
of View: 'v'
of Nazo:
'n'
result.path = &"/simu/p{modeChar}.html"

func allowedUriPaths(path: string): seq[string] {.inline.} =
## Returns the allowed paths.
result = @[path]

if path.endsWith "/index.html":
result.add path.dup(removeSuffix("index.html"))
elif path.endsWith '/':
result.add &"{path}index.html"

const AllowedSimulatorUriPaths = IdeUriPath.allowedUriPaths
self.simulator.toUri(withPositions, fqdn)

proc parseIde*(uri: Uri): Ide {.inline.} =
## Returns the IDE converted from the URI.
## If the URI is invalid, `ValueError` is raised.
var
kind = SimulatorKind.low
mode = SimulatorMode.low
let fqdn: IdeFqdn
case uri.hostname
of $Pon2:
if uri.path notin AllowedSimulatorUriPaths:
raise newException(ValueError, "Invalid IDE: " & $uri)

fqdn = Pon2
of $Ishikawa, $Ips:
fqdn = if uri.hostname == $Ishikawa: Ishikawa else: Ips

# kind, mode
case uri.path
of "/simu/pe.html":
kind = Regular
mode = Edit
of "/simu/ps.html":
kind = Regular
mode = Play
of "/simu/pv.html":
kind = Regular
mode = View
of "/simu/pn.html":
kind = Nazo
mode = Play
else:
result = newIde() # HACK: dummy to suppress warning
raise newException(ValueError, "Invalid IDE: " & $uri)
else:
fqdn = Pon2 # HACK: dummy to compile
result = newIde() # HACK: dummy to suppress warning
raise newException(ValueError, "Invalid IDE: " & $uri)

let simulator = uri.query.parseSimulator fqdn
if fqdn in {Ishikawa, Ips}:
simulator.kind = kind
simulator.mode = mode

result = simulator.newIde
uri.parseSimulator.newIde

# ------------------------------------------------
# Keyboard Operation
Expand Down
22 changes: 5 additions & 17 deletions src/pon2/app/ide/web.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import std/[strformat, sugar]
import karax/[karax, karaxdsl, kdom, vdom]
import ../[ide, key, simulator]
import ../simulator/[web]
import
../../private/app/ide/web/[answer, controller, pagination, settings, share, progress]
import ../../private/app/ide/web/[answer, controller, pagination, settings, progress]

# ------------------------------------------------
# Keyboard Handler
Expand Down Expand Up @@ -48,23 +47,16 @@ const
MainSimulatorIdPrefix = "pon2-ide-mainsimulator-"
AnswerSimulatorIdPrefix = "pon2-ide-answersimulator-"
SettingsIdPrefix = "pon2-ide-settings-"
ShareIdPrefix = "pon2-ide-share-"
AnswerShareIdPrefix = "pon2-ide-share-answer-"

proc newIdeNode(self: Ide, id: string): VNode {.inline.} =
## Returns the IDE node without the external section.
let
simulatorNode = self.simulator.newSimulatorNode(
wrapSection = false, id = &"{MainSimulatorIdPrefix}{id}"
)
settingsId = &"{SettingsIdPrefix}{id}"
let settingsId = &"{SettingsIdPrefix}{id}"

result = buildHtml(tdiv(class = "columns is-mobile is-variable is-1")):
tdiv(class = "column is-narrow"):
tdiv(class = "block"):
simulatorNode
tdiv(class = "block"):
self.newShareNode(&"{ShareIdPrefix}{id}", false)
self.simulator.newSimulatorNode(
wrapSection = false, id = &"{MainSimulatorIdPrefix}{id}"
)
if self.simulator.mode != Play and self.simulator.kind == Nazo:
tdiv(class = "column is-narrow"):
section(class = "section"):
Expand All @@ -80,10 +72,6 @@ proc newIdeNode(self: Ide, id: string): VNode {.inline.} =
if self.answerData.pairsPositionsSeq.len > 0:
tdiv(class = "block"):
self.newAnswerSimulatorNode &"{AnswerSimulatorIdPrefix}{id}"
tdiv(class = "block"):
self.newShareNode(&"{AnswerShareIdPrefix}{id}", true)
tdiv(class = "column is-narrow"):
self.newDisplayNode &"{ShareIdPrefix}{id}"

proc newIdeNode*(
self: Ide, setKeyHandler = true, wrapSection = true, id = ""
Expand Down
87 changes: 87 additions & 0 deletions src/pon2/app/simulator.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## This module implements Puyo Puyo simulators.
##
## Compile Options:
## | Option | Description | Default |
## | -------------------- | ------------------------------ | -------- |
## | `-d:pon2.path=<str>` | URI path of the web simulator. | `/pon2/` |
##
when defined(js):
## See also the [backend-specific documentation](./simulator/web.html).
##
Expand Down Expand Up @@ -75,6 +80,11 @@ type
operatingPos: Position
editing: SimulatorEditing

const SimulatorUriPath* {.define: "pon2.path".} = "/pon2/"

static:
doAssert SimulatorUriPath.startsWith '/'

# ------------------------------------------------
# Constructor
# ------------------------------------------------
Expand Down Expand Up @@ -662,6 +672,29 @@ func toUriQuery*(
of Ishikawa, Ips:
result = mainQuery

func toUri*(self: Simulator, withPositions = true, fqdn = Pon2): Uri {.inline.} =
## Returns the URI converted from the simulator.
result = initUri()
result.scheme = "https"
result.hostname = $fqdn
result.query = self.toUriQuery(withPositions, fqdn)

# path
case fqdn
of Pon2:
result.path = SimulatorUriPath
of Ishikawa, Ips:
let modeChar =
case self.kind
of Regular:
case self.mode
of Play, PlayEditor: 's'
of Edit: 'e'
of View: 'v'
of Nazo:
'n'
result.path = &"/simu/p{modeChar}.html"

func parseSimulator*(query: string, fqdn: IdeFqdn): Simulator {.inline.} =
## Returns the simulator converted from the URI query.
## If the URI is invalid, `ValueError` is raised.
Expand Down Expand Up @@ -716,6 +749,60 @@ func parseSimulator*(query: string, fqdn: IdeFqdn): Simulator {.inline.} =
of Ishikawa, Ips:
result = parsePuyoPuyo[TsuField](query, fqdn).newSimulator Play

func allowedUriPaths(path: string): seq[string] {.inline.} =
## Returns the allowed paths.
result = @[path]

if path.endsWith "/index.html":
result.add path.dup(removeSuffix("index.html"))
elif path.endsWith '/':
result.add &"{path}index.html"

const AllowedSimulatorUriPaths = SimulatorUriPath.allowedUriPaths

proc parseSimulator*(uri: Uri): Simulator {.inline.} =
## Returns the simulator converted from the URI.
## If the URI is invalid, `ValueError` is raised.
var
kind = SimulatorKind.low
mode = SimulatorMode.low
let fqdn: IdeFqdn
case uri.hostname
of $Pon2:
if uri.path notin AllowedSimulatorUriPaths:
raise newException(ValueError, "Invalid simulator: " & $uri)

fqdn = Pon2
of $Ishikawa, $Ips:
fqdn = if uri.hostname == $Ishikawa: Ishikawa else: Ips

# kind, mode
case uri.path
of "/simu/pe.html":
kind = Regular
mode = Edit
of "/simu/ps.html":
kind = Regular
mode = Play
of "/simu/pv.html":
kind = Regular
mode = View
of "/simu/pn.html":
kind = Nazo
mode = Play
else:
result = newSimulator[TsuField]() # HACK: dummy to suppress warning
raise newException(ValueError, "Invalid simulator: " & $uri)
else:
fqdn = Pon2 # HACK: dummy to compile
result = newSimulator[TsuField]() # HACK: dummy to suppress warning
raise newException(ValueError, "Invalid simulator: " & $uri)

result = uri.query.parseSimulator fqdn
if fqdn in {Ishikawa, Ips}:
result.kind = kind
result.mode = mode

# ------------------------------------------------
# Keyboard Operation
# ------------------------------------------------
Expand Down
25 changes: 17 additions & 8 deletions src/pon2/app/simulator/web.nim
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,25 @@ import
palette,
requirement,
select,
share,
]

# ------------------------------------------------
# Node
# ------------------------------------------------

const ReqIdPrefix = "pon2-simulator-req-"
const
ReqIdPrefix = "pon2-simulator-req-"
ShareIdPrefix = "pon2-simulator-share-"

proc newSimulatorNode(self: Simulator, id: string, hideSelect: bool): VNode {.inline.} =
proc newSimulatorNode(self: Simulator, id: string, isAnswer: bool): VNode {.inline.} =
## Returns the node without the external section.
buildHtml(tdiv):
tdiv(class = "block"):
self.newRequirementNode(id = &"{ReqIdPrefix}{id}")
let shareId = &"{ShareIdPrefix}{id}"

result = buildHtml(tdiv):
if self.kind == Nazo:
tdiv(class = "block"):
self.newRequirementNode &"{ReqIdPrefix}{id}"
tdiv(class = "block"):
tdiv(class = "columns is-mobile is-variable is-1"):
tdiv(class = "column is-narrow"):
Expand All @@ -46,9 +52,11 @@ proc newSimulatorNode(self: Simulator, id: string, hideSelect: bool): VNode {.in
if self.mode != Edit:
tdiv(class = "block"):
self.newMessagesNode
if not hideSelect and self.mode != Play:
if not isAnswer and self.mode != Play:
tdiv(class = "block"):
self.newSelectNode
tdiv(class = "block"):
self.newShareNode(shareId, isAnswer)
if self.mode != Edit:
tdiv(class = "column is-narrow"):
tdiv(class = "block"):
Expand All @@ -61,12 +69,13 @@ proc newSimulatorNode(self: Simulator, id: string, hideSelect: bool): VNode {.in
self.newPaletteNode
tdiv(class = "block"):
self.newPairsNode
self.newDisplayNode shareId

proc newSimulatorNode*(
self: Simulator, wrapSection = true, id = "", hideSelect = false
self: Simulator, wrapSection = true, id = "", isAnswer = false
): VNode {.inline.} =
## Returns the simulator node.
let node = self.newSimulatorNode(id, hideSelect)
let node = self.newSimulatorNode(id, isAnswer)

if wrapSection:
result = buildHtml(section(class = "section")):
Expand Down
2 changes: 1 addition & 1 deletion src/pon2/private/app/ide/web/answer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ import ../../../../app/simulator/[web]

proc newAnswerSimulatorNode*(ide: Ide, id: string): VNode {.inline.} =
## Returns the answer simulator node.
ide.answerSimulator.newSimulatorNode(wrapSection = false, id = id, hideSelect = true)
ide.answerSimulator.newSimulatorNode(wrapSection = false, id = id, isAnswer = true)
Loading

0 comments on commit dd2f79e

Please sign in to comment.