1- use std, files, http, json, functional, downloader, robot, java
1+ use std
22
3- PB = newClass("java.lang.ProcessBuilder")
3+ include "own/Registry.own"
4+ include "own/Config.own"
5+ include "own/Packages.own"
6+ include "own/Projects.own"
7+ include "own/Own.own"
48
5- DEBUG=getenv("OWN_DEBUG", false) == "true"
9+ DEBUG=getenv("OWN_DEBUG", " false" ) == "true"
610logger = {}
711logger.color = def(num = 0) = sprintf("%s[0%sm", toChar(27), num > 0 ? (";" + num) : "")
812logger.error = def(a = "", b = "", c = "") = echo(logger.color(31) + a, b, c + logger.color())
@@ -13,380 +17,6 @@ logger.debug = match(DEBUG) {
1317 case _: def(a = "", b = "", c = "") = 0
1418}
1519
16- class Registry {
17- GLOBAL_URL = "https://raw.githubusercontent.com/aNNiMON/Own-Programming-Language-Tutorial/registry/registry.json"
18-
19- def Registry(ctx) {
20- this.ctx = ctx
21- this.registry = {}
22- this.registryPackages = {}
23- this.registryCacheFile = ctx.TMPDIR + "/ownlang-registry.json"
24- }
25-
26- def isRegistryLoaded() = length(this.registry) > 0
27-
28- def getPackages() {
29- this.ctx.logger.debug("Registry.getPackages")
30- if !this.isRegistryLoaded() {
31- this.loadRegistry()
32- }
33- return this.registryPackages
34- }
35-
36- def loadRegistry() {
37- this.ctx.logger.debug("Registry.loadRegistry")
38- if (!this.loadFromCache()) {
39- this.loadFromHttp()
40- }
41- }
42-
43- def loadFromCache() {
44- this.ctx.logger.debug("Registry.loadFromCache")
45- if exists(this.registryCacheFile) {
46- f = fopen(this.registryCacheFile, "r")
47- this.registry = jsondecode(readText(f))
48- this.registryPackages = this.registry.packages ?? {}
49- fclose(f)
50- return true
51- }
52- return false
53- }
54-
55- def loadFromHttp() {
56- this.ctx.logger.debug("Registry.loadFromHttp")
57- extract(ok, content) = httpSync(this.GLOBAL_URL)
58- this.registry = ok ? jsondecode(content) : {}
59- this.registryPackages = this.registry.packages ?? {}
60- if (ok) this.updateCache()
61- return ok
62- }
63-
64- def updateCache() {
65- this.ctx.logger.debug("Registry.updateCache")
66- f = fopen(this.registryCacheFile, "w")
67- writeText(f, jsonencode(this.registry, 0))
68- flush(f)
69- fclose(f)
70- }
71- }
72-
73- class Config {
74- def Config(ctx) {
75- this.ctx = ctx
76- this.configName = "own-package.json"
77- this.config = {}
78- }
79-
80- def createNewConfig() = {"dependencies": {}}
81-
82- def getConfig() {
83- this.ctx.logger.debug("Config.getConfig")
84- if length(this.config) == 0 {
85- f = fopen(this.configName, "i")
86- this.config = exists(f)
87- ? this.loadConfig()
88- : this.createNewConfig()
89- fclose(f)
90- }
91- return this.config
92- }
93-
94- def loadConfig() {
95- this.ctx.logger.debug("Config.loadConfig")
96- f = fopen(this.configName, "r")
97- config = jsondecode(readText(f))
98- fclose(f)
99- return config
100- }
101-
102- def saveConfig(config) {
103- this.ctx.logger.debug("Config.saveConfig")
104- f = fopen(this.configName, "w")
105- writeText(f, jsonencode(config, 2))
106- flush(f)
107- fclose(f)
108- }
109-
110- def mergeConfig(config) {
111- this.ctx.logger.debug("Config.mergeConfig")
112- mapping = {
113- "name": def() = this.getDefaultProjectName(),
114- "version": def() = this.getDefaultVersion(),
115- "author": def() = this.getDefaultAuthor(),
116- "program": def() = this.getDefaultProgram(),
117- "scripts": def() = {
118- "default": ["$ownlang$", "-f", "$program$"]
119- },
120- "dependencies": def() = {},
121- }
122- for property, defaultValue : mapping {
123- if !arrayKeyExists(property, config)
124- config[property] = defaultValue()
125- }
126- return config
127- }
128-
129- def getDefaultProjectName() {
130- this.ctx.logger.debug("Config.getDefaultProjectName")
131- parts = getprop("user.dir").replace("\\", "/").split("/")
132- name = parts[parts.length - 1]
133- .toLowerCase()
134- .replace("\\s+", "_")
135- .replaceAll("[^a-z\\-_]", "")
136- return match(length(name)) {
137- case 0: "project"
138- case x if x > 20: substring(name, 0, 20)
139- case _: name
140- }
141- }
142-
143- def getDefaultVersion() = "1.0.0"
144- def getDefaultAuthor() = getprop("user.name", "unknown")
145- def getDefaultProgram() = "main.own"
146- }
147-
148- class Packages {
149- def Packages(ctx) {
150- this.ctx = ctx
151- this.packagesDir = "own-modules"
152- this.usageFile = ".usage.own"
153- }
154-
155- def addDependencies(packages) {
156- this.ctx.logger.debug("Packages.addDependencies", packages)
157- if packages.isEmpty() {
158- this.ctx.logger.error("No package names provided. Try: own add telegram-bot")
159- return false
160- }
161- res = true
162- config = this.ctx.config.getConfig()
163- for package : packages {
164- res = res && this.addDependency(package, config)
165- }
166- return res
167- }
168-
169- def addDependency(packageName, config) {
170- this.ctx.logger.debug("Packages.addDependency", packageName)
171- if packageName == "" {
172- this.ctx.logger.error("No package name provided. Try: own add telegram-bot")
173- return false
174- }
175- // Check in global registry
176- registryPackages = this.ctx.registry.getPackages()
177- if !arrayKeyExists(packageName, registryPackages) {
178- this.ctx.logger.error("Unknown package " + packageName + ". Check if the name specified correctly")
179- return false
180- }
181- dep = registryPackages[packageName]
182- // Check if exists in config
183- if arrayKeyExists(packageName, config.dependencies ?? {}) {
184- // Exists, check version match
185- if dep.version == config.dependencies[packageName] {
186- this.ctx.logger.info("Package " + packageName + " is up to date")
187- return true
188- }
189- }
190- // Install this dependency
191- dep.name = packageName
192- config.dependencies[packageName] = dep.version
193- downloaded = this.ctx.packages.downloadPackage(dep)
194- logText = "Installing " + packageName + " " + dep.version
195- if downloaded {
196- this.ctx.logger.success(logText + " [success]")
197- } else {
198- this.ctx.logger.error(logText + " [failure]")
199- }
200- return downloaded
201- }
202-
203- def downloadDependencies(dependencies) {
204- this.ctx.logger.debug("Packages.downloadDependencies")
205- if length(dependencies) == 0 {
206- this.ctx.logger.error("No dependency packages provided, or own-package.json is missing")
207- return false
208- }
209- registryPackages = this.ctx.registry.getPackages()
210- for name, version : dependencies {
211- logText = "Installing " + name + " " + version
212- dep = registryPackages[name] ?? {"downloaded": 0}
213- if !(dep.downloaded ?? 0) {
214- dep.name = name
215- dep.downloaded = this.ctx.packages.downloadPackage(dep)
216- }
217- if dep.downloaded {
218- this.ctx.logger.success(logText + " [success]")
219- } else {
220- this.ctx.logger.error(logText + " [failure]")
221- }
222- }
223- return true
224- }
225-
226- def getPackageFiles(dep) = match (dep.type ?? 0) {
227- case "gist": {
228- this.ctx.logger.debug("Packages.getPackageFiles for gist")
229- extract(ok, content) = httpSync("https://api.github.com/gists/" + dep.id)
230- return match ok {
231- case false: []
232- case true: stream(jsondecode(content).files)
233- .map(def(r) = [r[0], r[1].raw_url])
234- .toArray()
235- }
236- }
237- case _: []
238- }
239-
240- def getPackagePath(package) {
241- this.ctx.logger.debug("Packages.getPackagePath", package)
242- dir = this.packagesDir + "/" + package
243- stream([this.packagesDir, dir])
244- .filterNot(::exists)
245- .forEach(::mkdir)
246- return dir
247- }
248-
249- def downloadPackage(dep) {
250- this.ctx.logger.debug("Packages.downloadPackage", dep.name)
251- files = this.getPackageFiles(dep)
252- dir = this.getPackagePath(dep.name)
253- result = 0
254- for mf : files {
255- extract(name, url) = mf
256- result += downloader(url, dir + "/" + name)
257- }
258- return result > 0
259- }
260-
261- def showUsages(packages) {
262- this.ctx.logger.debug("Packages.showUsages", packages)
263- if packages.isEmpty() {
264- this.ctx.logger.error("No package names provided")
265- return false
266- }
267- config = this.ctx.config.getConfig()
268- for package : packages {
269- this.showUsage(package, config)
270- }
271- return true
272- }
273-
274- def showUsage(package, config) {
275- file = this.packagesDir + "/" + package + "/" + this.usageFile
276- exists = arrayKeyExists(package, config.dependencies ?? {})
277- && exists(file)
278- if !exists {
279- this.ctx.logger.error("[" + package + "] "
280- + "Error: no .usage.own file provided in the package,"
281- + " or the package is not installed")
282- return false
283- }
284- this.ctx.logger.success("\n[" + package + "]")
285- f = fopen(file, "r")
286- this.ctx.logger.info(readText(f))
287- fclose(f)
288- return true
289- }
290- }
291-
292- class Projects {
293- def Projects(ctx) {
294- this.ctx = ctx
295- this.defaultPrograms = {
296- "main.own" : "use std\n\necho(\"Hello, world!\", ARGS)"
297- }
298- }
299-
300- def savePrograms() {
301- this.ctx.logger.debug("Projects.savePrograms")
302- for name, content : this.defaultPrograms {
303- if exists(name) continue
304- f = fopen(name, "w")
305- writeText(f, content)
306- flush(f)
307- fclose(f)
308- }
309- }
310- }
311-
312- class Own {
313- def Own(ctx) {
314- this.ctx = ctx
315- }
316-
317- def init() {
318- this.ctx.logger.debug("Own.init")
319- config = this.ctx.config.getConfig()
320- config = this.ctx.config.mergeConfig(config)
321- this.ctx.config.saveConfig(config)
322- this.ctx.projects.savePrograms()
323- this.ctx.logger.success("Project created")
324- }
325-
326- def install() {
327- this.ctx.logger.debug("Own.install")
328- config = this.ctx.config.getConfig()
329- this.ctx.packages.downloadDependencies(config.dependencies ?? {})
330- }
331-
332- def addDependencies() {
333- packages = this.nonEmptyArgs()
334- this.ctx.logger.debug("Own.addDependencies", packages)
335- this.ctx.packages.addDependencies(packages)
336- this.ctx.config.saveConfig(this.ctx.config.getConfig())
337- }
338-
339- def showUsages() {
340- packages = this.nonEmptyArgs()
341- this.ctx.logger.debug("Own.showUsages", packages)
342- this.ctx.packages.showUsages(packages)
343- }
344-
345- def runScript(script) {
346- this.ctx.logger.debug("Own.runScript", script)
347- config = this.ctx.config.getConfig()
348- vars = {
349- "$ownlang$": "ownlang" + (this.ctx.OSNAME.startsWith("win") ? ".cmd" : ""),
350- "$program$": config.program ?? "",
351- "$dir$": getprop("user.dir").replace("\\", "/")
352- }
353- if (script == "") script = "default"
354- if arrayKeyExists(script, config.scripts ?? {}) {
355- commands = []
356- for c : config.scripts[script] {
357- commands += arrayKeyExists(c, vars) ? vars[c] : c
358- }
359- this.ctx.logger.debug("Own.runScript run:", commands)
360- pb = new PB(commands)
361- pb.inheritIO()
362- p = pb.start()
363- } else {
364- this.ctx.logger.error("No script '%s' found in config's scripts section".sprintf(script))
365- }
366- }
367-
368- def usage() {
369- this.ctx.logger.info("own package manager v1.0.0")
370- this.ctx.logger.info("")
371- this.ctx.logger.info("
372- |Usage: own [options]
373- | options:
374- | init initialize new project
375- | add [package] add new package dependency to own-package.json
376- | usage [package] display the package usage (from .usage.own) if exists
377- | install install packages defined in own-package.json
378- | run run program defined in the config program section
379- | run [script] run script defined in the config scripts section
380- ".stripMargin())
381- }
382-
383- def nonEmptyArgs(skip = 1) = stream(ARGS)
384- .skip(skip)
385- .filter(def(s) = !s.isEmpty())
386- .toArray()
387- }
388-
389-
39020ctx = {}
39121ctx.DEBUG = DEBUG
39222ctx.TMPDIR = getenv("TEMP", getenv("TMP", "."))
0 commit comments