1use itertools::Itertools;23use crate::Value;45pub fn write_identifier(k: &str, out: &mut String) {6 if k.contains(['.', '\'', '\"', '\\', '\n', '\t', '\r', '$']) {7 write_nix_str_singleline(k, out);8 } else {9 out.push_str(k);10 }11}1213fn write_nix_obj_key_buf(k: &str, v: &Value, out: &mut String, padding: &mut usize) {14 write_identifier(k, out);15 match v {16 Value::Object(o) if o.len() == 1 => {17 let (k, v) = o.iter().next().unwrap();1819 out.push('.');20 write_nix_obj_key_buf(k, v, out, padding);21 }22 v => {23 out.push_str(" = ");24 write_nix_buf(v, out, padding);25 out.push(';');26 }27 }28}2930pub fn escape_string(str: &str) -> String {31 format!(32 "\"{}\"",33 str.replace('\\', "\\\\")34 .replace('"', "\\\"")35 .replace('\n', "\\n")36 .replace('\t', "\\t")37 .replace('\r', "\\r")38 .replace('$', "\\$")39 )40}4142fn write_padding(out: &mut String, padding: &usize) {43 for _ in 0..*padding {44 out.push_str(" ");45 }46}4748pub fn write_nix_str_singleline(str: &str, out: &mut String) {49 out.push_str(&escape_string(str))50}51pub fn write_nix_str(str: &str, out: &mut String, padding: &mut usize) {52 if str.ends_with('\n') {53 out.push_str("''");54 *padding += 1;55 for ele in str[0..str.len() - 1].split('\n') {56 out.push('\n');57 write_padding(out, padding);58 out.push_str(59 &ele60 61 .replace("''", "'''")62 63 .replace("${", "''${")64 65 66 67 68 .replace('\t', "''\\t"),69 );70 }71 out.push('\n');72 *padding -= 1;73 write_padding(out, padding);74 75 out.push_str("''");76 } else {77 write_nix_str_singleline(str, out);78 }79}8081fn write_nix_import(import: &str, out: &mut String, padding: &mut usize) {82 out.push_str("import ");83 write_nix_str(import, out, padding)84}85fn write_nix_buf(value: &Value, out: &mut String, padding: &mut usize) {86 match value {87 Value::Null => out.push_str("null"),88 Value::Boolean(v) => out.push_str(if *v { "true" } else { "false" }),89 Value::Number(n) => out.push_str(&format!("{n}")),90 Value::String(s) => write_nix_str(s, out, padding),91 Value::Array(a) => {92 if a.is_empty() {93 out.push_str("[ ]");94 } else {95 out.push_str("[\n");96 *padding += 1;97 for item in a {98 write_padding(out, padding);99 write_nix_buf(item, out, padding);100 out.push('\n');101 }102 *padding -= 1;103 write_padding(out, padding);104 out.push(']');105 }106 }107 Value::Import(i) => write_nix_import(&i.import, out, padding),108 Value::Object(obj) => {109 if obj.is_empty() {110 out.push_str("{ }");111 } else if obj.len() == 2112 && let Some([(importk, Value::String(importv)), (markerk, Value::Null)]) =113 obj.iter().next_array::<2>()114 && markerk == "__magic_marker"115 && importk == "__magic_import"116 {117 write_nix_import(importv, out, padding)118 } else {119 out.push_str("{\n");120 *padding += 1;121 for (k, v) in obj {122 write_padding(out, padding);123 write_nix_obj_key_buf(k, v, out, padding);124 out.push('\n');125 }126 *padding -= 1;127 write_padding(out, padding);128 out.push('}');129 }130 }131 };132}133134pub fn write_nix(value: &Value) -> String {135 let mut out = String::new();136 write_nix_buf(value, &mut out, &mut 0);137 out138}