freelang-v4 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/README.md +548 -0
  2. package/dist/ast.d.ts +367 -0
  3. package/dist/ast.js +4 -0
  4. package/dist/ast.js.map +1 -0
  5. package/dist/async-basic.test.d.ts +1 -0
  6. package/dist/async-basic.test.js +88 -0
  7. package/dist/async-basic.test.js.map +1 -0
  8. package/dist/async-jest.test.d.ts +1 -0
  9. package/dist/async-jest.test.js +99 -0
  10. package/dist/async-jest.test.js.map +1 -0
  11. package/dist/channel-jest.test.d.ts +1 -0
  12. package/dist/channel-jest.test.js +148 -0
  13. package/dist/channel-jest.test.js.map +1 -0
  14. package/dist/checker-jest.test.d.ts +1 -0
  15. package/dist/checker-jest.test.js +160 -0
  16. package/dist/checker-jest.test.js.map +1 -0
  17. package/dist/checker.d.ts +149 -0
  18. package/dist/checker.js +1565 -0
  19. package/dist/checker.js.map +1 -0
  20. package/dist/checker.test.d.ts +1 -0
  21. package/dist/checker.test.js +217 -0
  22. package/dist/checker.test.js.map +1 -0
  23. package/dist/compiler-jest.test.d.ts +1 -0
  24. package/dist/compiler-jest.test.js +233 -0
  25. package/dist/compiler-jest.test.js.map +1 -0
  26. package/dist/compiler.d.ts +127 -0
  27. package/dist/compiler.js +1588 -0
  28. package/dist/compiler.js.map +1 -0
  29. package/dist/compiler.test.d.ts +1 -0
  30. package/dist/compiler.test.js +313 -0
  31. package/dist/compiler.test.js.map +1 -0
  32. package/dist/db-100m-full.d.ts +5 -0
  33. package/dist/db-100m-full.js +78 -0
  34. package/dist/db-100m-full.js.map +1 -0
  35. package/dist/db-100m-no-index.d.ts +12 -0
  36. package/dist/db-100m-no-index.js +119 -0
  37. package/dist/db-100m-no-index.js.map +1 -0
  38. package/dist/db-100m-real.d.ts +5 -0
  39. package/dist/db-100m-real.js +131 -0
  40. package/dist/db-100m-real.js.map +1 -0
  41. package/dist/db-100m-streaming.d.ts +15 -0
  42. package/dist/db-100m-streaming.js +164 -0
  43. package/dist/db-100m-streaming.js.map +1 -0
  44. package/dist/db-100m-test.d.ts +5 -0
  45. package/dist/db-100m-test.js +111 -0
  46. package/dist/db-100m-test.js.map +1 -0
  47. package/dist/db-jest.test.d.ts +1 -0
  48. package/dist/db-jest.test.js +182 -0
  49. package/dist/db-jest.test.js.map +1 -0
  50. package/dist/db-runtime.d.ts +24 -0
  51. package/dist/db-runtime.js +204 -0
  52. package/dist/db-runtime.js.map +1 -0
  53. package/dist/db.d.ts +249 -0
  54. package/dist/db.js +593 -0
  55. package/dist/db.js.map +1 -0
  56. package/dist/file-io-jest.test.d.ts +1 -0
  57. package/dist/file-io-jest.test.js +225 -0
  58. package/dist/file-io-jest.test.js.map +1 -0
  59. package/dist/for-of-jest.test.d.ts +1 -0
  60. package/dist/for-of-jest.test.js +230 -0
  61. package/dist/for-of-jest.test.js.map +1 -0
  62. package/dist/for-of.test.d.ts +1 -0
  63. package/dist/for-of.test.js +305 -0
  64. package/dist/for-of.test.js.map +1 -0
  65. package/dist/function-literal-jest.test.d.ts +1 -0
  66. package/dist/function-literal-jest.test.js +180 -0
  67. package/dist/function-literal-jest.test.js.map +1 -0
  68. package/dist/function-literal.test.d.ts +1 -0
  69. package/dist/function-literal.test.js +245 -0
  70. package/dist/function-literal.test.js.map +1 -0
  71. package/dist/generics-jest.test.d.ts +1 -0
  72. package/dist/generics-jest.test.js +93 -0
  73. package/dist/generics-jest.test.js.map +1 -0
  74. package/dist/ir-gen.d.ts +15 -0
  75. package/dist/ir-gen.js +400 -0
  76. package/dist/ir-gen.js.map +1 -0
  77. package/dist/ir.d.ts +114 -0
  78. package/dist/ir.js +5 -0
  79. package/dist/ir.js.map +1 -0
  80. package/dist/lexer.d.ts +110 -0
  81. package/dist/lexer.js +467 -0
  82. package/dist/lexer.js.map +1 -0
  83. package/dist/lexer.test.d.ts +1 -0
  84. package/dist/lexer.test.js +426 -0
  85. package/dist/lexer.test.js.map +1 -0
  86. package/dist/main.d.ts +2 -0
  87. package/dist/main.js +241 -0
  88. package/dist/main.js.map +1 -0
  89. package/dist/module-jest.test.d.ts +1 -0
  90. package/dist/module-jest.test.js +123 -0
  91. package/dist/module-jest.test.js.map +1 -0
  92. package/dist/parser.d.ts +56 -0
  93. package/dist/parser.js +1060 -0
  94. package/dist/parser.js.map +1 -0
  95. package/dist/parser.test.d.ts +1 -0
  96. package/dist/parser.test.js +461 -0
  97. package/dist/parser.test.js.map +1 -0
  98. package/dist/pattern-matching-jest.test.d.ts +1 -0
  99. package/dist/pattern-matching-jest.test.js +158 -0
  100. package/dist/pattern-matching-jest.test.js.map +1 -0
  101. package/dist/pkg/init.d.ts +1 -0
  102. package/dist/pkg/init.js +118 -0
  103. package/dist/pkg/init.js.map +1 -0
  104. package/dist/pkg/install.d.ts +1 -0
  105. package/dist/pkg/install.js +77 -0
  106. package/dist/pkg/install.js.map +1 -0
  107. package/dist/pkg/registry.d.ts +23 -0
  108. package/dist/pkg/registry.js +106 -0
  109. package/dist/pkg/registry.js.map +1 -0
  110. package/dist/pkg/run.d.ts +1 -0
  111. package/dist/pkg/run.js +76 -0
  112. package/dist/pkg/run.js.map +1 -0
  113. package/dist/pkg/toml.d.ts +5 -0
  114. package/dist/pkg/toml.js +117 -0
  115. package/dist/pkg/toml.js.map +1 -0
  116. package/dist/repl.d.ts +15 -0
  117. package/dist/repl.js +197 -0
  118. package/dist/repl.js.map +1 -0
  119. package/dist/runtime/bytecode.d.ts +92 -0
  120. package/dist/runtime/bytecode.js +253 -0
  121. package/dist/runtime/bytecode.js.map +1 -0
  122. package/dist/runtime/value.d.ts +102 -0
  123. package/dist/runtime/value.js +302 -0
  124. package/dist/runtime/value.js.map +1 -0
  125. package/dist/runtime/vm.d.ts +65 -0
  126. package/dist/runtime/vm.js +293 -0
  127. package/dist/runtime/vm.js.map +1 -0
  128. package/dist/struct-instance-jest.test.d.ts +1 -0
  129. package/dist/struct-instance-jest.test.js +209 -0
  130. package/dist/struct-instance-jest.test.js.map +1 -0
  131. package/dist/struct-instance.test.d.ts +1 -0
  132. package/dist/struct-instance.test.js +291 -0
  133. package/dist/struct-instance.test.js.map +1 -0
  134. package/dist/struct-jest.test.d.ts +1 -0
  135. package/dist/struct-jest.test.js +176 -0
  136. package/dist/struct-jest.test.js.map +1 -0
  137. package/dist/struct.test.d.ts +1 -0
  138. package/dist/struct.test.js +231 -0
  139. package/dist/struct.test.js.map +1 -0
  140. package/dist/trait-jest.test.d.ts +1 -0
  141. package/dist/trait-jest.test.js +120 -0
  142. package/dist/trait-jest.test.js.map +1 -0
  143. package/dist/vm-jest.test.d.ts +1 -0
  144. package/dist/vm-jest.test.js +569 -0
  145. package/dist/vm-jest.test.js.map +1 -0
  146. package/dist/vm.d.ts +81 -0
  147. package/dist/vm.js +1956 -0
  148. package/dist/vm.js.map +1 -0
  149. package/dist/vm.test.d.ts +1 -0
  150. package/dist/vm.test.js +337 -0
  151. package/dist/vm.test.js.map +1 -0
  152. package/dist/web-repl/sandbox.d.ts +11 -0
  153. package/dist/web-repl/sandbox.js +76 -0
  154. package/dist/web-repl/sandbox.js.map +1 -0
  155. package/dist/web-repl/server.d.ts +1 -0
  156. package/dist/web-repl/server.js +111 -0
  157. package/dist/web-repl/server.js.map +1 -0
  158. package/dist/while-loop-jest.test.d.ts +1 -0
  159. package/dist/while-loop-jest.test.js +201 -0
  160. package/dist/while-loop-jest.test.js.map +1 -0
  161. package/dist/while-loop.test.d.ts +1 -0
  162. package/dist/while-loop.test.js +262 -0
  163. package/dist/while-loop.test.js.map +1 -0
  164. package/docs/EXPERIENCE.md +787 -0
  165. package/docs/README.md +175 -0
  166. package/docs/V1_V2_V3_ANALYSIS.md +107 -0
  167. package/docs/_config.yml +36 -0
  168. package/docs/api-reference.md +459 -0
  169. package/docs/architecture.md +470 -0
  170. package/docs/benchmarks.md +295 -0
  171. package/docs/comparison.md +454 -0
  172. package/docs/index.md +335 -0
  173. package/docs/language-completeness.md +228 -0
  174. package/docs/learning-guide.md +651 -0
  175. package/package.json +65 -0
  176. package/src/api/deploy_key.fl +294 -0
  177. package/src/api/issue.fl +302 -0
  178. package/src/api/org.fl +356 -0
  179. package/src/api/repo.fl +394 -0
  180. package/src/api/team.fl +299 -0
  181. package/src/api/user.fl +385 -0
  182. package/src/api/webhook.fl +273 -0
  183. package/src/ast.ts +158 -0
  184. package/src/async-basic.test.ts +94 -0
  185. package/src/async-jest.test.ts +107 -0
  186. package/src/channel-jest.test.ts +158 -0
  187. package/src/checker-jest.test.ts +189 -0
  188. package/src/checker.test.ts +279 -0
  189. package/src/checker.ts +1861 -0
  190. package/src/commands/analyze.fl +227 -0
  191. package/src/commands/auth.fl +315 -0
  192. package/src/commands/batch.fl +349 -0
  193. package/src/commands/config.fl +199 -0
  194. package/src/commands/deploy_key.fl +352 -0
  195. package/src/commands/issue.fl +275 -0
  196. package/src/commands/main.fl +492 -0
  197. package/src/commands/org.fl +425 -0
  198. package/src/commands/repo.fl +581 -0
  199. package/src/commands/team.fl +244 -0
  200. package/src/commands/user.fl +423 -0
  201. package/src/commands/webhook.fl +400 -0
  202. package/src/compiler-jest.test.ts +275 -0
  203. package/src/compiler.test.ts +375 -0
  204. package/src/compiler.ts +1770 -0
  205. package/src/config.fl +175 -0
  206. package/src/core/batch.fl +355 -0
  207. package/src/core/cache.fl +284 -0
  208. package/src/core/ensure.fl +324 -0
  209. package/src/db-100m-full.ts +96 -0
  210. package/src/db-100m-no-index.ts +133 -0
  211. package/src/db-100m-real.ts +152 -0
  212. package/src/db-100m-streaming.ts +154 -0
  213. package/src/db-100m-test.ts +136 -0
  214. package/src/db-jest.test.ts +161 -0
  215. package/src/db-runtime.ts +242 -0
  216. package/src/db.ts +676 -0
  217. package/src/errors.fl +134 -0
  218. package/src/for-of-jest.test.ts +246 -0
  219. package/src/for-of.test.ts +308 -0
  220. package/src/function-literal-jest.test.ts +193 -0
  221. package/src/function-literal.test.ts +248 -0
  222. package/src/generics-jest.test.ts +104 -0
  223. package/src/http/client.fl +327 -0
  224. package/src/ir-gen.ts +459 -0
  225. package/src/ir.ts +80 -0
  226. package/src/lexer.test.ts +499 -0
  227. package/src/lexer.ts +522 -0
  228. package/src/main.ts +223 -0
  229. package/src/models.fl +162 -0
  230. package/src/module-jest.test.ts +145 -0
  231. package/src/parser.test.ts +542 -0
  232. package/src/parser.ts +1211 -0
  233. package/src/pattern-matching-jest.test.ts +170 -0
  234. package/src/pkg/init.ts +91 -0
  235. package/src/pkg/install.ts +56 -0
  236. package/src/pkg/registry.ts +103 -0
  237. package/src/pkg/run.ts +49 -0
  238. package/src/pkg/toml.ts +129 -0
  239. package/src/repl.ts +190 -0
  240. package/src/runtime/bytecode.ts +291 -0
  241. package/src/runtime/value.ts +322 -0
  242. package/src/runtime/vm.ts +354 -0
  243. package/src/self-host/bootstrap.fl +68 -0
  244. package/src/self-host/interpreter.fl +361 -0
  245. package/src/self-host/lexer-simple.fl +22 -0
  246. package/src/self-host/lexer.fl +305 -0
  247. package/src/self-host/parser.fl +580 -0
  248. package/src/struct-instance-jest.test.ts +221 -0
  249. package/src/struct-instance.test.ts +293 -0
  250. package/src/struct-jest.test.ts +187 -0
  251. package/src/struct.test.ts +234 -0
  252. package/src/trait-jest.test.ts +136 -0
  253. package/src/vm-jest.test.ts +754 -0
  254. package/src/vm.ts +1976 -0
  255. package/src/web-repl/public/index.html +50 -0
  256. package/src/web-repl/public/main.js +105 -0
  257. package/src/web-repl/public/style.css +225 -0
  258. package/src/web-repl/sandbox.ts +88 -0
  259. package/src/web-repl/server.ts +97 -0
  260. package/src/while-loop-jest.test.ts +218 -0
  261. package/src/while-loop.test.ts +267 -0
@@ -0,0 +1,581 @@
1
+ // FreeLang v4.2 — gogs-cli Phase 5 저장소 명령어
2
+ // create, list, view, delete, ensure 구현
3
+ // 400라인
4
+
5
+ /// 저장소 생성
6
+ /// gogs repo create REPO_NAME [--private] [--description "desc"]
7
+ async fn cmd_repo_create(args: CliArgs) -> CliResult {
8
+ // 1. 인자 검증
9
+ var repo_name = get_positional(args, 0)
10
+ if repo_name == "" {
11
+ return CliResult {
12
+ exit_code: 1,
13
+ output: "",
14
+ error: "Repository name is required"
15
+ }
16
+ }
17
+
18
+ // 2. 플래그 파싱
19
+ var is_private = has_flag(args, "private")
20
+ var description = get_flag(args, "description")
21
+
22
+ // 3. 설정 로드
23
+ var host = await get_default_host()
24
+ if host.url == "" {
25
+ return CliResult {
26
+ exit_code: 1,
27
+ output: "",
28
+ error: "Not logged in. Run: gogs auth login --host URL --token TOKEN"
29
+ }
30
+ }
31
+
32
+ // 4. 클라이언트 및 캐시 생성
33
+ var client = new_client(host.url, host.token)
34
+ var cache = new_cache(300) // 5분 TTL
35
+
36
+ // 5. API 호출
37
+ println("Creating repository '" + repo_name + "'...")
38
+
39
+ var repo_service = new_repo_service(client, cache)
40
+ var req = CreateRepoRequest {
41
+ name: repo_name,
42
+ private: is_private,
43
+ description: description
44
+ }
45
+ var result = await create_repo(repo_service, req)
46
+
47
+ // 6. 결과 처리
48
+ match result {
49
+ Result::Ok(repo) => {
50
+ var output = ""
51
+ output = output + "✓ Repository created\n"
52
+ output = output + " Name: " + repo.full_name + "\n"
53
+ output = output + " URL: " + repo.html_url + "\n"
54
+ output = output + " SSH: " + repo.ssh_url + "\n"
55
+ output = output + " Private: " + bool_to_str(repo.private) + "\n"
56
+
57
+ CliResult {
58
+ exit_code: 0,
59
+ output: output,
60
+ error: ""
61
+ }
62
+ }
63
+ Result::Err(err) => {
64
+ CliResult {
65
+ exit_code: 1,
66
+ output: "",
67
+ error: error_message(err)
68
+ }
69
+ }
70
+ }
71
+ }
72
+
73
+ /// 저장소 목록
74
+ /// gogs repo list [--json] [--yaml]
75
+ async fn cmd_repo_list(args: CliArgs) -> CliResult {
76
+ // 1. 설정 로드
77
+ var host = await get_default_host()
78
+ if host.url == "" {
79
+ return CliResult {
80
+ exit_code: 1,
81
+ output: "",
82
+ error: "Not logged in"
83
+ }
84
+ }
85
+
86
+ // 2. 클라이언트 및 캐시 생성
87
+ var client = new_client(host.url, host.token)
88
+ var cache = new_cache(300)
89
+
90
+ // 3. API 호출
91
+ println("Fetching repositories...")
92
+
93
+ var repo_service = new_repo_service(client, cache)
94
+ var result = await list_repos(repo_service)
95
+
96
+ // 4. 결과 처리
97
+ match result {
98
+ Result::Ok(repos) => {
99
+ var format = get_format(args)
100
+
101
+ if format == "json" {
102
+ return repo_list_json(repos)
103
+ }
104
+ if format == "yaml" {
105
+ return repo_list_yaml(repos)
106
+ }
107
+
108
+ // 기본: 테이블
109
+ repo_list_text(repos)
110
+ }
111
+ Result::Err(err) => {
112
+ CliResult {
113
+ exit_code: 1,
114
+ output: "",
115
+ error: error_message(err)
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ /// 저장소 상세
122
+ /// gogs repo view OWNER/REPO [--json] [--yaml]
123
+ async fn cmd_repo_view(args: CliArgs) -> CliResult {
124
+ // 1. 인자 검증
125
+ var repo_path = get_positional(args, 0)
126
+ if repo_path == "" {
127
+ return CliResult {
128
+ exit_code: 1,
129
+ output: "",
130
+ error: "Repository path (OWNER/REPO) is required"
131
+ }
132
+ }
133
+
134
+ // 파싱: "owner/repo" -> [owner, repo]
135
+ var parts = split_repo_path(repo_path)
136
+ if length(parts) != 2 {
137
+ return CliResult {
138
+ exit_code: 1,
139
+ output: "",
140
+ error: "Invalid repository path. Use: OWNER/REPO"
141
+ }
142
+ }
143
+
144
+ var owner = parts[0]
145
+ var repo_name = parts[1]
146
+
147
+ // 2. 설정 로드
148
+ var host = await get_default_host()
149
+ if host.url == "" {
150
+ return CliResult {
151
+ exit_code: 1,
152
+ output: "",
153
+ error: "Not logged in"
154
+ }
155
+ }
156
+
157
+ // 3. 클라이언트 및 캐시
158
+ var client = new_client(host.url, host.token)
159
+ var cache = new_cache(300)
160
+
161
+ // 4. API 호출
162
+ println("Fetching repository " + repo_path + "...")
163
+
164
+ var repo_service = new_repo_service(client, cache)
165
+ var result = await get_repo(repo_service, owner, repo_name)
166
+
167
+ // 5. 결과 처리
168
+ match result {
169
+ Result::Ok(repo) => {
170
+ var format = get_format(args)
171
+
172
+ if format == "json" {
173
+ return repo_view_json(repo)
174
+ }
175
+ if format == "yaml" {
176
+ return repo_view_yaml(repo)
177
+ }
178
+
179
+ repo_view_text(repo)
180
+ }
181
+ Result::Err(err) => {
182
+ CliResult {
183
+ exit_code: 1,
184
+ output: "",
185
+ error: error_message(err)
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+ /// 저장소 삭제
192
+ /// gogs repo delete OWNER/REPO [--force]
193
+ async fn cmd_repo_delete(args: CliArgs) -> CliResult {
194
+ // 1. 인자 검증
195
+ var repo_path = get_positional(args, 0)
196
+ if repo_path == "" {
197
+ return CliResult {
198
+ exit_code: 1,
199
+ output: "",
200
+ error: "Repository path (OWNER/REPO) is required"
201
+ }
202
+ }
203
+
204
+ var parts = split_repo_path(repo_path)
205
+ if length(parts) != 2 {
206
+ return CliResult {
207
+ exit_code: 1,
208
+ output: "",
209
+ error: "Invalid repository path. Use: OWNER/REPO"
210
+ }
211
+ }
212
+
213
+ var owner = parts[0]
214
+ var repo_name = parts[1]
215
+
216
+ // 2. 플래그 확인
217
+ var force = has_flag(args, "force")
218
+
219
+ if !force {
220
+ println("⚠️ This will permanently delete " + repo_path)
221
+ println("Run with --force to confirm")
222
+ return CliResult {
223
+ exit_code: 1,
224
+ output: "",
225
+ error: "Deletion not confirmed"
226
+ }
227
+ }
228
+
229
+ // 3. 설정 로드
230
+ var host = await get_default_host()
231
+ if host.url == "" {
232
+ return CliResult {
233
+ exit_code: 1,
234
+ output: "",
235
+ error: "Not logged in"
236
+ }
237
+ }
238
+
239
+ // 4. 클라이언트 및 캐시
240
+ var client = new_client(host.url, host.token)
241
+ var cache = new_cache(300)
242
+
243
+ // 5. API 호출
244
+ println("Deleting repository " + repo_path + "...")
245
+
246
+ var repo_service = new_repo_service(client, cache)
247
+ var result = await delete_repo(repo_service, owner, repo_name)
248
+
249
+ // 6. 결과 처리
250
+ match result {
251
+ Result::Ok(_) => {
252
+ CliResult {
253
+ exit_code: 0,
254
+ output: "✓ Repository deleted: " + repo_path,
255
+ error: ""
256
+ }
257
+ }
258
+ Result::Err(err) => {
259
+ CliResult {
260
+ exit_code: 1,
261
+ output: "",
262
+ error: error_message(err)
263
+ }
264
+ }
265
+ }
266
+ }
267
+
268
+ /// 저장소 동기화 (멱등성)
269
+ /// gogs repo ensure REPO_NAME [--private] [--description "desc"]
270
+ async fn cmd_repo_ensure(args: CliArgs) -> CliResult {
271
+ // 1. 인자 검증
272
+ var repo_name = get_positional(args, 0)
273
+ if repo_name == "" {
274
+ return CliResult {
275
+ exit_code: 1,
276
+ output: "",
277
+ error: "Repository name is required"
278
+ }
279
+ }
280
+
281
+ // 2. 플래그 파싱
282
+ var is_private = has_flag(args, "private")
283
+ var description = get_flag(args, "description")
284
+
285
+ // 3. 설정 로드
286
+ var host = await get_default_host()
287
+ if host.url == "" {
288
+ return CliResult {
289
+ exit_code: 1,
290
+ output: "",
291
+ error: "Not logged in"
292
+ }
293
+ }
294
+
295
+ // 4. 클라이언트 및 캐시
296
+ var client = new_client(host.url, host.token)
297
+ var cache = new_cache(300)
298
+
299
+ // 5. 저장소 존재 확인
300
+ println("Checking if repository exists...")
301
+
302
+ var repo_service = new_repo_service(client, cache)
303
+ var check_result = await get_repo(repo_service, host.url, repo_name)
304
+
305
+ match check_result {
306
+ Result::Ok(repo) => {
307
+ // 저장소 존재 - 업데이트
308
+ println("Repository exists, updating...")
309
+
310
+ var req = CreateRepoRequest {
311
+ name: repo_name,
312
+ private: is_private,
313
+ description: description
314
+ }
315
+
316
+ var parts = split_repo_path(repo.full_name)
317
+ var owner = if length(parts) > 0 then parts[0] else ""
318
+
319
+ var update_result = await update_repo(repo_service, owner, repo_name, req)
320
+
321
+ match update_result {
322
+ Result::Ok(updated) => {
323
+ CliResult {
324
+ exit_code: 0,
325
+ output: "✓ Repository ensured (updated)\n " + updated.html_url,
326
+ error: ""
327
+ }
328
+ }
329
+ Result::Err(err) => {
330
+ CliResult {
331
+ exit_code: 1,
332
+ output: "",
333
+ error: error_message(err)
334
+ }
335
+ }
336
+ }
337
+ }
338
+ Result::Err(_) => {
339
+ // 저장소 미존재 - 생성
340
+ println("Repository does not exist, creating...")
341
+
342
+ var req = CreateRepoRequest {
343
+ name: repo_name,
344
+ private: is_private,
345
+ description: description
346
+ }
347
+
348
+ var create_result = await create_repo(repo_service, req)
349
+
350
+ match create_result {
351
+ Result::Ok(created) => {
352
+ CliResult {
353
+ exit_code: 0,
354
+ output: "✓ Repository ensured (created)\n " + created.html_url,
355
+ error: ""
356
+ }
357
+ }
358
+ Result::Err(err) => {
359
+ CliResult {
360
+ exit_code: 1,
361
+ output: "",
362
+ error: error_message(err)
363
+ }
364
+ }
365
+ }
366
+ }
367
+ }
368
+ }
369
+
370
+ // ==================== 헬퍼 함수 ====================
371
+
372
+ /// 저장소 경로 파싱
373
+ fn split_repo_path(path: str) -> [str] {
374
+ var result: [str] = []
375
+
376
+ var slash_idx = indexOf(path, "/")
377
+ if slash_idx == -1 {
378
+ result.push(path)
379
+ return result
380
+ }
381
+
382
+ var owner = substring(path, 0, slash_idx)
383
+ var repo = substring(path, slash_idx + 1, length(path))
384
+
385
+ result.push(owner)
386
+ result.push(repo)
387
+
388
+ result
389
+ }
390
+
391
+ // ==================== 출력 포맷: 목록 ====================
392
+
393
+ /// 저장소 목록 - 텍스트
394
+ fn repo_list_text(repos: [Repo]) -> CliResult {
395
+ var output = ""
396
+ output = output + "╔════════════════════════════════════════════════════════════════════╗\n"
397
+ output = output + "║ Repository List ║\n"
398
+ output = output + "╚════════════════════════════════════════════════════════════════════╝\n"
399
+ output = output + "\n"
400
+
401
+ if length(repos) == 0 {
402
+ output = output + "No repositories found\n"
403
+ return CliResult {
404
+ exit_code: 0,
405
+ output: output,
406
+ error: ""
407
+ }
408
+ }
409
+
410
+ var i: i32 = 0
411
+ while i < length(repos) {
412
+ var repo = repos[i]
413
+ var private_mark = if repo.private then "[P]" else "[O]"
414
+ output = output + private_mark + " " + repo.full_name + "\n"
415
+ output = output + " " + repo.html_url + "\n"
416
+ if repo.description != "" {
417
+ output = output + " " + repo.description + "\n"
418
+ }
419
+ output = output + "\n"
420
+ i = i + 1
421
+ }
422
+
423
+ output = output + "Total: " + str(length(repos)) + " repositories\n"
424
+
425
+ CliResult {
426
+ exit_code: 0,
427
+ output: output,
428
+ error: ""
429
+ }
430
+ }
431
+
432
+ /// 저장소 목록 - JSON
433
+ fn repo_list_json(repos: [Repo]) -> CliResult {
434
+ var json = "[\n"
435
+
436
+ var i: i32 = 0
437
+ while i < length(repos) {
438
+ var repo = repos[i]
439
+ json = json + " {\n"
440
+ json = json + " \"name\": \"" + repo.full_name + "\",\n"
441
+ json = json + " \"url\": \"" + repo.html_url + "\",\n"
442
+ json = json + " \"ssh\": \"" + repo.ssh_url + "\",\n"
443
+ json = json + " \"private\": " + bool_to_json(repo.private) + ",\n"
444
+ json = json + " \"description\": \"" + repo.description + "\"\n"
445
+ json = json + " }"
446
+ if i < length(repos) - 1 {
447
+ json = json + ","
448
+ }
449
+ json = json + "\n"
450
+ i = i + 1
451
+ }
452
+
453
+ json = json + "]\n"
454
+
455
+ CliResult {
456
+ exit_code: 0,
457
+ output: json,
458
+ error: ""
459
+ }
460
+ }
461
+
462
+ /// 저장소 목록 - YAML
463
+ fn repo_list_yaml(repos: [Repo]) -> CliResult {
464
+ var yaml = "repositories:\n"
465
+
466
+ var i: i32 = 0
467
+ while i < length(repos) {
468
+ var repo = repos[i]
469
+ yaml = yaml + " - name: " + repo.full_name + "\n"
470
+ yaml = yaml + " url: " + repo.html_url + "\n"
471
+ yaml = yaml + " ssh: " + repo.ssh_url + "\n"
472
+ yaml = yaml + " private: " + bool_to_str(repo.private) + "\n"
473
+ if repo.description != "" {
474
+ yaml = yaml + " description: " + repo.description + "\n"
475
+ }
476
+ i = i + 1
477
+ }
478
+
479
+ CliResult {
480
+ exit_code: 0,
481
+ output: yaml,
482
+ error: ""
483
+ }
484
+ }
485
+
486
+ // ==================== 출력 포맷: 상세 ====================
487
+
488
+ /// 저장소 상세 - 텍스트
489
+ fn repo_view_text(repo: Repo) -> CliResult {
490
+ var output = ""
491
+ output = output + "╔════════════════════════════════════════════════════════════════════╗\n"
492
+ output = output + "║ Repository Details ║\n"
493
+ output = output + "╚════════════════════════════════════════════════════════════════════╝\n"
494
+ output = output + "\n"
495
+ output = output + "Name: " + repo.full_name + "\n"
496
+ output = output + "Description: " + repo.description + "\n"
497
+ output = output + "URL: " + repo.html_url + "\n"
498
+ output = output + "SSH: " + repo.ssh_url + "\n"
499
+ output = output + "Private: " + bool_to_str(repo.private) + "\n"
500
+ output = output + "Fork: " + bool_to_str(repo.fork) + "\n"
501
+ output = output + "Default Branch: " + repo.default_branch + "\n"
502
+ output = output + "Owner: " + repo.owner.login + "\n"
503
+ output = output + "Created: " + repo.created_at + "\n"
504
+ output = output + "Updated: " + repo.updated_at + "\n"
505
+ output = output + "\n"
506
+
507
+ CliResult {
508
+ exit_code: 0,
509
+ output: output,
510
+ error: ""
511
+ }
512
+ }
513
+
514
+ /// 저장소 상세 - JSON
515
+ fn repo_view_json(repo: Repo) -> CliResult {
516
+ var json = "{\n"
517
+ json = json + " \"name\": \"" + repo.full_name + "\",\n"
518
+ json = json + " \"description\": \"" + repo.description + "\",\n"
519
+ json = json + " \"url\": \"" + repo.html_url + "\",\n"
520
+ json = json + " \"ssh\": \"" + repo.ssh_url + "\",\n"
521
+ json = json + " \"private\": " + bool_to_json(repo.private) + ",\n"
522
+ json = json + " \"fork\": " + bool_to_json(repo.fork) + ",\n"
523
+ json = json + " \"default_branch\": \"" + repo.default_branch + "\",\n"
524
+ json = json + " \"owner\": \"" + repo.owner.login + "\",\n"
525
+ json = json + " \"created_at\": \"" + repo.created_at + "\",\n"
526
+ json = json + " \"updated_at\": \"" + repo.updated_at + "\"\n"
527
+ json = json + "}\n"
528
+
529
+ CliResult {
530
+ exit_code: 0,
531
+ output: json,
532
+ error: ""
533
+ }
534
+ }
535
+
536
+ /// 저장소 상세 - YAML
537
+ fn repo_view_yaml(repo: Repo) -> CliResult {
538
+ var yaml = "name: " + repo.full_name + "\n"
539
+ yaml = yaml + "description: " + repo.description + "\n"
540
+ yaml = yaml + "url: " + repo.html_url + "\n"
541
+ yaml = yaml + "ssh: " + repo.ssh_url + "\n"
542
+ yaml = yaml + "private: " + bool_to_str(repo.private) + "\n"
543
+ yaml = yaml + "fork: " + bool_to_str(repo.fork) + "\n"
544
+ yaml = yaml + "default_branch: " + repo.default_branch + "\n"
545
+ yaml = yaml + "owner: " + repo.owner.login + "\n"
546
+ yaml = yaml + "created_at: " + repo.created_at + "\n"
547
+ yaml = yaml + "updated_at: " + repo.updated_at + "\n"
548
+
549
+ CliResult {
550
+ exit_code: 0,
551
+ output: yaml,
552
+ error: ""
553
+ }
554
+ }
555
+
556
+ // ==================== 유틸리티 ====================
557
+
558
+ fn bool_to_str(b: bool) -> str {
559
+ if b { "true" } else { "false" }
560
+ }
561
+
562
+ fn bool_to_json(b: bool) -> str {
563
+ if b { "true" } else { "false" }
564
+ }
565
+
566
+ fn indexOf(s: str, substr: str) -> i32 {
567
+ var i: i32 = 0
568
+ var len = length(s)
569
+ var sublen = length(substr)
570
+ while i <= len - sublen {
571
+ if substring(s, i, i + sublen) == substr { return i }
572
+ i = i + 1
573
+ }
574
+ -1
575
+ }
576
+
577
+ // 필요한 함수들:
578
+ // - get_positional, has_flag, get_flag, get_format (from main.fl)
579
+ // - get_default_host, new_client, new_cache
580
+ // - new_repo_service, create_repo, list_repos, get_repo, update_repo, delete_repo
581
+ // - error_message, bool_to_str, split_repo_path, indexOf, substring