// 설정 파일 리포트 도구. // // 파일을 읽고, 파싱하고, 문제를 보고하고, 요약을 낸다. // 권한은 셋뿐이다 — 읽기(File), 인자(Args), 출력(Console). 그 밖의 일은 // 이 프로그램이 할 수 없다. 시그니처에 적힌 것이 이 프로그램의 전부다. import "config" as Cfg import "cool.dev/std/list" as List import "cool.dev/std/string" as String import "cool.dev/std/int" as Int pub capability Console { fn print(s: String) effects {Console.print} } pub capability File { fn read(path: String) effects {File.read} -> Result[String, String] } pub capability Args { fn all() effects {Args.all} -> List[String] } pub fn report(c: Console, cfg: Cfg.Config) effects {Console.print} { c.print("설정") List.each(cfg.entries, fn(e) { c.print(String.concat(" ", String.concat(e.key, String.concat(" = ", String.concat(Cfg.show_value(e.value), String.concat(" (", String.concat(Cfg.type_name(e.value), ")"))))))) }) c.print("") c.print(String.concat("항목 ", String.concat(Int.show(List.len(cfg.entries)), String.concat("개, 문제 ", String.concat(Int.show(List.len(cfg.problems)), "개"))))) if List.is_empty(cfg.problems) { c.print("문제 없음") } else { c.print("") c.print("문제") List.each(cfg.problems, fn(p) { c.print(String.concat(" ", String.concat(Int.show(p.line), String.concat("행: ", p.message)))) }) } } // 타입이 있는 조회. 없거나 타입이 다르면 Err이고, 둘 다 사람이 읽을 수 있다. pub fn check_known(c: Console, cfg: Cfg.Config) effects {Console.print} { c.print("") c.print("알려진 설정") show_int(c, cfg, "threads") show_int(c, cfg, "port") show_flag(c, cfg, "verbose") } pub fn show_int(c: Console, cfg: Cfg.Config, key: String) effects {Console.print} { match Cfg.get_int(cfg, key) { Ok(n) => c.print(String.concat(" ", String.concat(key, String.concat(" = ", Int.show(n))))), Err(m) => c.print(String.concat(" ", m)), } } pub fn show_flag(c: Console, cfg: Cfg.Config, key: String) effects {Console.print} { match Cfg.get_flag(cfg, key) { Ok(b) => c.print(String.concat(" ", String.concat(key, String.concat(" = ", if b { "true" } else { "false" })))), Err(m) => c.print(String.concat(" ", m)), } } pub fn first_arg(xs: List[String]) -> Result[String, String] { match Cfg.first_text(xs) { Some(p) => Ok(p), None => Err("설정 파일 경로가 필요합니다"), } } pub fn main(c: Console, f: File, a: Args) effects {Console.print, File.read, Args.all} { match run(c, f, a) { Ok(_) => unit, Err(m) => c.print(String.concat("오류: ", m)), } } pub fn run(c: Console, f: File, a: Args) effects {Console.print, File.read, Args.all} -> Result[Unit, String] { let path = first_arg(a.all())? let text = f.read(path)? let cfg = Cfg.parse(text) report(c, cfg) check_known(c, cfg) Ok(unit) }