difftreelog
style fix clippy warnings
in: master
1 file changed
crates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth1use crate::error::Error::*;2use crate::error::Result;3use crate::push_description_frame;4use crate::{throw, Val};56#[derive(PartialEq, Clone, Copy)]7pub enum ManifestType {8 // Applied in manifestification9 Manifest,10 /// Used for std.manifestJson11 /// Empty array/objects extends to "[\n\n]" instead of "[ ]" as in manifest12 Std,13 /// No line breaks, used in `obj+''`14 ToString,15 /// Minified json16 Minify,17}1819pub struct ManifestJsonOptions<'s> {20 pub padding: &'s str,21 pub mtype: ManifestType,22}2324pub fn manifest_json_ex(val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {25 let mut out = String::new();26 manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;27 Ok(out)28}29fn manifest_json_ex_buf(30 val: &Val,31 buf: &mut String,32 cur_padding: &mut String,33 options: &ManifestJsonOptions<'_>,34) -> Result<()> {35 use std::fmt::Write;36 let mtype = options.mtype;37 match val {38 Val::Bool(v) => {39 if *v {40 buf.push_str("true");41 } else {42 buf.push_str("false");43 }44 }45 Val::Null => buf.push_str("null"),46 Val::Str(s) => escape_string_json_buf(s, buf),47 Val::Num(n) => write!(buf, "{}", n).unwrap(),48 Val::Arr(items) => {49 buf.push('[');50 if !items.is_empty() {51 if mtype != ManifestType::ToString && mtype != ManifestType::Minify {52 buf.push('\n');53 }5455 let old_len = cur_padding.len();56 cur_padding.push_str(options.padding);57 for (i, item) in items.iter().enumerate() {58 if i != 0 {59 buf.push(',');60 if mtype == ManifestType::ToString {61 buf.push(' ');62 } else if mtype != ManifestType::Minify {63 buf.push('\n');64 }65 }66 buf.push_str(cur_padding);67 manifest_json_ex_buf(&item?, buf, cur_padding, options)?;68 }69 cur_padding.truncate(old_len);7071 if mtype != ManifestType::ToString && mtype != ManifestType::Minify {72 buf.push('\n');73 buf.push_str(cur_padding);74 }75 } else if mtype == ManifestType::Std {76 buf.push_str("\n\n");77 buf.push_str(cur_padding);78 } else if mtype == ManifestType::ToString || mtype == ManifestType::Manifest {79 buf.push(' ');80 }81 buf.push(']');82 }83 Val::Obj(obj) => {84 obj.run_assertions()?;85 buf.push('{');86 let fields = obj.fields();87 if !fields.is_empty() {88 if mtype != ManifestType::ToString && mtype != ManifestType::Minify {89 buf.push('\n');90 }9192 let old_len = cur_padding.len();93 cur_padding.push_str(options.padding);94 for (i, field) in fields.into_iter().enumerate() {95 if i != 0 {96 buf.push(',');97 if mtype == ManifestType::ToString {98 buf.push(' ');99 } else if mtype != ManifestType::Minify {100 buf.push('\n');101 }102 }103 buf.push_str(cur_padding);104 escape_string_json_buf(&field, buf);105 buf.push_str(": ");106 push_description_frame(107 || format!("field <{}> manifestification", field.clone()),108 || {109 let value = obj.get(field.clone())?.unwrap();110 manifest_json_ex_buf(&value, buf, cur_padding, options)?;111 Ok(Val::Null)112 },113 )?;114 }115 cur_padding.truncate(old_len);116117 if mtype != ManifestType::ToString && mtype != ManifestType::Minify {118 buf.push('\n');119 buf.push_str(cur_padding);120 }121 } else if mtype == ManifestType::Std {122 buf.push_str("\n\n");123 buf.push_str(cur_padding);124 } else if mtype == ManifestType::ToString || mtype == ManifestType::Manifest {125 buf.push(' ');126 }127 buf.push('}');128 }129 Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),130 };131 Ok(())132}133134pub fn escape_string_json(s: &str) -> String {135 let mut buf = String::new();136 escape_string_json_buf(s, &mut buf);137 buf138}139140fn escape_string_json_buf(s: &str, buf: &mut String) {141 use std::fmt::Write;142 buf.push('"');143 for c in s.chars() {144 match c {145 '"' => buf.push_str("\\\""),146 '\\' => buf.push_str("\\\\"),147 '\u{0008}' => buf.push_str("\\b"),148 '\u{000c}' => buf.push_str("\\f"),149 '\n' => buf.push_str("\\n"),150 '\r' => buf.push_str("\\r"),151 '\t' => buf.push_str("\\t"),152 c if c < 32 as char || (c >= 127 as char && c <= 159 as char) => {153 write!(buf, "\\u{:04x}", c as u32).unwrap()154 }155 c => buf.push(c),156 }157 }158 buf.push('"');159}160161pub struct ManifestYamlOptions<'s> {162 /// Padding before fields, i.e163 /// ```yaml164 /// a:165 /// b:166 /// ## <- this167 /// ```168 pub padding: &'s str,169 /// Padding before array elements in objects170 /// ```yaml171 /// a:172 /// - 1173 /// ## <- this174 /// ```175 pub arr_element_padding: &'s str,176 /// Should yaml keys appear unescaped, when possible177 /// ```yaml178 /// "safe_key": 1179 /// # vs180 /// safe_key: 1181 /// ```182 pub quote_keys: bool,183}184185/// From https://github.com/chyh1990/yaml-rust/blob/da52a68615f2ecdd6b7e4567019f280c433c1521/src/emitter.rs#L289186/// With added date check187fn yaml_needs_quotes(string: &str) -> bool {188 fn need_quotes_spaces(string: &str) -> bool {189 string.starts_with(' ') || string.ends_with(' ')190 }191192 string == ""193 || need_quotes_spaces(string)194 || string.starts_with(|character: char| match character {195 '&' | '*' | '?' | '|' | '-' | '<' | '>' | '=' | '!' | '%' | '@' => true,196 _ => false,197 }) || string.contains(|character: char| match character {198 ':'199 | '{'200 | '}'201 | '['202 | ']'203 | ','204 | '#'205 | '`'206 | '\"'207 | '\''208 | '\\'209 | '\0'..='\x06'210 | '\t'211 | '\n'212 | '\r'213 | '\x0e'..='\x1a'214 | '\x1c'..='\x1f' => true,215 _ => false,216 }) || [217 // http://yaml.org/type/bool.html218 // Note: 'y', 'Y', 'n', 'N', is not quoted deliberately, as in libyaml. PyYAML also parse219 // them as string, not booleans, although it is violating the YAML 1.1 specification.220 // See https://github.com/dtolnay/serde-yaml/pull/83#discussion_r152628088.221 "yes", "Yes", "YES", "no", "No", "NO", "True", "TRUE", "true", "False", "FALSE", "false",222 "on", "On", "ON", "off", "Off", "OFF", // http://yaml.org/type/null.html223 "null", "Null", "NULL", "~",224 ]225 .contains(&string)226 || (string.chars().all(|c| matches!(c, '0'..='9' | '-'))227 && string.chars().filter(|c| *c == '-').count() == 2)228 || string.starts_with('.')229 || string.starts_with("0x")230 || string.parse::<i64>().is_ok()231 || string.parse::<f64>().is_ok()232}233234pub fn manifest_yaml_ex(val: &Val, options: &ManifestYamlOptions<'_>) -> Result<String> {235 let mut out = String::new();236 manifest_yaml_ex_buf(val, &mut out, &mut String::new(), options)?;237 Ok(out)238}239fn manifest_yaml_ex_buf(240 val: &Val,241 buf: &mut String,242 cur_padding: &mut String,243 options: &ManifestYamlOptions<'_>,244) -> Result<()> {245 use std::fmt::Write;246 match val {247 Val::Bool(v) => {248 if *v {249 buf.push_str("true")250 } else {251 buf.push_str("false")252 }253 }254 Val::Null => buf.push_str("null"),255 Val::Str(s) => {256 if s.is_empty() {257 buf.push_str("\"\"");258 } else if let Some(s) = s.strip_suffix('\n') {259 buf.push('|');260 for line in s.split('\n') {261 buf.push('\n');262 buf.push_str(options.padding);263 buf.push_str(line);264 }265 } else if !options.quote_keys && !yaml_needs_quotes(&s) {266 buf.push_str(&s);267 } else {268 escape_string_json_buf(s, buf);269 }270 }271 Val::Num(n) => write!(buf, "{}", *n).unwrap(),272 Val::Arr(a) => {273 if a.is_empty() {274 buf.push_str("[]");275 } else {276 for (i, item) in a.iter().enumerate() {277 if i != 0 {278 buf.push('\n');279 buf.push_str(cur_padding);280 }281 let item = item?;282 buf.push('-');283 match &item {284 Val::Arr(a) if !a.is_empty() => {285 buf.push('\n');286 buf.push_str(cur_padding);287 buf.push_str(options.padding);288 }289 _ => buf.push(' '),290 }291 let extra_padding = match &item {292 Val::Arr(a) => !a.is_empty(),293 Val::Obj(o) => !o.is_empty(),294 _ => false,295 };296 let prev_len = cur_padding.len();297 if extra_padding {298 cur_padding.push_str(options.padding);299 }300 manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;301 cur_padding.truncate(prev_len);302 }303 }304 }305 Val::Obj(o) => {306 if o.is_empty() {307 buf.push_str("{}");308 } else {309 for (i, key) in o.fields().iter().enumerate() {310 if i != 0 {311 buf.push('\n');312 buf.push_str(cur_padding);313 }314 if !options.quote_keys && !yaml_needs_quotes(&key) {315 buf.push_str(&key);316 } else {317 escape_string_json_buf(key, buf);318 }319 buf.push(':');320 let prev_len = cur_padding.len();321 let item = o.get(key.clone())?.expect("field exists");322 match &item {323 Val::Arr(a) if !a.is_empty() => {324 buf.push('\n');325 buf.push_str(cur_padding);326 buf.push_str(options.arr_element_padding);327 cur_padding.push_str(options.arr_element_padding);328 }329 Val::Obj(o) if !o.is_empty() => {330 buf.push('\n');331 buf.push_str(cur_padding);332 buf.push_str(options.padding);333 cur_padding.push_str(options.padding);334 }335 _ => buf.push(' '),336 }337 manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;338 cur_padding.truncate(prev_len);339 }340 }341 }342 Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),343 }344 Ok(())345}