difftreelog
feat(nixlike) formatted output
in: trunk
1 file changed
crates/nixlike/src/to_string.rsdiffbeforeafterboth1use crate::Value;23pub fn write_identifier(k: &str, out: &mut String) {4 if k.contains(['.', '\'', '\"', '\\', '\n', '\t', '\r', '$']) {5 write_nix_str(k, out);6 } else {7 out.push_str(k);8 }9}1011fn write_nix_obj_key_buf(k: &str, v: &Value, out: &mut String) {12 write_identifier(k, out);13 match v {14 Value::Object(o) if o.len() == 1 => {15 let (k, v) = o.iter().next().unwrap();1617 out.push('.');18 write_nix_obj_key_buf(k, v, out);19 }20 v => {21 out.push_str(" = ");22 write_nix_buf(v, out);23 out.push(';');24 }25 }26}2728pub fn escape_string(str: &str) -> String {29 format!(30 "\"{}\"",31 str.replace('\\', "\\\\")32 .replace('"', "\\\"")33 .replace('\n', "\\n")34 .replace('\t', "\\t")35 .replace('\r', "\\r")36 .replace('$', "\\$")37 )38}3940pub fn write_nix_str(str: &str, out: &mut String) {41 if str.ends_with('\n') {42 out.push_str("''");43 for ele in str.split('\n') {44 out.push('\n');45 out.push_str(46 &ele47 // '' is escaped with '48 .replace("''", "'''")49 // ${ is escaped wth ''50 .replace("${", "''${")51 // \t is not counted as whitespace for dedent52 // to avoid confusion, it is printed literally.53 //54 // ...Escaped \t literal should be prefixed with '' for... Idk, this logic is complicated.55 .replace('\t', "''\\t"),56 );57 }58 // Final newline is assumed due to str.ends_with condition59 out.push_str("''");60 } else {61 out.push_str(&escape_string(str))62 }63}6465fn write_nix_buf(value: &Value, out: &mut String) {66 match value {67 Value::Null => out.push_str("null"),68 Value::Boolean(v) => out.push_str(if *v { "true" } else { "false" }),69 Value::Number(n) => out.push_str(&format!("{}", n)),70 Value::String(s) => write_nix_str(s, out),71 Value::Array(a) => {72 if a.is_empty() {73 out.push_str("[ ]");74 } else {75 out.push('[');76 for item in a {77 write_nix_buf(item, out);78 out.push('\n');79 }80 out.push(']');81 }82 }83 Value::Object(obj) => {84 if obj.is_empty() {85 out.push_str("{ }")86 } else {87 out.push('{');88 for (k, v) in obj {89 write_nix_obj_key_buf(k, v, out);90 out.push('\n');91 }92 out.push('}');93 }94 }95 };96}9798pub fn write_nix(value: &Value) -> String {99 let mut out = String::new();100 write_nix_buf(value, &mut out);101 out102}1use crate::Value;23pub fn write_identifier(k: &str, out: &mut String) {4 if k.contains(['.', '\'', '\"', '\\', '\n', '\t', '\r', '$']) {5 write_nix_str_singleline(k, out);6 } else {7 out.push_str(k);8 }9}1011fn write_nix_obj_key_buf(k: &str, v: &Value, out: &mut String, padding: &mut usize) {12 write_identifier(k, out);13 match v {14 Value::Object(o) if o.len() == 1 => {15 let (k, v) = o.iter().next().unwrap();1617 out.push('.');18 write_nix_obj_key_buf(k, v, out, padding);19 }20 v => {21 out.push_str(" = ");22 write_nix_buf(v, out, padding);23 out.push(';');24 }25 }26}2728pub fn escape_string(str: &str) -> String {29 format!(30 "\"{}\"",31 str.replace('\\', "\\\\")32 .replace('"', "\\\"")33 .replace('\n', "\\n")34 .replace('\t', "\\t")35 .replace('\r', "\\r")36 .replace('$', "\\$")37 )38}3940fn write_padding(out: &mut String, padding: &usize) {41 for _ in 0..*padding {42 out.push_str(" ");43 }44}4546pub fn write_nix_str_singleline(str: &str, out: &mut String) {47 out.push_str(&escape_string(str))48}49pub fn write_nix_str(str: &str, out: &mut String, padding: &mut usize) {50 if str.ends_with('\n') {51 out.push_str("''");52 *padding += 1;53 for ele in str[0..str.len() - 1].split('\n') {54 out.push('\n');55 write_padding(out, padding);56 out.push_str(57 &ele58 // '' is escaped with '59 .replace("''", "'''")60 // ${ is escaped wth ''61 .replace("${", "''${")62 // \t is not counted as whitespace for dedent63 // to avoid confusion, it is printed literally.64 //65 // ...Escaped \t literal should be prefixed with '' for... Idk, this logic is complicated.66 .replace('\t', "''\\t"),67 );68 }69 out.push('\n');70 *padding -= 1;71 write_padding(out, padding);72 // Final newline is assumed due to str.ends_with condition73 out.push_str("''");74 } else {75 write_nix_str_singleline(str, out);76 }77}7879fn write_nix_buf(value: &Value, out: &mut String, padding: &mut usize) {80 match value {81 Value::Null => out.push_str("null"),82 Value::Boolean(v) => out.push_str(if *v { "true" } else { "false" }),83 Value::Number(n) => out.push_str(&format!("{n}")),84 Value::String(s) => write_nix_str(s, out, padding),85 Value::Array(a) => {86 if a.is_empty() {87 out.push_str("[ ]");88 } else {89 out.push_str("[\n");90 *padding += 1;91 for item in a {92 write_padding(out, padding);93 write_nix_buf(item, out, padding);94 out.push('\n');95 }96 *padding -= 1;97 write_padding(out, padding);98 out.push(']');99 }100 }101 Value::Object(obj) => {102 if obj.is_empty() {103 out.push_str("{ }")104 } else {105 out.push_str("{\n");106 *padding += 1;107 for (k, v) in obj {108 write_padding(out, padding);109 write_nix_obj_key_buf(k, v, out, padding);110 out.push('\n');111 }112 *padding -= 1;113 write_padding(out, padding);114 out.push('}');115 }116 }117 };118}119120pub fn write_nix(value: &Value) -> String {121 let mut out = String::new();122 write_nix_buf(value, &mut out, &mut 0);123 out124}