Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fad16246a | ||
|
|
a5e1fffc25 | ||
|
|
b63dbd4aab | ||
|
|
4bb34d212a | ||
|
|
b420341e60 | ||
|
|
e24f269e62 | ||
|
|
8bb677258f | ||
|
|
556b3c5879 | ||
|
|
9e6545229f | ||
|
|
d0b3097851 | ||
|
|
9fc0a09f6f | ||
|
|
93181f57a7 | ||
|
|
050aa5c90e | ||
|
|
5bc5f8d2d9 | ||
|
|
d0ef91759d | ||
|
|
bb26364522 | ||
|
|
49cee91239 | ||
|
|
a0f8e2d31e | ||
|
|
87ee7f26d0 | ||
|
|
0c18821fca | ||
|
|
a66b172dc5 | ||
|
|
3d3578c801 | ||
|
|
c528e4796c | ||
|
|
b5b70bbdc7 | ||
|
|
1c864231ec | ||
|
|
e1e5ebc614 | ||
|
|
1d1e235d99 | ||
|
|
53c95b4e3a | ||
|
|
7173a79e98 | ||
|
|
b42649b249 | ||
|
|
e844e8100e | ||
|
|
b9ca315dad | ||
|
|
472af2e766 | ||
|
|
d5d16cd627 | ||
|
|
454fbd9686 | ||
|
|
6886a049ae | ||
|
|
45e61ae350 | ||
|
|
3a3e0325a2 | ||
|
|
d81012e5fc |
@@ -5,7 +5,6 @@ extension MeetupId {
|
||||
public struct V: View {
|
||||
@StateObject var isJoinAvailable = BusUI.Value(K.isJoinAvailable, false)
|
||||
@StateObject var isLoading = BusUI.Value(K.isLoading, false)
|
||||
@StateObject var join = BusUI.Button(K.join)
|
||||
@StateObject var textField = BusUI.TextField(K.textApp, K.textUI)
|
||||
let processors: [Any]
|
||||
|
||||
@@ -34,7 +33,7 @@ extension MeetupId {
|
||||
}
|
||||
}
|
||||
|
||||
Button(action: join.v.send) {
|
||||
Button(action: { Bus.send(K.join, true) }) {
|
||||
Text("Join")
|
||||
.padding(8)
|
||||
.border(
|
||||
|
||||
18
Modules/MicX/Mic/Mic.yml
Normal file
18
Modules/MicX/Mic/Mic.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
version: 2
|
||||
|
||||
model:
|
||||
activeIds: [[String], []]
|
||||
activityDates: [[String:Date], [:]]
|
||||
requestActivityDate: [String?, nil]
|
||||
|
||||
service:
|
||||
actions:
|
||||
shouldDeliverActivityDates: Bus.deliver(K.activityDate, v)
|
||||
shouldResetActivityDates: Bus.send(K.activityDates, v)
|
||||
|
||||
pipes:
|
||||
activeIds: [recent, K]
|
||||
activityDates: [recent, K]
|
||||
requestActivityDate: [toggleNil, K]
|
||||
|
||||
world:
|
||||
5
Modules/MicX/Mic/src/Mic.C.swift
Normal file
5
Modules/MicX/Mic/src/Mic.C.swift
Normal file
@@ -0,0 +1,5 @@
|
||||
public extension Mic {
|
||||
enum C {
|
||||
static let activityTimeout: TimeInterval = 5
|
||||
}
|
||||
}
|
||||
140
Modules/MicX/Mic/src/Mic.Generated.swift
Normal file
140
Modules/MicX/Mic/src/Mic.Generated.swift
Normal file
@@ -0,0 +1,140 @@
|
||||
// ВНИМАНИЕ Сгенерировано автоматом из файла Mic.yml
|
||||
// ВНИМАНИЕ Не менять руками!
|
||||
|
||||
import AELog
|
||||
import BusX
|
||||
import Combine
|
||||
import Foundation
|
||||
import MPAKX
|
||||
import UIKit
|
||||
|
||||
// MARK: - Context
|
||||
|
||||
public protocol MicContext {
|
||||
var activeIds: MPAK.Recent<[String]> { get }
|
||||
var activityDates: MPAK.Recent<[String:Date]> { get }
|
||||
var requestActivityDate: String? { get }
|
||||
}
|
||||
|
||||
// MARK: - Controller
|
||||
|
||||
extension Mic {
|
||||
final class Controller: MPAK.Controller<Mic.Model> {
|
||||
init() {
|
||||
super.init(
|
||||
Mic.Model(),
|
||||
debugClassName: "MicCtrl",
|
||||
debugLog: { aelog($0) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - Model
|
||||
|
||||
public struct Model: MicContext {
|
||||
public var activeIds: MPAK.Recent<[String]> = .init([])
|
||||
public var activityDates: MPAK.Recent<[String:Date]> = .init([:])
|
||||
public var requestActivityDate: String? = nil
|
||||
}
|
||||
|
||||
// MARK: - Service
|
||||
|
||||
public final class Service {
|
||||
let ctrl = Controller()
|
||||
let world: World
|
||||
|
||||
var subscriptions = [AnyCancellable]()
|
||||
static private(set) weak var singleton: Service?
|
||||
|
||||
public init(_ world: World) {
|
||||
self.world = world
|
||||
Self.singleton = self
|
||||
SectionGenerated.setupPlatform(ctrl, self, world)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - World
|
||||
|
||||
public struct World {
|
||||
|
||||
|
||||
public init(
|
||||
|
||||
) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
enum SectionGenerated {
|
||||
|
||||
|
||||
|
||||
// MARK: - SectionGenerated Service
|
||||
|
||||
static func setupPlatform(
|
||||
_ ctrl: Controller,
|
||||
_ service: Service,
|
||||
_ world: World
|
||||
) {
|
||||
// MARK: - SectionGenerated Service Actions
|
||||
|
||||
ctrl.m
|
||||
.compactMap { shouldDeliverActivityDates($0) }
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { v in Bus.deliver(K.activityDate, v) }
|
||||
.store(in: &service.subscriptions)
|
||||
ctrl.m
|
||||
.compactMap { shouldResetActivityDates($0) }
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { v in Bus.send(K.activityDates, v) }
|
||||
.store(in: &service.subscriptions)
|
||||
|
||||
|
||||
// MARK: - SectionGenerated Service Pipes
|
||||
|
||||
ctrl.pipeValue(
|
||||
dbg: "activeI",
|
||||
sub: nil,
|
||||
Bus.events.compactMap { Bus.convertKeyValue(K.activeIds, $0) }.map { (k: String, v: [String]) in v }.eraseToAnyPublisher(),
|
||||
{
|
||||
$0.activeIds.value = $1
|
||||
$0.activeIds.isRecent = true
|
||||
},
|
||||
{ m, _ in m.activeIds.isRecent = false }
|
||||
)
|
||||
|
||||
|
||||
|
||||
ctrl.pipeValue(
|
||||
dbg: "activityD",
|
||||
sub: nil,
|
||||
Bus.events.compactMap { Bus.convertKeyValue(K.activityDates, $0) }.map { (k: String, v: [String:Date]) in v }.eraseToAnyPublisher(),
|
||||
{
|
||||
$0.activityDates.value = $1
|
||||
$0.activityDates.isRecent = true
|
||||
},
|
||||
{ m, _ in m.activityDates.isRecent = false }
|
||||
)
|
||||
|
||||
|
||||
|
||||
ctrl.pipeValue(
|
||||
dbg: "requestAD",
|
||||
sub: nil,
|
||||
Bus.events.compactMap { Bus.convertKeyValue(K.requestActivityDate, $0) }.map { (k: String, v: String?) in v }.eraseToAnyPublisher(),
|
||||
{ $0.requestActivityDate = $1 },
|
||||
{ m, _ in m.requestActivityDate = nil }
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
public extension Mic {
|
||||
enum K {
|
||||
public static let activeIds = "Mic.activeIds"
|
||||
public static let activityDate = "Mic.activityDate"
|
||||
public static let activityDates = "Mic.activityDates"
|
||||
public static let isActive = "Mic.isActive"
|
||||
public static let MI = "Mic.Model.Item"
|
||||
public static let MI = "MicItem"
|
||||
public static let requestActivityDate = "Mic.requestActivityDate"
|
||||
public static let timeout = "Mic.timeout"
|
||||
}
|
||||
}
|
||||
|
||||
63
Modules/MicX/Mic/src/Mic.Shoulds.swift
Normal file
63
Modules/MicX/Mic/src/Mic.Shoulds.swift
Normal file
@@ -0,0 +1,63 @@
|
||||
public extension Mic {
|
||||
/// Следует доставить состояния звуковой активности элементам
|
||||
///
|
||||
/// Условия:
|
||||
/// 1. Изменились даты активности
|
||||
/// 2. Элемент запросил свою дату активности при наличии валидной даты
|
||||
/// 2. Элемент запросил свою дату активности без наличия валидной даты
|
||||
///
|
||||
/// - Returns: Словарь состояний
|
||||
static func shouldDeliverActivityDates(_ c: MicContext) -> [String: Date]? {
|
||||
if let ad = shouldResetActivityDates(c) {
|
||||
return ad
|
||||
}
|
||||
|
||||
if
|
||||
let id = c.requestActivityDate,
|
||||
let timeout = c.activityDates.value[id],
|
||||
timeout > Date()
|
||||
{
|
||||
return [id: timeout]
|
||||
}
|
||||
|
||||
if let id = c.requestActivityDate {
|
||||
return [id: .distantPast]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Следует обновить словарь состояний звуковой активности
|
||||
///
|
||||
/// Условия:
|
||||
/// 1. Пришли id активных участников
|
||||
///
|
||||
/// - Returns: Словарь активных состояний
|
||||
static func shouldResetActivityDates(_ c: MicContext) -> [String: Date]? {
|
||||
if c.activeIds.isRecent {
|
||||
var ad = c.activityDates.value
|
||||
let ids = c.activeIds.value
|
||||
let now = Date()
|
||||
// Задаём абсолютную дату истечения для активных id.
|
||||
for id in ids {
|
||||
ad[id] = now + C.activityTimeout
|
||||
}
|
||||
|
||||
// Собираем истёкшие id.
|
||||
var expiredIds = [String]()
|
||||
for (id, timeout) in ad {
|
||||
if timeout < now {
|
||||
expiredIds.append(id)
|
||||
}
|
||||
}
|
||||
// Удаляем истёкшие id.
|
||||
for id in expiredIds {
|
||||
ad[id] = nil
|
||||
}
|
||||
|
||||
return ad
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
4
Modules/MicX/MicItem/MicItem.yml
Normal file
4
Modules/MicX/MicItem/MicItem.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
version: 5
|
||||
|
||||
activityDate: [Date?, nil, recent]
|
||||
timeout: [Bool, false, toggle]
|
||||
@@ -1,5 +1,14 @@
|
||||
// ВНИМАНИЕ Сгенерировано автоматом из файла MicItem.yml
|
||||
// ВНИМАНИЕ Не менять руками!
|
||||
|
||||
import AELog
|
||||
import BusX
|
||||
import Combine
|
||||
import Foundation
|
||||
import MPAKX
|
||||
import UIKit
|
||||
|
||||
// MARK: - Context
|
||||
|
||||
public protocol MicItemContext {
|
||||
var activityDate: MPAK.Recent<Date?> { get }
|
||||
@@ -7,35 +16,35 @@ public protocol MicItemContext {
|
||||
}
|
||||
|
||||
extension MicItem {
|
||||
|
||||
// MARK: - Model
|
||||
|
||||
public struct Model: MicItemContext {
|
||||
public var activityDate: MPAK.Recent<Date?> = .init(nil)
|
||||
public var timeout: Bool = false
|
||||
}
|
||||
}
|
||||
|
||||
extension MicItem {
|
||||
// MARK: - Controller
|
||||
|
||||
final class Controller: MPAK.Controller<MicItem.Model> {
|
||||
init(_ id: String? = nil) {
|
||||
var sid = ""
|
||||
if let id {
|
||||
sid = " : \(id)"
|
||||
}
|
||||
/**/print("MicIC.init\(sid)")
|
||||
super.init(
|
||||
MicItem.Model(),
|
||||
debugClassName: "MicICtrl",
|
||||
debugLog: { print("\($0)\(sid)") }
|
||||
debugClassName: "MicItemCtrl",
|
||||
debugLog: { aelog("\($0)\(sid)") }
|
||||
)
|
||||
|
||||
|
||||
// Нижеследующее предстоит сгенерить.
|
||||
// Отправляем модель в Шину.
|
||||
m
|
||||
.sink { v in Bus.send(Bus.keyId(K.MI, id), v) }
|
||||
.sink { v in Bus.send(Bus.keyId("MicItem", id), v) }
|
||||
.store(in: &subscriptions)
|
||||
|
||||
pipeValue(
|
||||
dbg: "activityD",
|
||||
sub: nil,
|
||||
Bus.events.compactMap { Bus.convertKeyValue(Bus.keyId(K.activityDate, id), $0) }.map { (k: String, v: Date?) in v }.eraseToAnyPublisher(),
|
||||
{
|
||||
$0.activityDate.value = $1
|
||||
@@ -43,10 +52,8 @@ extension MicItem {
|
||||
},
|
||||
{ m, _ in m.activityDate.isRecent = false }
|
||||
)
|
||||
|
||||
pipe(
|
||||
dbg: "timeout",
|
||||
sub: nil,
|
||||
Bus.events.compactMap { Bus.convertKeyValue(Bus.keyId(K.timeout, id), $0) }.map { (k: String, v: Bool) in v }.eraseToAnyPublisher(),
|
||||
{ $0.timeout = true },
|
||||
{ $0.timeout = false }
|
||||
@@ -1,4 +1,11 @@
|
||||
public extension MicItem {
|
||||
/// Следует задать активность элемента
|
||||
///
|
||||
/// Условия:
|
||||
/// 1. Пришла актуальная дата завершения активности
|
||||
/// 2. Таймер сообщил о завершении активности
|
||||
///
|
||||
/// - Returns: Состояние активности элемента
|
||||
static func shouldResetActivity(_ c: MicItemContext) -> Bool? {
|
||||
if
|
||||
c.activityDate.isRecent,
|
||||
@@ -15,6 +22,12 @@ public extension MicItem {
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Следует задать время истечения активности
|
||||
///
|
||||
/// Условия:
|
||||
/// 1. Пришла актуальная дата завершения активности
|
||||
///
|
||||
/// - Returns: Время истечения активности
|
||||
static func shouldResetTimeout(_ c: MicItemContext) -> TimeInterval? {
|
||||
if
|
||||
c.activityDate.isRecent,
|
||||
|
||||
@@ -3,19 +3,25 @@ import SwiftUI
|
||||
|
||||
extension MicItem {
|
||||
public struct V: View {
|
||||
var id: String?
|
||||
let id: String?
|
||||
@StateObject var holder = BusUI.Holder()
|
||||
@StateObject var isActive = BusUI.Value(K.isActive, false)
|
||||
let proc: [Any]
|
||||
|
||||
|
||||
public init(_ id: String? = nil) {
|
||||
self.id = id
|
||||
self.proc = [
|
||||
}
|
||||
|
||||
func configure(_ id: String? = nil) {
|
||||
isActive.id = id
|
||||
holder.items = [
|
||||
Bus.Delay(shouldResetTimeout, K.MI, K.timeout, id),
|
||||
Bus.Sync(shouldResetActivity, K.MI, K.isActive, id),
|
||||
MicItem.Controller(id)
|
||||
MicItem.Controller(id),
|
||||
]
|
||||
// Запрашиваем актуальные данные при (пере)создании.
|
||||
Bus.send(K.requestActivityDate, id)
|
||||
}
|
||||
|
||||
|
||||
public var body: some View {
|
||||
Text("Mic activity")
|
||||
.padding(8)
|
||||
@@ -24,12 +30,8 @@ extension MicItem {
|
||||
width: isActive.v ? 3 : 1
|
||||
)
|
||||
.animation(.easeInOut(duration: 0.3))
|
||||
.onAppear {
|
||||
isActive.id = id
|
||||
}
|
||||
.onChange(of: id) { newValue in
|
||||
isActive.id = newValue
|
||||
}
|
||||
.onAppear { self.configure(id) }
|
||||
.onChange(of: id) { id in self.configure(id) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,373 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
from argparse import ArgumentParser
|
||||
|
||||
parser = ArgumentParser(prog='generate v1')
|
||||
parser.add_argument('module', type=str,
|
||||
help='the name of the module to generate')
|
||||
parser.add_argument('-i', '--input', type=str,
|
||||
help='The path and name of the input file')
|
||||
parser.add_argument('-o', '--output', type=str,
|
||||
help='The path of the output files')
|
||||
|
||||
args = parser.parse_args()
|
||||
DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
MODULE = args.module
|
||||
|
||||
def isNotKeyword(str):
|
||||
keywords = [
|
||||
"core",
|
||||
"ex",
|
||||
"recent",
|
||||
"service",
|
||||
"set",
|
||||
"toggle",
|
||||
"toggleNil",
|
||||
"vm",
|
||||
"$vm",
|
||||
"world",
|
||||
]
|
||||
return str not in keywords
|
||||
|
||||
class CoreAction:
|
||||
def __init__(self, func, sink):
|
||||
self.func = func
|
||||
self.sink = sink
|
||||
|
||||
def code(self):
|
||||
return f"""
|
||||
p.ctrl.m
|
||||
.compactMap {{ {self.func}($0) }}
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink {{ [weak core = p.core] v in {self.sink} }}
|
||||
.store(in: &p.core.subscriptions)
|
||||
""".replace("\n\n", "\n")
|
||||
|
||||
class CorePipe:
|
||||
def __init__(self, name, props):
|
||||
self.name = name
|
||||
self.props = props
|
||||
self.resetMethod()
|
||||
self.resetDbg()
|
||||
self.resetSrc()
|
||||
self.resetExName()
|
||||
self.resetSteps()
|
||||
|
||||
def code(self):
|
||||
return f"""
|
||||
p.ctrl.{self.method}(
|
||||
dbg: "{self.dbg}",
|
||||
sub: &p.core.subscriptions,
|
||||
{self.src}.eraseToAnyPublisher(),
|
||||
{self.steps}
|
||||
)
|
||||
""".replace("\n\n", "\n")
|
||||
|
||||
def resetDbg(self):
|
||||
capitals = [l for l in self.name if l.isupper()]
|
||||
# Нет заглавных.
|
||||
if len(capitals) == 0:
|
||||
self.dbg = self.name
|
||||
return
|
||||
# Есть заглавные.
|
||||
firstCap = self.name.find(capitals[0])
|
||||
self.dbg = self.name[:firstCap] + "".join(capitals)
|
||||
|
||||
def resetExName(self):
|
||||
firstLetter = self.name[0].capitalize()
|
||||
self.exName = f"""ex{firstLetter}{self.name[1:]}"""
|
||||
|
||||
def resetMethod(self):
|
||||
if "toggle" in self.props:
|
||||
self.method = "pipe"
|
||||
else:
|
||||
self.method = "pipeValue"
|
||||
|
||||
def resetSrc(self):
|
||||
if "core" in self.props:
|
||||
self.src = "p.core." + self.name
|
||||
elif "world" in self.props:
|
||||
self.src = "p.world." + self.name
|
||||
elif "vm" in self.props:
|
||||
self.src = "p.core.vm." + self.name
|
||||
elif "$vm" in self.props:
|
||||
self.src = "p.core.vm.$" + self.name
|
||||
else:
|
||||
# Если это что-то неизвестное заранее, то ищем строку,
|
||||
# отличную от известных ключевых слов.
|
||||
self.src = next(filter(isNotKeyword, self.props))
|
||||
|
||||
def resetSteps(self):
|
||||
if "recent" and "ex" in self.props:
|
||||
self.steps = f"""
|
||||
{{
|
||||
$0.{self.name}.value = $1
|
||||
$0.{self.name}.isRecent = true
|
||||
}},
|
||||
{{
|
||||
$0.{self.name}.isRecent = false
|
||||
$0.{self.exName} = $1
|
||||
}}
|
||||
"""
|
||||
elif "recent" in self.props:
|
||||
self.steps = f"""
|
||||
{{
|
||||
$0.{self.name}.value = $1
|
||||
$0.{self.name}.isRecent = true
|
||||
}},
|
||||
{{ m, _ in m.{self.name}.isRecent = false }}
|
||||
"""
|
||||
elif "set" in self.props:
|
||||
self.steps = f"""
|
||||
{{ $0.{self.name} = $1 }}
|
||||
"""
|
||||
elif "toggle" in self.props:
|
||||
self.steps = f"""
|
||||
{{ $0.{self.name} = true }},
|
||||
{{ $0.{self.name} = false }}
|
||||
"""
|
||||
elif "toggleNil" in self.props:
|
||||
self.steps = f"""
|
||||
{{ $0.{self.name} = $1 }},
|
||||
{{ m, _ in m.{self.name} = nil }}
|
||||
"""
|
||||
|
||||
class ServiceAction:
|
||||
def __init__(self, func, sink):
|
||||
self.func = func
|
||||
self.sink = sink
|
||||
|
||||
def code(self):
|
||||
return f"""
|
||||
sp.ctrl.m
|
||||
.compactMap {{ {self.func}($0) }}
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink {{ v in {self.sink} }}
|
||||
.store(in: &sp.service.subscriptions)
|
||||
""".replace("\n\n", "\n")
|
||||
|
||||
class ServicePipe:
|
||||
def __init__(self, name, props):
|
||||
self.name = name
|
||||
self.props = props
|
||||
self.resetMethod()
|
||||
self.resetDbg()
|
||||
self.resetSrc()
|
||||
self.resetExName()
|
||||
self.resetSteps()
|
||||
|
||||
def code(self):
|
||||
return f"""
|
||||
sp.ctrl.{self.method}(
|
||||
dbg: "{self.dbg}",
|
||||
{self.src}.eraseToAnyPublisher(),
|
||||
{self.steps}
|
||||
)
|
||||
""".replace("\n\n", "\n")
|
||||
|
||||
def resetDbg(self):
|
||||
capitals = [l for l in self.name if l.isupper()]
|
||||
# Нет заглавных.
|
||||
if len(capitals) == 0:
|
||||
self.dbg = self.name
|
||||
return
|
||||
# Есть заглавные.
|
||||
firstCap = self.name.find(capitals[0])
|
||||
self.dbg = self.name[:firstCap] + "".join(capitals)
|
||||
|
||||
def resetExName(self):
|
||||
firstLetter = self.name[0].capitalize()
|
||||
self.exName = f"""ex{firstLetter}{self.name[1:]}"""
|
||||
|
||||
def resetMethod(self):
|
||||
if "toggle" in self.props:
|
||||
self.method = "pipe"
|
||||
else:
|
||||
self.method = "pipeValue"
|
||||
|
||||
def resetSrc(self):
|
||||
if "service" in self.props:
|
||||
self.src = "sp.service." + self.name
|
||||
elif "world" in self.props:
|
||||
self.src = "sp.world." + self.name
|
||||
else:
|
||||
# Если это и не сервис, и не мир, то
|
||||
# ищем строку, отличную от известных ключевых слов.
|
||||
self.src = next(filter(isNotKeyword, self.props))
|
||||
|
||||
def resetSteps(self):
|
||||
if "recent" and "ex" in self.props:
|
||||
self.steps = f"""
|
||||
{{
|
||||
$0.{self.name}.value = $1
|
||||
$0.{self.name}.isRecent = true
|
||||
}},
|
||||
{{
|
||||
$0.{self.name}.isRecent = false
|
||||
$0.{self.exName} = $1
|
||||
}}
|
||||
"""
|
||||
elif "recent" in self.props:
|
||||
self.steps = f"""
|
||||
{{
|
||||
$0.{self.name}.value = $1
|
||||
$0.{self.name}.isRecent = true
|
||||
}},
|
||||
{{ m, _ in m.{self.name}.isRecent = false }}
|
||||
"""
|
||||
elif "set" in self.props:
|
||||
self.steps = f"""
|
||||
{{ $0.{self.name} = $1 }}
|
||||
"""
|
||||
elif "toggle" in self.props:
|
||||
self.steps = f"""
|
||||
{{ $0.{self.name} = true }},
|
||||
{{ $0.{self.name} = false }}
|
||||
"""
|
||||
elif "toggleNil" in self.props:
|
||||
self.steps = f"""
|
||||
{{ $0.{self.name} = $1 }},
|
||||
{{ m, _ in m.{self.name} = nil }}
|
||||
"""
|
||||
|
||||
class State:
|
||||
isCoreAction = False
|
||||
isCoreController = False
|
||||
isServiceAction = False
|
||||
isServiceController = False
|
||||
|
||||
def generatePlatform(coreActions, corePipes, serviceActions, servicePipes):
|
||||
corePlatform = ""
|
||||
if len(coreActions) > 0 or len(corePipes) > 0:
|
||||
corePlatform = f"""
|
||||
static func setupPlatform(_ p: CoreParameters) {{
|
||||
{coreActions}
|
||||
{corePipes}
|
||||
}}
|
||||
"""
|
||||
|
||||
return f"""
|
||||
// ВНИМАНИЕ: Сгенерировано автоматом, не менять руками!
|
||||
|
||||
import Foundation
|
||||
|
||||
extension {MODULE} {{
|
||||
enum SectionGenerated {{
|
||||
{corePlatform}
|
||||
static func setupPlatform(_ sp: ServiceParameters) {{
|
||||
{serviceActions}
|
||||
{servicePipes}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
|
||||
def generateCoreAction(ln):
|
||||
parts = ln.split(": ")
|
||||
if len(parts) != 2:
|
||||
return None
|
||||
func = parts[0].lstrip()
|
||||
sink = parts[1].rstrip()
|
||||
action = CoreAction(func, sink)
|
||||
return action.code()
|
||||
|
||||
def generateCorePipe(ln):
|
||||
parts = ln.split(": [")
|
||||
if len(parts) != 2:
|
||||
return None
|
||||
name = parts[0].lstrip()
|
||||
props = parts[1][:-1].split(", ")
|
||||
pipe = CorePipe(name, props)
|
||||
return pipe.code()
|
||||
|
||||
def generateServiceAction(ln):
|
||||
parts = ln.split(": ")
|
||||
if len(parts) != 2:
|
||||
return None
|
||||
func = parts[0].lstrip()
|
||||
sink = parts[1].rstrip()
|
||||
action = ServiceAction(func, sink)
|
||||
return action.code()
|
||||
|
||||
def generateServicePipe(ln):
|
||||
parts = ln.split(": [")
|
||||
if len(parts) != 2:
|
||||
return None
|
||||
name = parts[0].lstrip()
|
||||
props = parts[1][:-1].split(", ")
|
||||
pipe = ServicePipe(name, props)
|
||||
return pipe.code()
|
||||
|
||||
def readFile(fileName):
|
||||
lines = []
|
||||
with open(fileName) as file:
|
||||
for line in file:
|
||||
lines.append(line.rstrip())
|
||||
return lines
|
||||
|
||||
def resetState(ln):
|
||||
if ln.startswith("ca:"):
|
||||
state.isCoreAction = True
|
||||
state.isCoreController = False
|
||||
state.isServiceAction = False
|
||||
state.isServiceController = False
|
||||
elif ln.startswith("cp:"):
|
||||
state.isCoreAction = False
|
||||
state.isCoreController = True
|
||||
state.isServiceAction = False
|
||||
state.isServiceController = False
|
||||
elif ln.startswith("sa:"):
|
||||
state.isCoreAction = False
|
||||
state.isCoreController = False
|
||||
state.isServiceAction = True
|
||||
state.isServiceController = False
|
||||
elif ln.startswith("sp:"):
|
||||
state.isCoreAction = False
|
||||
state.isCoreController = False
|
||||
state.isServiceAction = False
|
||||
state.isServiceController = True
|
||||
return state
|
||||
|
||||
def saveFile(fileName, data):
|
||||
with open(fileName, "w") as file:
|
||||
file.write(data)
|
||||
|
||||
def validateVersion(ln):
|
||||
if ln.startswith("version: 1") == False:
|
||||
print("ERROR: Invalid version")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# Main
|
||||
state = State()
|
||||
print(f"Generating platform for module '{MODULE}'...")
|
||||
fileIn = args.input or f"{DIR}/../../../Modules/{MODULE}/{MODULE}.yml"
|
||||
fileOut = args.output or f"{DIR}/../../../Modules/{MODULE}/src"
|
||||
fileOut = os.path.join(fileOut, f'{MODULE}.SectionGenerated.swift')
|
||||
lines = readFile(fileIn)
|
||||
validateVersion(lines[0])
|
||||
coreActions = ""
|
||||
corePipes = ""
|
||||
serviceActions = ""
|
||||
servicePipes = ""
|
||||
for ln in lines:
|
||||
st = resetState(ln)
|
||||
if st.isCoreAction:
|
||||
action = generateCoreAction(ln)
|
||||
if action:
|
||||
coreActions += action
|
||||
if st.isCoreController:
|
||||
pipe = generateCorePipe(ln)
|
||||
if pipe:
|
||||
corePipes += pipe
|
||||
if st.isServiceAction:
|
||||
action = generateServiceAction(ln)
|
||||
if action:
|
||||
serviceActions += action
|
||||
if st.isServiceController:
|
||||
pipe = generateServicePipe(ln)
|
||||
if pipe:
|
||||
servicePipes += pipe
|
||||
result = generatePlatform(coreActions, corePipes, serviceActions, servicePipes)
|
||||
saveFile(fileOut, result)
|
||||
1
Utilities/platform/2/generation
Symbolic link
1
Utilities/platform/2/generation
Symbolic link
@@ -0,0 +1 @@
|
||||
/Users/mk/m/u/Utilities/platform/2/generation
|
||||
@@ -1,29 +0,0 @@
|
||||
class Result:
|
||||
def __init__(self, dir, path, module, readFile, shortenName, src, structure):
|
||||
self.dir = dir
|
||||
self.path = path
|
||||
self.module = module
|
||||
self.readFile = readFile
|
||||
self.shortenName = shortenName
|
||||
self.src = src
|
||||
self.structure = structure
|
||||
|
||||
self.contextFields = ""
|
||||
self.core = ""
|
||||
self.coreSectionGenerated = ""
|
||||
self.coreSectionGeneratedActions = ""
|
||||
self.coreSectionGeneratedPipes = ""
|
||||
self.coreSectionsDestroy = ""
|
||||
self.coreSectionsSetup = ""
|
||||
self.coreVM = ""
|
||||
self.coreWindow = ""
|
||||
self.file = ""
|
||||
self.imports = ""
|
||||
self.modelFields = ""
|
||||
self.serviceCore = ""
|
||||
self.serviceSectionGenerated = ""
|
||||
self.serviceSectionGeneratedActions = ""
|
||||
self.serviceSectionGeneratedPipes = ""
|
||||
self.serviceSections = ""
|
||||
self.worldFields = ""
|
||||
self.worldParameters = ""
|
||||
@@ -1,10 +0,0 @@
|
||||
from generation.isPipeRecent import *
|
||||
|
||||
def fieldFormat(fmtPlain, fmtRecent, name, structure):
|
||||
fmt = fmtPlain
|
||||
if (
|
||||
isPipeRecent(name, structure.core) or
|
||||
isPipeRecent(name, structure.service)
|
||||
):
|
||||
fmt = fmtRecent
|
||||
return fmt
|
||||
@@ -1,18 +0,0 @@
|
||||
from generation.fieldFormat import *
|
||||
|
||||
def generateContextFields(c):
|
||||
fileName = f"{c.dir}/templates/context-field"
|
||||
lines = c.readFile(fileName)
|
||||
fmtPlain = lines[0]
|
||||
fmtRecent = lines[1]
|
||||
fields = []
|
||||
|
||||
for key in c.structure.model.fields:
|
||||
values = c.structure.model.fields[key]
|
||||
fmt = fieldFormat(fmtPlain, fmtRecent, key, c.structure)
|
||||
ln = fmt \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", values[0])
|
||||
fields.append(ln)
|
||||
|
||||
c.contextFields = "\n".join(fields)
|
||||
@@ -1,7 +0,0 @@
|
||||
def generateCore(c):
|
||||
fileName = f"{c.dir}/templates/core"
|
||||
lines = c.readFile(fileName)
|
||||
c.core = "\n".join(lines)
|
||||
|
||||
fileName = f"{c.dir}/templates/service-core"
|
||||
c.serviceCore = c.readFile(fileName)[0]
|
||||
@@ -1,4 +0,0 @@
|
||||
def generateCoreSectionGenerated(c):
|
||||
fileName = f"{c.dir}/templates/core-section-generated"
|
||||
lines = c.readFile(fileName)
|
||||
c.coreSectionGenerated = "\n".join(lines)
|
||||
@@ -1,37 +0,0 @@
|
||||
from generation.sectionGeneratedActionShouldLoad import *
|
||||
|
||||
def generateCoreSectionGeneratedActions(c):
|
||||
base = f"{c.dir}/templates/core-section-generated-action"
|
||||
fmtCommon = c.readFile(base)
|
||||
fmtInstant = c.readFile(f"{base}-instant")
|
||||
|
||||
for key in c.structure.core.actions:
|
||||
value = c.structure.core.actions[key]
|
||||
|
||||
# Шаблонные действия.
|
||||
if value == "":
|
||||
# shouldLoad*.
|
||||
shouldLoad = "shouldLoad"
|
||||
if key.startswith(shouldLoad):
|
||||
c.coreSectionGeneratedActions += sectionGeneratedActionShouldLoad(key, "core", c)
|
||||
continue
|
||||
|
||||
continue
|
||||
|
||||
|
||||
output = ""
|
||||
|
||||
action = key
|
||||
template = fmtCommon
|
||||
# Действие без receive(on:)
|
||||
if action.startswith("🚀"):
|
||||
action = action[1:]
|
||||
template = fmtInstant
|
||||
|
||||
for fmt in template:
|
||||
ln = fmt \
|
||||
.replace("%SHOULD%", action) \
|
||||
.replace("%SINK%", value)
|
||||
output += ln + "\n"
|
||||
|
||||
c.coreSectionGeneratedActions += output
|
||||
@@ -1,19 +0,0 @@
|
||||
from generation.sectionFunctions import *
|
||||
from generation.sectionNames import *
|
||||
|
||||
def generateCoreSectionsDestroy(c):
|
||||
fileName = f"{c.dir}/templates/core-section"
|
||||
fmt = c.readFile(fileName)[0]
|
||||
items = []
|
||||
|
||||
sections = sectionNames(c)
|
||||
for name in sections:
|
||||
# Пропускаем секции, не относящиеся к удалению ядра.
|
||||
funcs = sectionFunctions(name, c)
|
||||
if "destroyCore" not in funcs:
|
||||
continue
|
||||
|
||||
ln = fmt.replace("%NAME%", name)
|
||||
items.append(ln)
|
||||
|
||||
c.coreSectionsDestroy = "\n".join(items)
|
||||
@@ -1,27 +0,0 @@
|
||||
from generation.hasSectionGenerated import *
|
||||
from generation.sectionFunctions import *
|
||||
from generation.sectionNames import *
|
||||
|
||||
def generateCoreSectionsSetup(c):
|
||||
fileName = f"{c.dir}/templates/core-section"
|
||||
lines = c.readFile(fileName)
|
||||
fmtCore = lines[1]
|
||||
fmtPlatform = lines[2]
|
||||
items = []
|
||||
|
||||
sections = sectionNames(c)
|
||||
for name in sections:
|
||||
# Пропускаем секции, не относящиеся к установке ядра.
|
||||
funcs = sectionFunctions(name, c)
|
||||
if "setupCore" not in funcs:
|
||||
continue
|
||||
|
||||
ln = fmtCore.replace("%NAME%", name)
|
||||
items.append(ln)
|
||||
|
||||
# Генерированная секция.
|
||||
# Должна быть добавлена последней.
|
||||
if hasSectionGenerated(c.structure.core):
|
||||
items.append(fmtPlatform)
|
||||
|
||||
c.coreSectionsSetup = "\n".join(items)
|
||||
@@ -1,11 +0,0 @@
|
||||
from generation.sectionNames import *
|
||||
|
||||
def generateCoreWindow(c):
|
||||
fileName = f"{c.dir}/templates/core-window"
|
||||
lines = c.readFile(fileName)
|
||||
fmtV = lines[0]
|
||||
fmtW = lines[1]
|
||||
sections = sectionNames(c)
|
||||
if "UI" in sections:
|
||||
c.coreVM = fmtV
|
||||
c.coreWindow = fmtW
|
||||
@@ -1,28 +0,0 @@
|
||||
def generateFile(c):
|
||||
fileName = f"{c.dir}/templates/file"
|
||||
fmt = c.readFile(fileName)
|
||||
|
||||
for ln in fmt:
|
||||
newLine = ln \
|
||||
.replace("%CONTEXT_FIELDS%", c.contextFields) \
|
||||
.replace("%CORE%", c.core) \
|
||||
.replace("%CORE_SECTION_GENERATED%", c.coreSectionGenerated) \
|
||||
.replace("%CORE_SECTION_GENERATED_ACTIONS%", c.coreSectionGeneratedActions) \
|
||||
.replace("%CORE_SECTION_GENERATED_PIPES%", c.coreSectionGeneratedPipes) \
|
||||
.replace("%CORE_SECTIONS_DESTROY%", c.coreSectionsDestroy) \
|
||||
.replace("%CORE_SECTIONS_SETUP%", c.coreSectionsSetup) \
|
||||
.replace("%CORE_VM%", c.coreVM) \
|
||||
.replace("%CORE_WINDOW%", c.coreWindow) \
|
||||
.replace("%IMPORTS%", c.imports) \
|
||||
.replace("%MODEL_FIELDS%", c.modelFields) \
|
||||
.replace("%SERVICE_CORE%", c.serviceCore) \
|
||||
.replace("%SERVICE_SECTION_GENERATED%", c.serviceSectionGenerated) \
|
||||
.replace("%SERVICE_SECTION_GENERATED_ACTIONS%", c.serviceSectionGeneratedActions) \
|
||||
.replace("%SERVICE_SECTION_GENERATED_PIPES%", c.serviceSectionGeneratedPipes) \
|
||||
.replace("%SERVICE_SECTIONS%", c.serviceSections) \
|
||||
.replace("%WORLD_CONSTRUCTOR%", c.worldConstructor) \
|
||||
.replace("%WORLD_FIELDS%", c.worldFields) \
|
||||
.replace("%WORLD_PARAMETERS%", c.worldParameters) \
|
||||
.replace("%MODULE%", c.module) \
|
||||
.replace("%MODULE_SHORT%", c.shortenName(c.module)) # Замены %MODULE*% должны быть в конце.
|
||||
c.file += newLine + "\n"
|
||||
@@ -1,19 +0,0 @@
|
||||
def generateImports(c):
|
||||
fileName = f"{c.src}/../{c.module}.podspec"
|
||||
# Для сборного модуля используем путь к корневому podspec.
|
||||
if c.path != c.module:
|
||||
parent = c.path.split("/")[0]
|
||||
fileName = fileName.replace(f"{c.module}/src/../{c.module}", parent)
|
||||
# Для обычного модуля с окончание X учитываем такое же название podspec.
|
||||
if c.path[-1] == "X":
|
||||
fileName = fileName.replace(f"{c.module}.podspec", f"{c.module}X.podspec")
|
||||
lines = c.readFile(fileName)
|
||||
items = ["Combine", "Foundation", "UIKit"]
|
||||
|
||||
prefix = "s.dependency '"
|
||||
for ln in lines:
|
||||
if ln.startswith(prefix):
|
||||
name = ln[len(prefix):-1]
|
||||
items.append(name)
|
||||
|
||||
c.imports = "import " + "\nimport ".join(sorted(items))
|
||||
@@ -1,19 +0,0 @@
|
||||
from generation.fieldFormat import *
|
||||
|
||||
def generateModelFields(c):
|
||||
fileName = f"{c.dir}/templates/model-field"
|
||||
lines = c.readFile(fileName)
|
||||
fmtPlain = lines[0]
|
||||
fmtRecent = lines[1]
|
||||
fields = []
|
||||
|
||||
for key in c.structure.model.fields:
|
||||
values = c.structure.model.fields[key]
|
||||
fmt = fieldFormat(fmtPlain, fmtRecent, key, c.structure)
|
||||
ln = fmt \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", values[0]) \
|
||||
.replace("%DEFAULT%", values[1])
|
||||
fields.append(ln)
|
||||
|
||||
c.modelFields = "\n".join(fields)
|
||||
@@ -1,4 +0,0 @@
|
||||
def generateServiceSectionGenerated(c):
|
||||
fileName = f"{c.dir}/templates/service-section-generated"
|
||||
lines = c.readFile(fileName)
|
||||
c.serviceSectionGenerated = "\n".join(lines)
|
||||
@@ -1,79 +0,0 @@
|
||||
from generation.sectionGeneratedActionShouldLoad import *
|
||||
|
||||
def generateServiceSectionGeneratedActions(c):
|
||||
base = f"{c.dir}/templates/service-section-generated-action"
|
||||
fmtBusModel = c.readFile(f"{base}-bus-model")
|
||||
fmtCommon = c.readFile(base)
|
||||
fmtInstant = c.readFile(f"{base}-instant")
|
||||
fmtRelay = c.readFile(f"{base}-relay")
|
||||
fmtInstantDelay = c.readFile(f"{base}-instant-delay")
|
||||
fmtInstantRelay = c.readFile(f"{base}-instant-relay")
|
||||
fmtInstantModel = c.readFile(f"{base}-instant-model")
|
||||
fmtInstantShouldResetCore = c.readFile(f"{base}-instant-shouldResetCore")
|
||||
fmtModel = c.readFile(f"{base}-model")
|
||||
fmtShouldResetCore = c.readFile(f"{base}-shouldResetCore")
|
||||
|
||||
canRelay = "🚀model" in c.structure.service.actions
|
||||
if canRelay:
|
||||
c.serviceSectionGeneratedActions += "\n".join(fmtRelay + [''])
|
||||
|
||||
for key in c.structure.service.actions:
|
||||
value = c.structure.service.actions[key]
|
||||
|
||||
# Шаблонные действия.
|
||||
if value == "":
|
||||
# busModel.
|
||||
if key == "busModel":
|
||||
c.serviceSectionGeneratedActions += "\n".join(fmtBusModel) + "\n"
|
||||
continue
|
||||
|
||||
# instant model.
|
||||
if key == "🚀model":
|
||||
c.serviceSectionGeneratedActions += "\n".join(fmtInstantModel) + "\n"
|
||||
continue
|
||||
|
||||
# model.
|
||||
if key == "model":
|
||||
c.serviceSectionGeneratedActions += "\n".join(fmtModel) + "\n"
|
||||
continue
|
||||
|
||||
# shouldLoad*.
|
||||
shouldLoad = "shouldLoad"
|
||||
if key.startswith(shouldLoad):
|
||||
c.serviceSectionGeneratedActions += sectionGeneratedActionShouldLoad(key, "service", c)
|
||||
continue
|
||||
|
||||
# instant shouldResetCore.
|
||||
if key == "🚀shouldResetCore":
|
||||
c.serviceSectionGeneratedActions += "\n".join(fmtInstantShouldResetCore) + "\n"
|
||||
continue
|
||||
|
||||
# shouldResetCore.
|
||||
if key == "shouldResetCore":
|
||||
c.serviceSectionGeneratedActions += "\n".join(fmtShouldResetCore) + "\n"
|
||||
continue
|
||||
|
||||
continue
|
||||
|
||||
output = ""
|
||||
|
||||
action = key
|
||||
template = fmtCommon
|
||||
# Действие без receive(on:)
|
||||
if action.startswith("🚀"):
|
||||
action = action[1:]
|
||||
template = fmtInstantRelay if canRelay else fmtInstant
|
||||
|
||||
# Действие c .delay(on:)
|
||||
if action.startswith("⏳"):
|
||||
action = action[1:]
|
||||
template = fmtInstantDelay
|
||||
|
||||
for fmt in template:
|
||||
ln = fmt \
|
||||
.replace("%SHOULD%", action) \
|
||||
.replace("%SINK%", value) \
|
||||
.replace("%MODULE%", c.module)
|
||||
output += ln + "\n"
|
||||
|
||||
c.serviceSectionGeneratedActions += output
|
||||
@@ -1,27 +0,0 @@
|
||||
from generation.hasSectionGenerated import *
|
||||
from generation.sectionFunctions import *
|
||||
from generation.sectionNames import *
|
||||
|
||||
def generateServiceSections(c):
|
||||
fileName = f"{c.dir}/templates/service-section"
|
||||
lines = c.readFile(fileName)
|
||||
fmtService = lines[0]
|
||||
fmtPlatform = lines[1]
|
||||
items = []
|
||||
|
||||
sections = sectionNames(c)
|
||||
for name in sections:
|
||||
# Пропускаем секции, не относящиеся к сервису.
|
||||
funcs = sectionFunctions(name, c)
|
||||
if "setupService" not in funcs:
|
||||
continue
|
||||
|
||||
ln = fmtService.replace("%NAME%", name)
|
||||
items.append(ln)
|
||||
|
||||
# Генерированная секция.
|
||||
# Должна быть добавлена последней.
|
||||
if hasSectionGenerated(c.structure.service):
|
||||
items.append(fmtPlatform)
|
||||
|
||||
c.serviceSections= "\n".join(items)
|
||||
@@ -1,43 +0,0 @@
|
||||
from generation.generateContextFields import *
|
||||
from generation.generateCore import *
|
||||
from generation.generateCoreSectionGenerated import *
|
||||
from generation.generateCoreSectionGeneratedActions import *
|
||||
from generation.generateCoreSectionsDestroy import *
|
||||
from generation.generateCoreSectionsSetup import *
|
||||
from generation.generateCoreWindow import *
|
||||
from generation.generateFile import *
|
||||
from generation.generateImports import *
|
||||
from generation.generateModelFields import *
|
||||
from generation.generateServiceSectionGenerated import *
|
||||
from generation.generateServiceSectionGeneratedActions import *
|
||||
from generation.generateServiceSections import *
|
||||
from generation.generateWorldConstructor import *
|
||||
from generation.generateWorldFields import *
|
||||
from generation.generateWorldParameters import *
|
||||
from generation.hasSectionGenerated import *
|
||||
from generation.sectionGeneratedPipes import *
|
||||
|
||||
def generateStructure(c):
|
||||
generateContextFields(c)
|
||||
generateImports(c)
|
||||
generateModelFields(c)
|
||||
# Генерируем ядро лишь при наличии инструкций в YML.
|
||||
if hasSectionGenerated(c.structure.core):
|
||||
generateCore(c)
|
||||
generateCoreSectionsDestroy(c)
|
||||
generateCoreSectionsSetup(c)
|
||||
generateCoreWindow(c)
|
||||
generateCoreSectionGenerated(c)
|
||||
generateCoreSectionGeneratedActions(c)
|
||||
c.coreSectionGeneratedPipes = sectionGeneratedPipes(c.structure.core, "&core.subscriptions", c)
|
||||
generateServiceSections(c)
|
||||
# Генерируем секцию сервиса лишь при наличии инструкций в YML.
|
||||
if hasSectionGenerated(c.structure.service):
|
||||
generateServiceSectionGenerated(c)
|
||||
generateServiceSectionGeneratedActions(c)
|
||||
c.serviceSectionGeneratedPipes = sectionGeneratedPipes(c.structure.service, "nil", c)
|
||||
generateWorldConstructor(c)
|
||||
generateWorldFields(c)
|
||||
generateWorldParameters(c)
|
||||
# Файл обязательно генерировать последним: зависит от остальных.
|
||||
generateFile(c)
|
||||
@@ -1,18 +0,0 @@
|
||||
def generateWorldConstructor(c):
|
||||
fileName = f"{c.dir}/templates/world-constructor"
|
||||
fmt = c.readFile(fileName)[0]
|
||||
|
||||
items = []
|
||||
|
||||
for key in c.structure.world.fields:
|
||||
values = c.structure.world.fields[key]
|
||||
|
||||
if (
|
||||
"init" in values or
|
||||
key == "model" or
|
||||
key == "net"
|
||||
):
|
||||
ln = fmt.replace("%NAME%", key)
|
||||
items.append(ln)
|
||||
|
||||
c.worldConstructor = "\n".join(items)
|
||||
@@ -1,77 +0,0 @@
|
||||
from generation.worldFieldTypeInit import *
|
||||
from generation.worldFieldTypePS import *
|
||||
|
||||
def generateWorldFields(c):
|
||||
fileName = f"{c.dir}/templates/world-field"
|
||||
lines = c.readFile(fileName)
|
||||
fmtInitType = lines[0]
|
||||
fmtCVS = lines[1]
|
||||
fmtInit = lines[2]
|
||||
fmtModel = lines[3]
|
||||
fmtNet = lines[4]
|
||||
fmtPS = lines[5]
|
||||
fmtVar = lines[6]
|
||||
fields = []
|
||||
|
||||
for key in c.structure.world.fields:
|
||||
values = c.structure.world.fields[key]
|
||||
|
||||
# [TYPE, DEFAULT, cvs] -> CurrentValueSubject
|
||||
if "cvs" in values:
|
||||
type = values[0]
|
||||
default = values[1]
|
||||
ln = fmtCVS \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", type) \
|
||||
.replace("%DEFAULT%", default)
|
||||
fields.append(ln)
|
||||
|
||||
# [escape, init], [TYPE, escape, init] -> let TYPE
|
||||
elif "escape" in values and "init" in values:
|
||||
type = worldFieldTypeInit(key, c.structure)
|
||||
fmt = fmtInit
|
||||
if len(values) == 3:
|
||||
fmt = fmtInitType
|
||||
ln = fmt \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", type)
|
||||
fields.append(ln)
|
||||
|
||||
# [init], [TYPE, init] -> let TYPE
|
||||
elif "init" in values:
|
||||
type = worldFieldTypeInit(key, c.structure)
|
||||
fmt = fmtInit
|
||||
if len(values) == 2:
|
||||
fmt = fmtInitType
|
||||
ln = fmt \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", type)
|
||||
fields.append(ln)
|
||||
|
||||
# model -> PassthroughSubject<Model>
|
||||
elif key == "model":
|
||||
fields.append(fmtModel)
|
||||
|
||||
# net -> Net.Publisher
|
||||
elif key == "net":
|
||||
fields.append(fmtNet)
|
||||
|
||||
# [ps], [TYPE, ps] -> PassthroughSubject
|
||||
elif "ps" in values:
|
||||
type = worldFieldTypePS(key, c.structure)
|
||||
ln = fmtPS \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", type)
|
||||
fields.append(ln)
|
||||
|
||||
# [TYPE, DEFAULT, var] -> var TYPE
|
||||
elif "var" in values:
|
||||
type = values[0]
|
||||
default = values[1]
|
||||
ln = fmtVar \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", type) \
|
||||
.replace("%DEFAULT%", default)
|
||||
fields.append(ln)
|
||||
|
||||
c.worldFields = "\n".join(fields)
|
||||
@@ -1,35 +0,0 @@
|
||||
from generation.worldFieldTypeInit import *
|
||||
|
||||
def generateWorldParameters(c):
|
||||
fileName = f"{c.dir}/templates/world-parameter"
|
||||
lines = c.readFile(fileName)
|
||||
fmtInitType = lines[0]
|
||||
fmtEscInitType = lines[1]
|
||||
fmtInit = lines[2]
|
||||
fmtModel = lines[3]
|
||||
fmtNet = lines[4]
|
||||
|
||||
params = []
|
||||
|
||||
for key in c.structure.world.fields:
|
||||
values = c.structure.world.fields[key]
|
||||
|
||||
if "init" in values:
|
||||
type = worldFieldTypeInit(key, c.structure)
|
||||
fmt = fmtInit
|
||||
if "escape" in values:
|
||||
fmt = fmtEscInitType
|
||||
elif len(values) > 1:
|
||||
fmt = fmtInitType
|
||||
ln = fmt \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%TYPE%", type)
|
||||
params.append(ln)
|
||||
|
||||
elif key == "model":
|
||||
params.append(fmtModel)
|
||||
|
||||
elif key == "net":
|
||||
params.append(fmtNet)
|
||||
|
||||
c.worldParameters = ",\n".join(params)
|
||||
@@ -1,2 +0,0 @@
|
||||
def hasSectionGenerated(entity):
|
||||
return len(entity.actions) or len(entity.pipes) or entity.isPresent
|
||||
@@ -1,11 +0,0 @@
|
||||
def isNotKeyword(str):
|
||||
keywords = [
|
||||
"ex",
|
||||
"recent",
|
||||
"set",
|
||||
"toggle",
|
||||
"toggleNil",
|
||||
"vm",
|
||||
"$vm"
|
||||
]
|
||||
return str not in keywords
|
||||
@@ -1,5 +0,0 @@
|
||||
def isPipeRecent(name, entity):
|
||||
if name in entity.pipes:
|
||||
props = entity.pipes[name]
|
||||
return "recent" in props
|
||||
return False
|
||||
@@ -1,14 +0,0 @@
|
||||
def isToggle(name, structure):
|
||||
# Проверяем наличие `toggle` в свойствах канала ядра.
|
||||
if name in structure.core.pipes:
|
||||
props = structure.core.pipes[name]
|
||||
if "toggle" in props:
|
||||
return True
|
||||
|
||||
# Проверяем наличие `toggle` в свойствах канала сервиса.
|
||||
if name in structure.service.pipes:
|
||||
props = structure.service.pipes[name]
|
||||
if "toggle" in props:
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -1,5 +0,0 @@
|
||||
def pipeBusSource(name, entity, busKey, structure, fmt):
|
||||
valueType = structure.model.fields[name][0]
|
||||
return fmt \
|
||||
.replace("%BUS_KEY%", busKey) \
|
||||
.replace("%BUS_VALUE_TYPE%", valueType)
|
||||
@@ -1,14 +0,0 @@
|
||||
def pipeFormat(fmtExRecent, fmtRecent, fmtSet, fmtToggle, fmtToggleNil, name, entity):
|
||||
props = entity.pipes[name]
|
||||
if "recent" and "ex" in props:
|
||||
return fmtExRecent
|
||||
elif "recent" in props:
|
||||
return fmtRecent
|
||||
elif "set" in props:
|
||||
return fmtSet
|
||||
elif "toggle" in props:
|
||||
return fmtToggle
|
||||
elif "toggleNil" in props:
|
||||
return fmtToggleNil
|
||||
|
||||
return "НИНАЮ"
|
||||
@@ -1,20 +0,0 @@
|
||||
from generation.isNotKeyword import *
|
||||
|
||||
def pipeSource(name, entity):
|
||||
props = entity.pipes[name]
|
||||
if "vm" in props:
|
||||
return "core.vm." + name
|
||||
elif "$vm" in props:
|
||||
return "core.vm.$" + name
|
||||
else:
|
||||
# Если это что-то неизвестное заранее, то ищем строку,
|
||||
# отличную от известных ключевых слов для инструкции pipe.
|
||||
default = "world"
|
||||
src = next(filter(isNotKeyword, props), default)
|
||||
# Прямое обращение к VM.
|
||||
if src.startswith("vm."):
|
||||
src = "core." + src
|
||||
# Значение по умолчанию.
|
||||
elif src == default:
|
||||
return default + "." + name
|
||||
return src
|
||||
@@ -1,14 +0,0 @@
|
||||
def sectionFunctions(name, c):
|
||||
path = f"{c.src}/{c.module}.Section{name}.swift"
|
||||
lines = c.readFile(path)
|
||||
items = []
|
||||
|
||||
for ln in lines:
|
||||
if ln.startswith(" static func destroyCore"):
|
||||
items.append("destroyCore")
|
||||
elif ln.startswith(" static func setupCore"):
|
||||
items.append("setupCore")
|
||||
elif ln.startswith(" static func setupService"):
|
||||
items.append("setupService")
|
||||
|
||||
return items
|
||||
@@ -1,14 +0,0 @@
|
||||
def sectionGeneratedActionShouldLoad(key, sub, c):
|
||||
lines = c.readFile(f"{c.dir}/templates/section-generated-action-shouldLoad")
|
||||
name = key[len("shouldLoad"):]
|
||||
request = name[0].lower() + name[1:]
|
||||
output = ""
|
||||
|
||||
for fmt in lines:
|
||||
ln = fmt \
|
||||
.replace("%NAME%", name) \
|
||||
.replace("%REQUEST%", request) \
|
||||
.replace("%SUB%", sub)
|
||||
output += ln + "\n"
|
||||
|
||||
return output
|
||||
@@ -1,47 +0,0 @@
|
||||
from generation.shortenName import *
|
||||
from generation.pipeBusSource import *
|
||||
from generation.pipeFormat import *
|
||||
from generation.pipeSource import *
|
||||
|
||||
def sectionGeneratedPipes(entity, sub, c):
|
||||
fmtBusPipe = c.readFile(f"{c.dir}/templates/section-generated-pipe-src-bus")[0]
|
||||
fmtExRecent = c.readFile(f"{c.dir}/templates/section-generated-pipe-ex-recent")
|
||||
fmtRecent = c.readFile(f"{c.dir}/templates/section-generated-pipe-recent")
|
||||
fmtSet = c.readFile(f"{c.dir}/templates/section-generated-pipe-set")
|
||||
fmtToggle = c.readFile(f"{c.dir}/templates/section-generated-pipe-toggle")
|
||||
fmtToggleNil = c.readFile(f"{c.dir}/templates/section-generated-pipe-toggleNil")
|
||||
output = ""
|
||||
|
||||
for key in entity.pipes:
|
||||
values = entity.pipes[key]
|
||||
|
||||
# EX_NAME.
|
||||
firstLetter = key[:1].capitalize()
|
||||
exName = f"""ex{firstLetter}{key[1:]}"""
|
||||
|
||||
# PIPE.
|
||||
pipe = "pipeValue"
|
||||
if "toggle" in values:
|
||||
pipe = "pipe"
|
||||
|
||||
# SHORT_SRC.
|
||||
shortSrc = shortenName(key)
|
||||
|
||||
# SRC.
|
||||
src = pipeSource(key, entity)
|
||||
# Bus.
|
||||
if src.startswith("K."):
|
||||
src = pipeBusSource(key, entity, src, c.structure, fmtBusPipe)
|
||||
|
||||
fmtPipe = pipeFormat(fmtExRecent, fmtRecent, fmtSet, fmtToggle, fmtToggleNil, key, entity)
|
||||
for fmt in fmtPipe:
|
||||
ln = fmt \
|
||||
.replace("%EX_NAME%", exName) \
|
||||
.replace("%NAME%", key) \
|
||||
.replace("%PIPE%", pipe) \
|
||||
.replace("%SHORT_SRC%", shortSrc) \
|
||||
.replace("%SRC%", src) \
|
||||
.replace("%SUB%", sub)
|
||||
output += ln + "\n"
|
||||
|
||||
return output
|
||||
@@ -1,19 +0,0 @@
|
||||
import os
|
||||
|
||||
def sectionNames(c):
|
||||
prefix = "Section"
|
||||
ending = ".swift"
|
||||
fileNames = os.listdir(c.src)
|
||||
items = []
|
||||
|
||||
for fileName in sorted(fileNames):
|
||||
# Пропускаем файлы, не являющиеся секциями.
|
||||
id = fileName.find(prefix)
|
||||
if id == -1:
|
||||
continue
|
||||
|
||||
# Вычленяем имя секции из имени файла.
|
||||
name = fileName[id + len(prefix):-len(ending)]
|
||||
items.append(name)
|
||||
|
||||
return items
|
||||
@@ -1,24 +0,0 @@
|
||||
def shortenName(text):
|
||||
capitals = [l for l in text if l.isupper()]
|
||||
# Нет заглавных.
|
||||
if len(capitals) == 0:
|
||||
return text
|
||||
|
||||
capId = 0
|
||||
# Первая - заглавная.
|
||||
if text[0].isupper():
|
||||
capId = 1
|
||||
|
||||
# Заглавная лишь первая.
|
||||
if (
|
||||
capId == 1 and
|
||||
len(capitals) == 1
|
||||
):
|
||||
return text
|
||||
|
||||
# Убираем первое заглавное слово.
|
||||
if capId == 1:
|
||||
capitals = capitals[1:]
|
||||
# Есть ещё заглавные.
|
||||
firstCap = text.find(capitals[0])
|
||||
return text[:firstCap] + "".join(capitals)
|
||||
@@ -1,12 +0,0 @@
|
||||
from generation.isToggle import *
|
||||
|
||||
_keywords = { "init", "var", "escape" }
|
||||
|
||||
def worldFieldTypeInit(key, structure):
|
||||
values = structure.world.fields[key]
|
||||
values = [v for v in values if v not in _keywords]
|
||||
if len(values) == 1:
|
||||
return values[0]
|
||||
if isToggle(key, structure):
|
||||
return "Void"
|
||||
return structure.model.fields[key][0]
|
||||
@@ -1,9 +0,0 @@
|
||||
from generation.isToggle import *
|
||||
|
||||
def worldFieldTypePS(key, structure):
|
||||
values = structure.world.fields[key]
|
||||
if len(values) == 2:
|
||||
return values[0]
|
||||
if isToggle(key, structure):
|
||||
return "Void"
|
||||
return structure.model.fields[key][0]
|
||||
1
Utilities/platform/2/parsing
Symbolic link
1
Utilities/platform/2/parsing
Symbolic link
@@ -0,0 +1 @@
|
||||
/Users/mk/m/u/Utilities/platform/2/parsing
|
||||
@@ -1,39 +0,0 @@
|
||||
from parsing.readKeyValue import *
|
||||
from parsing.valueArray import *
|
||||
|
||||
class CoreService:
|
||||
def __init__(self):
|
||||
self.actions = {}
|
||||
self.isPresent = False
|
||||
self.pipes = {}
|
||||
|
||||
def parseAction(self, line):
|
||||
kv = readKeyValue(line)
|
||||
|
||||
# В действии всегда должен быть хотя бы ключ.
|
||||
if kv is None:
|
||||
return
|
||||
|
||||
# Лишь ключ.
|
||||
if kv[1] is None:
|
||||
self.actions[kv[0]] = ""
|
||||
return
|
||||
|
||||
# Ключ и значение.
|
||||
self.actions[kv[0]] = kv[1]
|
||||
|
||||
def parsePipe(self, line):
|
||||
kv = readKeyValue(line)
|
||||
# В канале всегда должны быть и ключ, и значение.
|
||||
if (
|
||||
kv is None or
|
||||
kv[1] is None
|
||||
):
|
||||
return
|
||||
|
||||
values = valueArray(kv[1])
|
||||
# В канале каждое значение должно быть массивом.
|
||||
if values is None:
|
||||
return
|
||||
|
||||
self.pipes[kv[0]] = values
|
||||
@@ -1,31 +0,0 @@
|
||||
class Mode:
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def parseLine(self, line):
|
||||
if line.startswith("model:"):
|
||||
self.reset()
|
||||
self.isModel = True
|
||||
elif line.startswith("core:"):
|
||||
self.reset()
|
||||
self.isCore = True
|
||||
elif line.startswith("service:"):
|
||||
self.reset()
|
||||
self.isService = True
|
||||
elif line.startswith("world:"):
|
||||
self.reset()
|
||||
self.isWorld = True
|
||||
elif line.startswith(" actions:"):
|
||||
self.isAction = True
|
||||
self.isPipe = False
|
||||
elif line.startswith(" pipes:"):
|
||||
self.isAction = False
|
||||
self.isPipe = True
|
||||
|
||||
def reset(self):
|
||||
self.isAction = False
|
||||
self.isCore = False
|
||||
self.isModel = False
|
||||
self.isPipe = False
|
||||
self.isService = False
|
||||
self.isWorld = False
|
||||
@@ -1,24 +0,0 @@
|
||||
from parsing.readKeyValue import *
|
||||
from parsing.valueArray import *
|
||||
|
||||
class ModelWorld:
|
||||
def __init__(self):
|
||||
self.fields = {}
|
||||
|
||||
def parseLine(self, line):
|
||||
kv = readKeyValue(line)
|
||||
# В модели/мире всегда должен быть хотя бы ключ.
|
||||
if kv is None:
|
||||
return
|
||||
|
||||
# Лишь ключ.
|
||||
if kv[1] is None:
|
||||
self.fields[kv[0]] = []
|
||||
return
|
||||
|
||||
values = valueArray(kv[1])
|
||||
# В модели/мире указанное значение должно быть массивом.
|
||||
if values is None:
|
||||
return
|
||||
|
||||
self.fields[kv[0]] = values
|
||||
@@ -1,9 +0,0 @@
|
||||
from parsing.CoreService import *
|
||||
from parsing.ModelWorld import *
|
||||
|
||||
class Structure:
|
||||
def __init__(self):
|
||||
self.core = CoreService()
|
||||
self.model = ModelWorld()
|
||||
self.service = CoreService()
|
||||
self.world = ModelWorld()
|
||||
@@ -1,42 +0,0 @@
|
||||
from parsing.CoreService import *
|
||||
from parsing.Mode import *
|
||||
from parsing.ModelWorld import *
|
||||
|
||||
def parseLines(lines, structure):
|
||||
mode = Mode()
|
||||
for line in lines:
|
||||
# Определяем режим строки.
|
||||
mode.parseLine(line)
|
||||
ln = line.strip()
|
||||
|
||||
# Игнорируем пустую строку.
|
||||
if len(ln) == 0:
|
||||
continue
|
||||
|
||||
# Игнорируем комментарий.
|
||||
if ln.startswith("#"):
|
||||
continue
|
||||
|
||||
# model.
|
||||
if mode.isModel:
|
||||
structure.model.parseLine(ln)
|
||||
|
||||
# core.
|
||||
elif mode.isCore:
|
||||
structure.core.isPresent = True
|
||||
if mode.isAction:
|
||||
structure.core.parseAction(ln)
|
||||
elif mode.isPipe:
|
||||
structure.core.parsePipe(ln)
|
||||
|
||||
# service.
|
||||
elif mode.isService:
|
||||
structure.service.isPresent = True
|
||||
if mode.isAction:
|
||||
structure.service.parseAction(ln)
|
||||
elif mode.isPipe:
|
||||
structure.service.parsePipe(ln)
|
||||
|
||||
# world.
|
||||
elif mode.isWorld:
|
||||
structure.world.parseLine(ln)
|
||||
@@ -1,15 +0,0 @@
|
||||
def readKeyValue(line):
|
||||
parts = line.split(": ")
|
||||
# Ключ со значением.
|
||||
if len(parts) == 2:
|
||||
return (parts[0], parts[1])
|
||||
|
||||
# Ключ без значения.
|
||||
if (
|
||||
line.endswith(":") == False and
|
||||
len(parts) == 1
|
||||
):
|
||||
return (parts[0], None)
|
||||
|
||||
# Не является ключом со значением.
|
||||
return None
|
||||
@@ -1,12 +0,0 @@
|
||||
def valueArray(value):
|
||||
# Удостоверяем наличие скобок.
|
||||
if not (
|
||||
value.startswith("[") and
|
||||
value.endswith("]")
|
||||
):
|
||||
return None
|
||||
|
||||
# Исключаем скобки.
|
||||
noBrackets = value[1:-1]
|
||||
parts = noBrackets.split(", ")
|
||||
return parts
|
||||
1
Utilities/platform/2/templates
Symbolic link
1
Utilities/platform/2/templates
Symbolic link
@@ -0,0 +1 @@
|
||||
/Users/mk/m/u/Utilities/platform/2/templates
|
||||
@@ -1,2 +0,0 @@
|
||||
var %NAME%: %TYPE% { get }
|
||||
var %NAME%: MPAK.Recent<%TYPE%> { get }
|
||||
@@ -1,17 +0,0 @@
|
||||
// MARK: - Core
|
||||
|
||||
final class Core {
|
||||
%CORE_VM%
|
||||
var subscriptions = [AnyCancellable]()
|
||||
%CORE_WINDOW%
|
||||
|
||||
deinit {
|
||||
/**/aelog("😟 %MODULE_SHORT%Core.deinit")
|
||||
%CORE_SECTIONS_DESTROY%
|
||||
}
|
||||
|
||||
init(_ ctrl: %MODULE%.Controller, _ world: %MODULE%.World) {
|
||||
/**/aelog("😀 %MODULE_SHORT%Core.init")
|
||||
%CORE_SECTIONS_SETUP%
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
Section%NAME%.destroyCore(self)
|
||||
Section%NAME%.setupCore(self, ctrl, world)
|
||||
SectionGenerated.setupPlatform(self, ctrl, world)
|
||||
@@ -1,15 +0,0 @@
|
||||
// MARK: - SectionGenerated Core
|
||||
|
||||
static func setupPlatform(
|
||||
_ core: Core,
|
||||
_ ctrl: Controller,
|
||||
_ world: World
|
||||
) {
|
||||
// MARK: - SectionGenerated Core Actions
|
||||
|
||||
%CORE_SECTION_GENERATED_ACTIONS%
|
||||
|
||||
// MARK: - SectionGenerated Core Pipes
|
||||
|
||||
%CORE_SECTION_GENERATED_PIPES%
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { %SHOULD%($0) }
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak core] v in %SINK% }
|
||||
.store(in: &core.subscriptions)
|
||||
@@ -1,4 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { %SHOULD%($0) }
|
||||
.sink { [weak core] v in %SINK% }
|
||||
.store(in: &core.subscriptions)
|
||||
@@ -1,2 +0,0 @@
|
||||
let vm = VM()
|
||||
var wnd: UIWindow?
|
||||
@@ -1,68 +0,0 @@
|
||||
// ВНИМАНИЕ Сгенерировано автоматом из файла %MODULE%.yml
|
||||
// ВНИМАНИЕ Не менять руками!
|
||||
|
||||
%IMPORTS%
|
||||
|
||||
// MARK: - Context
|
||||
|
||||
public protocol %MODULE%Context {
|
||||
%CONTEXT_FIELDS%
|
||||
}
|
||||
|
||||
// MARK: - Controller
|
||||
|
||||
extension %MODULE% {
|
||||
final class Controller: MPAK.Controller<%MODULE%.Model> {
|
||||
init() {
|
||||
super.init(
|
||||
%MODULE%.Model(),
|
||||
debugClassName: "%MODULE_SHORT%Ctrl",
|
||||
debugLog: { aelog($0) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
%CORE%
|
||||
|
||||
// MARK: - Model
|
||||
|
||||
public struct Model: %MODULE%Context {
|
||||
%MODEL_FIELDS%
|
||||
}
|
||||
|
||||
// MARK: - Service
|
||||
|
||||
public final class Service {
|
||||
let ctrl = Controller()
|
||||
let world: World
|
||||
%SERVICE_CORE%
|
||||
var subscriptions = [AnyCancellable]()
|
||||
static private(set) weak var singleton: Service?
|
||||
|
||||
public init(_ world: World) {
|
||||
self.world = world
|
||||
Self.singleton = self
|
||||
%SERVICE_SECTIONS%
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - World
|
||||
|
||||
public struct World {
|
||||
%WORLD_FIELDS%
|
||||
|
||||
public init(
|
||||
%WORLD_PARAMETERS%
|
||||
) {
|
||||
%WORLD_CONSTRUCTOR%
|
||||
}
|
||||
}
|
||||
|
||||
enum SectionGenerated {
|
||||
|
||||
%CORE_SECTION_GENERATED%
|
||||
|
||||
%SERVICE_SECTION_GENERATED%
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
public var %NAME%: %TYPE% = %DEFAULT%
|
||||
public var %NAME%: MPAK.Recent<%TYPE%> = .init(%DEFAULT%)
|
||||
@@ -1,8 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { shouldLoad%NAME%($0) }
|
||||
.flatMap { v -> AnyPublisher<Net.Result%NAME%, Never> in
|
||||
world.net().%REQUEST%(v).eraseToAnyPublisher()
|
||||
}
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { v in world.result%NAME%.send(v) }
|
||||
.store(in: &%SUB%.subscriptions)
|
||||
@@ -1,13 +0,0 @@
|
||||
ctrl.%PIPE%(
|
||||
dbg: "%SHORT_SRC%",
|
||||
sub: %SUB%,
|
||||
%SRC%.eraseToAnyPublisher(),
|
||||
{
|
||||
$0.%NAME%.value = $1
|
||||
$0.%NAME%.isRecent = true
|
||||
},
|
||||
{
|
||||
$0.%NAME%.isRecent = false
|
||||
$0.%EX_NAME% = $1
|
||||
}
|
||||
)
|
||||
@@ -1,13 +0,0 @@
|
||||
ctrl.%PIPE%(
|
||||
dbg: "%SHORT_SRC%",
|
||||
sub: %SUB%,
|
||||
%SRC%.eraseToAnyPublisher(),
|
||||
{
|
||||
$0.%NAME%.value = $1
|
||||
$0.%NAME%.isRecent = true
|
||||
},
|
||||
{ m, _ in m.%NAME%.isRecent = false }
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
ctrl.%PIPE%(
|
||||
dbg: "%SHORT_SRC%",
|
||||
sub: %SUB%,
|
||||
%SRC%.eraseToAnyPublisher(),
|
||||
{ $0.%NAME% = $1 }
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
Bus.events.compactMap { Bus.convertKeyValue(%BUS_KEY%, $0) }.map { (k: String, v: %BUS_VALUE_TYPE%) in v }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
ctrl.%PIPE%(
|
||||
dbg: "%SHORT_SRC%",
|
||||
sub: %SUB%,
|
||||
%SRC%.eraseToAnyPublisher(),
|
||||
{ $0.%NAME% = true },
|
||||
{ $0.%NAME% = false }
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
ctrl.%PIPE%(
|
||||
dbg: "%SHORT_SRC%",
|
||||
sub: %SUB%,
|
||||
%SRC%.eraseToAnyPublisher(),
|
||||
{ $0.%NAME% = $1 },
|
||||
{ m, _ in m.%NAME% = nil }
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
var core: Core?
|
||||
@@ -1,2 +0,0 @@
|
||||
Section%NAME%.setupService(ctrl, self, world)
|
||||
SectionGenerated.setupPlatform(ctrl, self, world)
|
||||
@@ -1,15 +0,0 @@
|
||||
// MARK: - SectionGenerated Service
|
||||
|
||||
static func setupPlatform(
|
||||
_ ctrl: Controller,
|
||||
_ service: Service,
|
||||
_ world: World
|
||||
) {
|
||||
// MARK: - SectionGenerated Service Actions
|
||||
|
||||
%SERVICE_SECTION_GENERATED_ACTIONS%
|
||||
|
||||
// MARK: - SectionGenerated Service Pipes
|
||||
|
||||
%SERVICE_SECTION_GENERATED_PIPES%
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { %SHOULD%($0) }
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { v in %SINK% }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,3 +0,0 @@
|
||||
ctrl.m
|
||||
.sink { v in Bus.send("%MODULE%", v) }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,4 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { %SHOULD%($0) }
|
||||
.sink { v in %SINK% }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,5 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { %SHOULD%($0) }
|
||||
.delay(for: .seconds(0.3), scheduler: RunLoop.main)
|
||||
.sink { v in %SINK% }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,3 +0,0 @@
|
||||
ctrl.m
|
||||
.sink { v in world.model.send(v); modelRelay.send(v) }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,4 +0,0 @@
|
||||
modelRelay
|
||||
.compactMap { %SHOULD%($0) }
|
||||
.sink { v in %SINK% }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,7 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { shouldResetCore($0) }
|
||||
.sink { v in
|
||||
service.core = v ? Core(ctrl, world) : nil
|
||||
world.isCoreRunning.send(v)
|
||||
}
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1,4 +0,0 @@
|
||||
ctrl.m
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { v in world.model.send(v) }
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1 +0,0 @@
|
||||
let modelRelay = PassthroughSubject<Model, Never>()
|
||||
@@ -1,8 +0,0 @@
|
||||
ctrl.m
|
||||
.compactMap { shouldResetCore($0) }
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { v in
|
||||
service.core = v ? Core(ctrl, world) : nil
|
||||
world.isCoreRunning.send(v)
|
||||
}
|
||||
.store(in: &service.subscriptions)
|
||||
@@ -1 +0,0 @@
|
||||
self.%NAME% = %NAME%
|
||||
@@ -1,7 +0,0 @@
|
||||
let %NAME%: %TYPE%
|
||||
let %NAME% = CurrentValueSubject<%TYPE%, Never>(%DEFAULT%)
|
||||
let %NAME%: AnyPublisher<%TYPE%, Never>
|
||||
let model: PassthroughSubject<%MODULE%.Model, Never>
|
||||
let net: () -> Net.Publisher
|
||||
let %NAME% = PassthroughSubject<%TYPE%, Never>()
|
||||
var %NAME%: %TYPE% = %DEFAULT%
|
||||
@@ -1,5 +0,0 @@
|
||||
_ %NAME%: %TYPE%
|
||||
_ %NAME%: @escaping %TYPE%
|
||||
_ %NAME%: AnyPublisher<%TYPE%, Never>
|
||||
_ model: PassthroughSubject<%MODULE%.Model, Never>
|
||||
_ net: @escaping () -> Net.Publisher
|
||||
@@ -1,15 +0,0 @@
|
||||
class Mode:
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def parseLine(self, line):
|
||||
if line.startswith("src:"):
|
||||
self.reset()
|
||||
self.isSource = True
|
||||
elif line.startswith("replace:"):
|
||||
self.reset()
|
||||
self.isReplacement = True
|
||||
|
||||
def reset(self):
|
||||
self.isSource = False
|
||||
self.isReplacement = False
|
||||
@@ -1,12 +0,0 @@
|
||||
from readKeyValue import *
|
||||
|
||||
class Replace:
|
||||
def __init__(self):
|
||||
self.items = { }
|
||||
|
||||
def parseLine(self, line):
|
||||
kv = readKeyValue(line)
|
||||
# Игнорируем всё, что не является ключом со значением.
|
||||
if kv is None:
|
||||
return
|
||||
self.items[kv[0]] = kv[1]
|
||||
@@ -1,5 +0,0 @@
|
||||
class Result:
|
||||
def __init__(self, structure):
|
||||
self.structure = structure
|
||||
|
||||
self.file = ""
|
||||
@@ -1,13 +0,0 @@
|
||||
from readKeyValue import *
|
||||
|
||||
class Source:
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
|
||||
def parseLine(self, line):
|
||||
kv = readKeyValue(line)
|
||||
# Игнорируем всё, что не является ключом со значением.
|
||||
if kv is None:
|
||||
return
|
||||
|
||||
self.name = kv[1]
|
||||
@@ -1,8 +0,0 @@
|
||||
from Replace import *
|
||||
from Source import *
|
||||
|
||||
class Structure:
|
||||
def __init__(self):
|
||||
self.orig = ""
|
||||
self.replace = Replace()
|
||||
self.src = Source()
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
from argparse import ArgumentParser
|
||||
from generateStructure import *
|
||||
from parseLines import *
|
||||
from readModuleSrc import *
|
||||
from Structure import *
|
||||
|
||||
DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
|
||||
# Импорт из общей для всех генераторов директории.
|
||||
sys.path.append(f"{DIR}/../common")
|
||||
from modulePaths import *
|
||||
from readFile import *
|
||||
|
||||
parser = ArgumentParser(prog='generate v3')
|
||||
parser.add_argument('module', type=str,
|
||||
help='the name of the module to generate')
|
||||
parser.add_argument('-i', '--input', type=str,
|
||||
help='The path and name of the input file')
|
||||
parser.add_argument('-o', '--output', type=str,
|
||||
help='The path of the output files')
|
||||
parser.add_argument('-s', '--source', type=str,
|
||||
help='The path of the source files')
|
||||
|
||||
args = parser.parse_args()
|
||||
(PATH, MODULE) = modulePaths(args.module)
|
||||
|
||||
print(f"Generating platform for module '{PATH}'...")
|
||||
|
||||
FILE_IN = args.input or f"{DIR}/../../../Modules/{PATH}/{MODULE}.yml"
|
||||
DIR_OUT = args.output or f"{DIR}/../../../Modules/{PATH}/src/"
|
||||
FILE_OUT = os.path.join(DIR_OUT, f"{MODULE}.Generated.swift")
|
||||
|
||||
# Читаем файл и разбираем его на ключи-значения.
|
||||
lines = readFile(FILE_IN)
|
||||
structure = Structure()
|
||||
parseLines(lines, structure)
|
||||
ORIG_SRC = args.source or f"{DIR}/../../../Modules/{structure.src.name}/src"
|
||||
structure.orig = readModuleSrc(ORIG_SRC, readFile)
|
||||
|
||||
# Генерируем код.
|
||||
output = generateStructure(structure)
|
||||
|
||||
# Сохраняем файл.
|
||||
with open(FILE_OUT, "w") as file:
|
||||
file.write(output)
|
||||
@@ -1,9 +0,0 @@
|
||||
def generateStructure(structure):
|
||||
output = f"""// ВНИМАНИЕ Сгенерировано автоматом из файла {structure.src.name}.yml
|
||||
// ВНИМАНИЕ Не менять руками!
|
||||
"""
|
||||
output += structure.orig
|
||||
for key in structure.replace.items:
|
||||
value = structure.replace.items[key]
|
||||
output = output.replace(key, value)
|
||||
return output
|
||||
@@ -1,23 +0,0 @@
|
||||
from Mode import *
|
||||
|
||||
def parseLines(lines, structure):
|
||||
mode = Mode()
|
||||
for line in lines:
|
||||
# Определяем режим строки.
|
||||
mode.parseLine(line)
|
||||
ln = line.strip()
|
||||
|
||||
# Игнорируем пустую строку.
|
||||
if len(ln) == 0:
|
||||
continue
|
||||
|
||||
# Игнорируем комментарий.
|
||||
if ln.startswith("#"):
|
||||
continue
|
||||
|
||||
# replace.
|
||||
if mode.isReplacement:
|
||||
structure.replace.parseLine(ln)
|
||||
# src.
|
||||
elif mode.isSource:
|
||||
structure.src.parseLine(ln)
|
||||
@@ -1,8 +0,0 @@
|
||||
def readKeyValue(line):
|
||||
parts = line.split(": ")
|
||||
# Ключ со значением.
|
||||
if len(parts) == 2:
|
||||
return (parts[0], parts[1])
|
||||
|
||||
# Не является ключом со значением.
|
||||
return None
|
||||
@@ -1,12 +0,0 @@
|
||||
import os
|
||||
|
||||
def readModuleSrc(dir, readFile):
|
||||
fileNames = os.listdir(dir)
|
||||
contents = []
|
||||
|
||||
for fileName in sorted(fileNames):
|
||||
path = dir + "/" + fileName
|
||||
lines = readFile(path)
|
||||
contents.extend(lines)
|
||||
|
||||
return "\n".join(contents)
|
||||
@@ -1,13 +0,0 @@
|
||||
from readKeyValue import *
|
||||
|
||||
class FeatureToggle:
|
||||
def __init__(self):
|
||||
self.link = None
|
||||
|
||||
def parseLine(self, line):
|
||||
kv = readKeyValue(line)
|
||||
# Игнорируем всё, что не является ключом со значением.
|
||||
if kv is None:
|
||||
return
|
||||
|
||||
self.link = kv[1]
|
||||
@@ -1,11 +0,0 @@
|
||||
class Mode:
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def parseLine(self, line):
|
||||
if line.startswith("featureToggle:"):
|
||||
self.reset()
|
||||
self.isFeatureToggle = True
|
||||
|
||||
def reset(self):
|
||||
self.isFeatureToggle = False
|
||||
@@ -1,7 +0,0 @@
|
||||
from FeatureToggle import *
|
||||
|
||||
class Structure:
|
||||
def __init__(self):
|
||||
self.dir = ""
|
||||
self.module = ""
|
||||
self.featureToggle = FeatureToggle()
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
from Structure import *
|
||||
from generateFeatureToggle import *
|
||||
from parseLines import *
|
||||
|
||||
DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
MODULE = sys.argv[1]
|
||||
|
||||
# Импорт из общей для всех генераторов директории.
|
||||
sys.path.append(f"{DIR}/../common")
|
||||
from readFile import *
|
||||
|
||||
print(f"Генерируем сборный модуль '{MODULE}'...")
|
||||
|
||||
MODULE_DIR = f"{DIR}/../../../Modules/{MODULE}"
|
||||
FILE_IN = f"{MODULE_DIR}/{MODULE}.yml"
|
||||
|
||||
# Читаем файл и разбираем его на ключи-значения.
|
||||
lines = readFile(FILE_IN)
|
||||
structure = Structure()
|
||||
parseLines(lines, structure)
|
||||
structure.moduleDir = MODULE_DIR
|
||||
structure.module = MODULE
|
||||
|
||||
# Генерируем модуль FeatureToggle.
|
||||
generateFeatureToggle(structure)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user