git.delta.rocks / jrsonnet / refs/heads / trunk

difftreelog

source

crates/nixlike/src/to_string.rs3.4 KiBsourcehistory
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					// '' is escaped with '61					.replace("''", "'''")62					// ${ is escaped wth ''63					.replace("${", "''${")64					// \t is not counted as whitespace for dedent65					// to avoid confusion, it is printed literally.66					//67					// ...Escaped \t literal should be prefixed with '' for... Idk, this logic is complicated.68					.replace('\t', "''\\t"),69			);70		}71		out.push('\n');72		*padding -= 1;73		write_padding(out, padding);74		// Final newline is assumed due to str.ends_with condition75		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}