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,244 @@
1
+ // FreeLang v4.2 — gogs-cli Phase 5 팀 명령어
2
+ // create, list 구현
3
+ // 200라인
4
+
5
+ /// 팀 생성
6
+ /// gogs team create ORG/TEAM_NAME [--description "desc"] [--permission "push"]
7
+ async fn cmd_team_create(args: CliArgs) -> CliResult {
8
+ // 1. 인자 검증
9
+ var team_path = get_positional(args, 0)
10
+ if team_path == "" {
11
+ return CliResult {
12
+ exit_code: 1,
13
+ output: "",
14
+ error: "Team path (ORG/TEAM_NAME) is required"
15
+ }
16
+ }
17
+
18
+ // 파싱: "org/team" -> [org, team]
19
+ var parts = split_path(team_path, "/")
20
+ if length(parts) != 2 {
21
+ return CliResult {
22
+ exit_code: 1,
23
+ output: "",
24
+ error: "Invalid team path. Use: ORG/TEAM_NAME"
25
+ }
26
+ }
27
+
28
+ var org_name = parts[0]
29
+ var team_name = parts[1]
30
+
31
+ // 2. 플래그 파싱
32
+ var description = get_flag(args, "description")
33
+ var permission = get_flag(args, "permission")
34
+ if permission == "" {
35
+ permission = "push"
36
+ }
37
+
38
+ // 3. 설정 로드
39
+ var host = await get_default_host()
40
+ if host.url == "" {
41
+ return CliResult {
42
+ exit_code: 1,
43
+ output: "",
44
+ error: "Not logged in"
45
+ }
46
+ }
47
+
48
+ // 4. 클라이언트 및 캐시
49
+ var client = new_client(host.url, host.token)
50
+ var cache = new_cache(300)
51
+
52
+ // 5. API 호출
53
+ println("Creating team '" + team_name + "' in '" + org_name + "'...")
54
+
55
+ var team_service = new_team_service(client, cache)
56
+ var req = CreateTeamRequest {
57
+ name: team_name,
58
+ description: description,
59
+ permission: permission
60
+ }
61
+ var result = await create_team(team_service, org_name, req)
62
+
63
+ // 6. 결과 처리
64
+ match result {
65
+ Result::Ok(team) => {
66
+ var output = ""
67
+ output = output + "✓ Team created\n"
68
+ output = output + " Name: " + team.name + "\n"
69
+ output = output + " Description: " + team.description + "\n"
70
+ output = output + " Permission: " + team.permission + "\n"
71
+
72
+ CliResult {
73
+ exit_code: 0,
74
+ output: output,
75
+ error: ""
76
+ }
77
+ }
78
+ Result::Err(err) => {
79
+ CliResult {
80
+ exit_code: 1,
81
+ output: "",
82
+ error: error_message(err)
83
+ }
84
+ }
85
+ }
86
+ }
87
+
88
+ /// 팀 목록
89
+ /// gogs team list ORG_NAME [--json] [--yaml]
90
+ async fn cmd_team_list(args: CliArgs) -> CliResult {
91
+ // 1. 인자 검증
92
+ var org_name = get_positional(args, 0)
93
+ if org_name == "" {
94
+ return CliResult {
95
+ exit_code: 1,
96
+ output: "",
97
+ error: "Organization name is required"
98
+ }
99
+ }
100
+
101
+ // 2. 설정 로드
102
+ var host = await get_default_host()
103
+ if host.url == "" {
104
+ return CliResult {
105
+ exit_code: 1,
106
+ output: "",
107
+ error: "Not logged in"
108
+ }
109
+ }
110
+
111
+ // 3. 클라이언트 및 캐시
112
+ var client = new_client(host.url, host.token)
113
+ var cache = new_cache(300)
114
+
115
+ // 4. API 호출
116
+ println("Fetching teams for '" + org_name + "'...")
117
+
118
+ var team_service = new_team_service(client, cache)
119
+ var result = await list_teams(team_service, org_name)
120
+
121
+ // 5. 결과 처리
122
+ match result {
123
+ Result::Ok(teams) => {
124
+ var format = get_format(args)
125
+
126
+ if format == "json" {
127
+ return team_list_json(teams)
128
+ }
129
+ if format == "yaml" {
130
+ return team_list_yaml(teams)
131
+ }
132
+
133
+ team_list_text(org_name, teams)
134
+ }
135
+ Result::Err(err) => {
136
+ CliResult {
137
+ exit_code: 1,
138
+ output: "",
139
+ error: error_message(err)
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ // ==================== 출력 포맷 ====================
146
+
147
+ fn team_list_text(org_name: str, teams: [Team]) -> CliResult {
148
+ var output = ""
149
+ output = output + "╔════════════════════════════════════════════════════════════════════╗\n"
150
+ output = output + "║ Teams in " + org_name + "\n"
151
+ output = output + "╚════════════════════════════════════════════════════════════════════╝\n"
152
+ output = output + "\n"
153
+
154
+ if length(teams) == 0 {
155
+ output = output + "No teams found\n"
156
+ return CliResult {
157
+ exit_code: 0,
158
+ output: output,
159
+ error: ""
160
+ }
161
+ }
162
+
163
+ var i: i32 = 0
164
+ while i < length(teams) {
165
+ var team = teams[i]
166
+ output = output + "👥 " + team.name + "\n"
167
+ output = output + " " + team.description + "\n"
168
+ output = output + " Permission: " + team.permission + "\n"
169
+ output = output + "\n"
170
+ i = i + 1
171
+ }
172
+
173
+ output = output + "Total: " + str(length(teams)) + " teams\n"
174
+
175
+ CliResult {
176
+ exit_code: 0,
177
+ output: output,
178
+ error: ""
179
+ }
180
+ }
181
+
182
+ fn team_list_json(teams: [Team]) -> CliResult {
183
+ var json = "[\n"
184
+ var i: i32 = 0
185
+ while i < length(teams) {
186
+ var team = teams[i]
187
+ json = json + " {\"name\": \"" + team.name + "\", \"description\": \"" + team.description + "\", \"permission\": \"" + team.permission + "\"}"
188
+ if i < length(teams) - 1 { json = json + "," }
189
+ json = json + "\n"
190
+ i = i + 1
191
+ }
192
+ json = json + "]\n"
193
+ CliResult { exit_code: 0, output: json, error: "" }
194
+ }
195
+
196
+ fn team_list_yaml(teams: [Team]) -> CliResult {
197
+ var yaml = "teams:\n"
198
+ var i: i32 = 0
199
+ while i < length(teams) {
200
+ var team = teams[i]
201
+ yaml = yaml + " - name: " + team.name + "\n"
202
+ yaml = yaml + " description: " + team.description + "\n"
203
+ yaml = yaml + " permission: " + team.permission + "\n"
204
+ i = i + 1
205
+ }
206
+ CliResult { exit_code: 0, output: yaml, error: "" }
207
+ }
208
+
209
+ // ==================== 유틸리티 ====================
210
+
211
+ fn split_path(path: str, sep: str) -> [str] {
212
+ var result: [str] = []
213
+ var idx = indexOf(path, sep)
214
+
215
+ if idx == -1 {
216
+ result.push(path)
217
+ return result
218
+ }
219
+
220
+ var part1 = substring(path, 0, idx)
221
+ var part2 = substring(path, idx + 1, length(path))
222
+
223
+ result.push(part1)
224
+ result.push(part2)
225
+
226
+ result
227
+ }
228
+
229
+ fn indexOf(s: str, substr: str) -> i32 {
230
+ var i: i32 = 0
231
+ var len = length(s)
232
+ var sublen = length(substr)
233
+ while i <= len - sublen {
234
+ if substring(s, i, i + sublen) == substr { return i }
235
+ i = i + 1
236
+ }
237
+ -1
238
+ }
239
+
240
+ // 필요한 함수들:
241
+ // - get_positional, has_flag, get_flag, get_format
242
+ // - get_default_host, new_client, new_cache
243
+ // - new_team_service, create_team, list_teams
244
+ // - error_message
@@ -0,0 +1,423 @@
1
+ // FreeLang v4.2 — gogs-cli Phase 5 사용자 명령어
2
+ // create, list, view, delete 구현
3
+ // 300라인
4
+
5
+ /// 사용자 생성 (admin only)
6
+ /// gogs user create USERNAME --email EMAIL --password PASSWORD
7
+ async fn cmd_user_create(args: CliArgs) -> CliResult {
8
+ // 1. 인자 검증
9
+ var username = get_positional(args, 0)
10
+ if username == "" {
11
+ return CliResult {
12
+ exit_code: 1,
13
+ output: "",
14
+ error: "Username is required"
15
+ }
16
+ }
17
+
18
+ // 2. 플래그 파싱
19
+ var email = get_flag(args, "email")
20
+ var password = get_flag(args, "password")
21
+ var admin = has_flag(args, "admin")
22
+
23
+ if email == "" {
24
+ return CliResult {
25
+ exit_code: 1,
26
+ output: "",
27
+ error: "--email is required"
28
+ }
29
+ }
30
+
31
+ if password == "" {
32
+ return CliResult {
33
+ exit_code: 1,
34
+ output: "",
35
+ error: "--password is required"
36
+ }
37
+ }
38
+
39
+ // 3. 설정 로드
40
+ var host = await get_default_host()
41
+ if host.url == "" {
42
+ return CliResult {
43
+ exit_code: 1,
44
+ output: "",
45
+ error: "Not logged in"
46
+ }
47
+ }
48
+
49
+ // 4. 클라이언트 및 캐시
50
+ var client = new_client(host.url, host.token)
51
+ var cache = new_cache(300)
52
+
53
+ // 5. API 호출
54
+ println("Creating user '" + username + "'...")
55
+
56
+ var user_service = new_user_service(client, cache)
57
+ var req = CreateUserRequest {
58
+ username: username,
59
+ email: email,
60
+ password: password,
61
+ send_notify: false,
62
+ is_admin: admin
63
+ }
64
+ var result = await create_user(user_service, req)
65
+
66
+ // 6. 결과 처리
67
+ match result {
68
+ Result::Ok(user) => {
69
+ var output = ""
70
+ output = output + "✓ User created\n"
71
+ output = output + " Username: " + user.login + "\n"
72
+ output = output + " Email: " + user.email + "\n"
73
+ output = output + " Admin: " + bool_to_str(user.is_admin) + "\n"
74
+
75
+ CliResult {
76
+ exit_code: 0,
77
+ output: output,
78
+ error: ""
79
+ }
80
+ }
81
+ Result::Err(err) => {
82
+ CliResult {
83
+ exit_code: 1,
84
+ output: "",
85
+ error: error_message(err)
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+ /// 사용자 목록
92
+ /// gogs user list [--json] [--yaml]
93
+ async fn cmd_user_list(args: CliArgs) -> CliResult {
94
+ // 1. 설정 로드
95
+ var host = await get_default_host()
96
+ if host.url == "" {
97
+ return CliResult {
98
+ exit_code: 1,
99
+ output: "",
100
+ error: "Not logged in"
101
+ }
102
+ }
103
+
104
+ // 2. 클라이언트 및 캐시
105
+ var client = new_client(host.url, host.token)
106
+ var cache = new_cache(300)
107
+
108
+ // 3. API 호출
109
+ println("Fetching users...")
110
+
111
+ var user_service = new_user_service(client, cache)
112
+ var result = await list_users(user_service)
113
+
114
+ // 4. 결과 처리
115
+ match result {
116
+ Result::Ok(users) => {
117
+ var format = get_format(args)
118
+
119
+ if format == "json" {
120
+ return user_list_json(users)
121
+ }
122
+ if format == "yaml" {
123
+ return user_list_yaml(users)
124
+ }
125
+
126
+ user_list_text(users)
127
+ }
128
+ Result::Err(err) => {
129
+ CliResult {
130
+ exit_code: 1,
131
+ output: "",
132
+ error: error_message(err)
133
+ }
134
+ }
135
+ }
136
+ }
137
+
138
+ /// 사용자 상세
139
+ /// gogs user view USERNAME [--json] [--yaml]
140
+ async fn cmd_user_view(args: CliArgs) -> CliResult {
141
+ // 1. 인자 검증
142
+ var username = get_positional(args, 0)
143
+ if username == "" {
144
+ return CliResult {
145
+ exit_code: 1,
146
+ output: "",
147
+ error: "Username is required"
148
+ }
149
+ }
150
+
151
+ // 2. 설정 로드
152
+ var host = await get_default_host()
153
+ if host.url == "" {
154
+ return CliResult {
155
+ exit_code: 1,
156
+ output: "",
157
+ error: "Not logged in"
158
+ }
159
+ }
160
+
161
+ // 3. 클라이언트 및 캐시
162
+ var client = new_client(host.url, host.token)
163
+ var cache = new_cache(300)
164
+
165
+ // 4. API 호출
166
+ println("Fetching user '" + username + "'...")
167
+
168
+ var user_service = new_user_service(client, cache)
169
+ var result = await get_user(user_service, username)
170
+
171
+ // 5. 결과 처리
172
+ match result {
173
+ Result::Ok(user) => {
174
+ var format = get_format(args)
175
+
176
+ if format == "json" {
177
+ return user_view_json(user)
178
+ }
179
+ if format == "yaml" {
180
+ return user_view_yaml(user)
181
+ }
182
+
183
+ user_view_text(user)
184
+ }
185
+ Result::Err(err) => {
186
+ CliResult {
187
+ exit_code: 1,
188
+ output: "",
189
+ error: error_message(err)
190
+ }
191
+ }
192
+ }
193
+ }
194
+
195
+ /// 사용자 삭제
196
+ /// gogs user delete USERNAME [--force]
197
+ async fn cmd_user_delete(args: CliArgs) -> CliResult {
198
+ // 1. 인자 검증
199
+ var username = get_positional(args, 0)
200
+ if username == "" {
201
+ return CliResult {
202
+ exit_code: 1,
203
+ output: "",
204
+ error: "Username is required"
205
+ }
206
+ }
207
+
208
+ // 2. 플래그 확인
209
+ var force = has_flag(args, "force")
210
+
211
+ if !force {
212
+ println("⚠️ This will permanently delete user '" + username + "'")
213
+ println("Run with --force to confirm")
214
+ return CliResult {
215
+ exit_code: 1,
216
+ output: "",
217
+ error: "Deletion not confirmed"
218
+ }
219
+ }
220
+
221
+ // 3. 설정 로드
222
+ var host = await get_default_host()
223
+ if host.url == "" {
224
+ return CliResult {
225
+ exit_code: 1,
226
+ output: "",
227
+ error: "Not logged in"
228
+ }
229
+ }
230
+
231
+ // 4. 클라이언트 및 캐시
232
+ var client = new_client(host.url, host.token)
233
+ var cache = new_cache(300)
234
+
235
+ // 5. API 호출
236
+ println("Deleting user '" + username + "'...")
237
+
238
+ var user_service = new_user_service(client, cache)
239
+ var result = await delete_user(user_service, username)
240
+
241
+ // 6. 결과 처리
242
+ match result {
243
+ Result::Ok(_) => {
244
+ CliResult {
245
+ exit_code: 0,
246
+ output: "✓ User deleted: " + username,
247
+ error: ""
248
+ }
249
+ }
250
+ Result::Err(err) => {
251
+ CliResult {
252
+ exit_code: 1,
253
+ output: "",
254
+ error: error_message(err)
255
+ }
256
+ }
257
+ }
258
+ }
259
+
260
+ // ==================== 출력 포맷: 목록 ====================
261
+
262
+ /// 사용자 목록 - 텍스트
263
+ fn user_list_text(users: [User]) -> CliResult {
264
+ var output = ""
265
+ output = output + "╔════════════════════════════════════════════════════════════════════╗\n"
266
+ output = output + "║ User List ║\n"
267
+ output = output + "╚════════════════════════════════════════════════════════════════════╝\n"
268
+ output = output + "\n"
269
+
270
+ if length(users) == 0 {
271
+ output = output + "No users found\n"
272
+ return CliResult {
273
+ exit_code: 0,
274
+ output: output,
275
+ error: ""
276
+ }
277
+ }
278
+
279
+ var i: i32 = 0
280
+ while i < length(users) {
281
+ var user = users[i]
282
+ var admin_mark = if user.is_admin then "[A]" else " "
283
+ output = output + admin_mark + " " + user.login + "\n"
284
+ output = output + " " + user.full_name + " <" + user.email + ">\n"
285
+ output = output + " Created: " + user.created_at + "\n"
286
+ output = output + "\n"
287
+ i = i + 1
288
+ }
289
+
290
+ output = output + "Total: " + str(length(users)) + " users\n"
291
+
292
+ CliResult {
293
+ exit_code: 0,
294
+ output: output,
295
+ error: ""
296
+ }
297
+ }
298
+
299
+ /// 사용자 목록 - JSON
300
+ fn user_list_json(users: [User]) -> CliResult {
301
+ var json = "[\n"
302
+
303
+ var i: i32 = 0
304
+ while i < length(users) {
305
+ var user = users[i]
306
+ json = json + " {\n"
307
+ json = json + " \"login\": \"" + user.login + "\",\n"
308
+ json = json + " \"full_name\": \"" + user.full_name + "\",\n"
309
+ json = json + " \"email\": \"" + user.email + "\",\n"
310
+ json = json + " \"is_admin\": " + bool_to_json(user.is_admin) + ",\n"
311
+ json = json + " \"created_at\": \"" + user.created_at + "\"\n"
312
+ json = json + " }"
313
+ if i < length(users) - 1 {
314
+ json = json + ","
315
+ }
316
+ json = json + "\n"
317
+ i = i + 1
318
+ }
319
+
320
+ json = json + "]\n"
321
+
322
+ CliResult {
323
+ exit_code: 0,
324
+ output: json,
325
+ error: ""
326
+ }
327
+ }
328
+
329
+ /// 사용자 목록 - YAML
330
+ fn user_list_yaml(users: [User]) -> CliResult {
331
+ var yaml = "users:\n"
332
+
333
+ var i: i32 = 0
334
+ while i < length(users) {
335
+ var user = users[i]
336
+ yaml = yaml + " - login: " + user.login + "\n"
337
+ yaml = yaml + " full_name: " + user.full_name + "\n"
338
+ yaml = yaml + " email: " + user.email + "\n"
339
+ yaml = yaml + " is_admin: " + bool_to_str(user.is_admin) + "\n"
340
+ yaml = yaml + " created_at: " + user.created_at + "\n"
341
+ i = i + 1
342
+ }
343
+
344
+ CliResult {
345
+ exit_code: 0,
346
+ output: yaml,
347
+ error: ""
348
+ }
349
+ }
350
+
351
+ // ==================== 출력 포맷: 상세 ====================
352
+
353
+ /// 사용자 상세 - 텍스트
354
+ fn user_view_text(user: User) -> CliResult {
355
+ var output = ""
356
+ output = output + "╔════════════════════════════════════════════════════════════════════╗\n"
357
+ output = output + "║ User Details ║\n"
358
+ output = output + "╚════════════════════════════════════════════════════════════════════╝\n"
359
+ output = output + "\n"
360
+ output = output + "Login: " + user.login + "\n"
361
+ output = output + "Full Name: " + user.full_name + "\n"
362
+ output = output + "Email: " + user.email + "\n"
363
+ output = output + "Admin: " + bool_to_str(user.is_admin) + "\n"
364
+ output = output + "Created: " + user.created_at + "\n"
365
+ output = output + "Updated: " + user.updated_at + "\n"
366
+ output = output + "\n"
367
+
368
+ CliResult {
369
+ exit_code: 0,
370
+ output: output,
371
+ error: ""
372
+ }
373
+ }
374
+
375
+ /// 사용자 상세 - JSON
376
+ fn user_view_json(user: User) -> CliResult {
377
+ var json = "{\n"
378
+ json = json + " \"login\": \"" + user.login + "\",\n"
379
+ json = json + " \"full_name\": \"" + user.full_name + "\",\n"
380
+ json = json + " \"email\": \"" + user.email + "\",\n"
381
+ json = json + " \"is_admin\": " + bool_to_json(user.is_admin) + ",\n"
382
+ json = json + " \"created_at\": \"" + user.created_at + "\",\n"
383
+ json = json + " \"updated_at\": \"" + user.updated_at + "\"\n"
384
+ json = json + "}\n"
385
+
386
+ CliResult {
387
+ exit_code: 0,
388
+ output: json,
389
+ error: ""
390
+ }
391
+ }
392
+
393
+ /// 사용자 상세 - YAML
394
+ fn user_view_yaml(user: User) -> CliResult {
395
+ var yaml = "login: " + user.login + "\n"
396
+ yaml = yaml + "full_name: " + user.full_name + "\n"
397
+ yaml = yaml + "email: " + user.email + "\n"
398
+ yaml = yaml + "is_admin: " + bool_to_str(user.is_admin) + "\n"
399
+ yaml = yaml + "created_at: " + user.created_at + "\n"
400
+ yaml = yaml + "updated_at: " + user.updated_at + "\n"
401
+
402
+ CliResult {
403
+ exit_code: 0,
404
+ output: yaml,
405
+ error: ""
406
+ }
407
+ }
408
+
409
+ // ==================== 유틸리티 ====================
410
+
411
+ fn bool_to_str(b: bool) -> str {
412
+ if b { "true" } else { "false" }
413
+ }
414
+
415
+ fn bool_to_json(b: bool) -> str {
416
+ if b { "true" } else { "false" }
417
+ }
418
+
419
+ // 필요한 함수들:
420
+ // - get_positional, has_flag, get_flag, get_format
421
+ // - get_default_host, new_client, new_cache
422
+ // - new_user_service, create_user, list_users, get_user, delete_user
423
+ // - error_message