difftreelog
perf make manifestYamlDoc builtin
in: master
6 files changed
crates/jrsonnet-cli/src/manifest.rsdiffbeforeafterboth38 #[clap(long, short = 'y')]38 #[clap(long, short = 'y')]39 yaml_stream: bool,39 yaml_stream: bool,40 /// Number of spaces to pad output manifest with.40 /// Number of spaces to pad output manifest with.41 /// `0` for hard tabs, `-1` for single line output41 /// `0` for hard tabs, `-1` for single line output [default: 3 for json, 2 for yaml]42 #[clap(long, default_value = "3")]42 #[clap(long)]43 line_padding: usize,43 line_padding: Option<usize>,44}44}45impl ConfigureState for ManifestOpts {45impl ConfigureState for ManifestOpts {46 fn configure(&self, state: &EvaluationState) -> Result<()> {46 fn configure(&self, state: &EvaluationState) -> Result<()> {50 match self.format {50 match self.format {51 ManifestFormatName::String => state.set_manifest_format(ManifestFormat::String),51 ManifestFormatName::String => state.set_manifest_format(ManifestFormat::String),52 ManifestFormatName::Json => {52 ManifestFormatName::Json => {53 state.set_manifest_format(ManifestFormat::Json(self.line_padding))53 state.set_manifest_format(ManifestFormat::Json(self.line_padding.unwrap_or(3)))54 }54 }55 ManifestFormatName::Yaml => {55 ManifestFormatName::Yaml => {56 state.set_manifest_format(ManifestFormat::Yaml(self.line_padding))56 state.set_manifest_format(ManifestFormat::Yaml(self.line_padding.unwrap_or(2)))57 }57 }58 }58 }59 }59 }crates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth157 buf.push('"');157 buf.push('"');158}158}159160pub struct ManifestYamlOptions<'s> {161 pub padding: &'s str,162 pub pad_arrays: bool,163}164165pub fn manifest_yaml_ex(val: &Val, options: &ManifestYamlOptions<'_>) -> Result<String> {166 let mut out = String::new();167 manifest_yaml_ex_buf(val, &mut out, &mut String::new(), options)?;168 Ok(out)169}170fn manifest_yaml_ex_buf(171 val: &Val,172 buf: &mut String,173 cur_padding: &mut String,174 options: &ManifestYamlOptions<'_>,175) -> Result<()> {176 use std::fmt::Write;177 match val {178 Val::Bool(v) => {179 if *v {180 buf.push_str("true")181 } else {182 buf.push_str("false")183 }184 }185 Val::Null => buf.push_str("null"),186 Val::Str(s) => {187 if s.is_empty() {188 buf.push_str("\"\"");189 } else if let Some(s) = s.strip_suffix('\n') {190 buf.push('|');191 for line in s.split('\n') {192 buf.push('\n');193 buf.push_str(options.padding);194 buf.push_str(line);195 }196 } else {197 escape_string_json_buf(s, buf)198 }199 }200 Val::Num(n) => write!(buf, "{}", *n).unwrap(),201 Val::Arr(a) => {202 if a.is_empty() {203 buf.push_str("[]");204 } else {205 for (i, item) in a.iter().enumerate() {206 if i != 0 {207 buf.push('\n');208 buf.push_str(cur_padding);209 }210 let item = item?;211 buf.push('-');212 if let Val::Arr(a) = &item {213 if !a.is_empty() {214 buf.push('\n');215 buf.push_str(cur_padding);216 buf.push_str(options.padding);217 } else {218 buf.push(' ');219 }220 } else {221 buf.push(' ');222 }223 let extra_padding = if let Val::Arr(a) = &item {224 !a.is_empty()225 } else if let Val::Obj(a) = &item {226 !a.is_empty()227 } else {228 false229 };230 let prev_len = cur_padding.len();231 if extra_padding {232 cur_padding.push_str(options.padding);233 }234 manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;235 cur_padding.truncate(prev_len);236 }237 }238 }239 Val::Obj(o) => {240 if o.is_empty() {241 buf.push_str("{}");242 } else {243 for (i, key) in o.fields().iter().enumerate() {244 if i != 0 {245 buf.push('\n');246 buf.push_str(cur_padding);247 }248 escape_string_json_buf(key, buf);249 buf.push(':');250 let item = o.get(key.clone())?.expect("field exists");251 if let Val::Arr(a) = &item {252 if !a.is_empty() {253 buf.push('\n');254 buf.push_str(cur_padding);255 if options.pad_arrays {256 buf.push_str(options.padding);257 }258 } else {259 buf.push(' ');260 }261 } else if let Val::Obj(o) = &item {262 if !o.is_empty() {263 buf.push('\n');264 buf.push_str(cur_padding);265 buf.push_str(options.padding);266 } else {267 buf.push(' ');268 }269 } else {270 buf.push(' ');271 }272 let prev_len = cur_padding.len();273 if let Val::Arr(a) = &item {274 if !a.is_empty() && options.pad_arrays {275 cur_padding.push_str(options.padding);276 }277 } else if let Val::Obj(a) = &item {278 if !a.is_empty() {279 cur_padding.push_str(options.padding);280 }281 };282 manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;283 cur_padding.truncate(prev_len);284 }285 }286 }287 Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),288 }289 Ok(())290}159291crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth1use crate::{1use crate::{2 builtin::manifest::{manifest_yaml_ex, ManifestYamlOptions},2 equals,3 equals,3 error::{Error::*, Result},4 error::{Error::*, Result},4 operator::evaluate_mod_op,5 operator::evaluate_mod_op,121 ("join".into(), builtin_join),122 ("join".into(), builtin_join),122 ("escapeStringJson".into(), builtin_escape_string_json),123 ("escapeStringJson".into(), builtin_escape_string_json),123 ("manifestJsonEx".into(), builtin_manifest_json_ex),124 ("manifestJsonEx".into(), builtin_manifest_json_ex),125 ("manifestYamlDocImpl".into(), builtin_manifest_yaml_doc),124 ("reverse".into(), builtin_reverse),126 ("reverse".into(), builtin_reverse),125 ("id".into(), builtin_id),127 ("id".into(), builtin_id),126 ("strReplace".into(), builtin_str_replace),128 ("strReplace".into(), builtin_str_replace),768 })770 })769}771}772773fn builtin_manifest_yaml_doc(774 context: Context,775 _loc: Option<&ExprLocation>,776 args: &ArgsDesc,777) -> Result<Val> {778 parse_args!(context, "manifestYamlDoc", args, 2, [779 0, value: ty!(any);780 1, indent_array_in_object: ty!(boolean) => Val::Bool;781 ], {782 Ok(Val::Str(manifest_yaml_ex(&value, &ManifestYamlOptions {783 padding: " ",784 pad_arrays: indent_array_in_object,785 })?.into()))786 })787}770788771fn builtin_reverse(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {789fn builtin_reverse(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {772 parse_args!(context, "reverse", args, 1, [790 parse_args!(context, "reverse", args, 1, [794 1, from: ty!(string) => Val::Str;812 1, from: ty!(string) => Val::Str;795 2, to: ty!(string) => Val::Str;813 2, to: ty!(string) => Val::Str;796 ], {814 ], {797 let mut out = String::new();815 Ok(Val::Str(str.replace(&from as &str, &to as &str).into()))798 let mut last_idx = 0;799 while let Some(idx) = (&str[last_idx..]).find(&from as &str) {800 out.push_str(&str[last_idx..last_idx+idx]);801 out.push_str(&to);802 last_idx += idx + from.len();803 }804 if last_idx == 0 {805 return Ok(Val::Str(str))806 }807 out.push_str(&str[last_idx..]);808 Ok(Val::Str(out.into()))809 })816 })810}817}811818crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth105 }))105 }))106 }106 }107108 pub fn is_empty(&self) -> bool {109 if !self.0.this_entries.is_empty() {110 return false;111 }112 self.0113 .super_obj114 .as_ref()115 .map(|s| s.is_empty())116 .unwrap_or(true)117 }107118108 /// Run callback for every field found in object119 /// Run callback for every field found in object109 pub(crate) fn enum_fields(&self, handler: &mut impl FnMut(&IStr, &Visibility) -> bool) -> bool {120 pub(crate) fn enum_fields(&self, handler: &mut impl FnMut(&IStr, &Visibility) -> bool) -> bool {crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth2 builtin::{2 builtin::{3 call_builtin,3 call_builtin,4 manifest::{manifest_json_ex, ManifestJsonOptions, ManifestType},4 manifest::{5 manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType,6 ManifestYamlOptions,7 },5 },8 },6 error::{Error::*, LocError},9 error::{Error::*, LocError},7 evaluate,10 evaluate,8 function::{parse_function_call, parse_function_call_map, place_args},11 function::{parse_function_call, parse_function_call_map, place_args},9 native::NativeCallback,12 native::NativeCallback,10 throw, with_state, Context, ObjValue, Result,13 throw, Context, ObjValue, Result,11};14};12use jrsonnet_gc::{Gc, GcCell, Trace};15use jrsonnet_gc::{Gc, GcCell, Trace};13use jrsonnet_interner::IStr;16use jrsonnet_interner::IStr;14use jrsonnet_parser::{el, ArgsDesc, Expr, ExprLocation, LiteralType, LocExpr, ParamsDesc};17use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};15use jrsonnet_types::ValType;18use jrsonnet_types::ValType;16use std::{collections::HashMap, fmt::Debug, rc::Rc};19use std::{collections::HashMap, fmt::Debug, rc::Rc};1720393 pub fn unwrap_num(self) -> Result<f64> {396 pub fn unwrap_num(self) -> Result<f64> {394 Ok(matches_unwrap!(self, Self::Num(v), v))397 Ok(matches_unwrap!(self, Self::Num(v), v))395 }398 }399 pub fn unwrap_str(self) -> Result<IStr> {400 Ok(matches_unwrap!(self, Self::Str(v), v))401 }402 pub fn unwrap_arr(self) -> Result<ArrValue> {403 Ok(matches_unwrap!(self, Self::Arr(v), v))404 }396 pub fn unwrap_func(self) -> Result<Gc<FuncVal>> {405 pub fn unwrap_func(self) -> Result<Gc<FuncVal>> {397 Ok(matches_unwrap!(self, Self::Func(v), v))406 Ok(matches_unwrap!(self, Self::Func(v), v))398 }407 }544 }553 }545554546 pub fn to_yaml(&self, padding: usize) -> Result<IStr> {555 pub fn to_yaml(&self, padding: usize) -> Result<IStr> {547 with_state(|s| {556 manifest_yaml_ex(548 let ctx = s557 self,549 .create_default_context()558 &ManifestYamlOptions {550 .with_var("__tmp__to_json__".into(), self.clone());551 evaluate(552 ctx,553 &el!(Expr::Apply(559 padding: &" ".repeat(padding),554 el!(Expr::Index(560 pad_arrays: true,555 el!(Expr::Var("std".into())),561 },556 el!(Expr::Str("manifestYamlDoc".into()))562 )557 )),563 .map(|s| s.into())558 ArgsDesc::new(559 vec![560 el!(Expr::Var("__tmp__to_json__".into())),561 el!(Expr::Literal(if padding != 0 {562 LiteralType::True563 } else {564 LiteralType::False565 })),566 ],567 vec![]568 ),569 false570 )),571 )?572 .try_cast_str("to json")573 })574 }564 }575 pub fn into_indexable(self) -> Result<IndexableVal> {565 pub fn into_indexable(self) -> Result<IndexableVal> {576 Ok(match self {566 Ok(match self {crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth374374375 manifestJsonEx:: $intrinsic(manifestJsonEx),375 manifestJsonEx:: $intrinsic(manifestJsonEx),376376377 manifestYamlDoc(value, indent_array_in_object=false)::377 manifestYamlDocImpl:: $intrinsic(manifestYamlDocImpl),378 local aux(v, path, cindent) =378379 if v == true then379 manifestYamlDoc(value, indent_array_in_object=false):: std.manifestYamlDocImpl(value, indent_array_in_object),380 'true'381 else if v == false then382 'false'383 else if v == null then384 'null'385 else if std.isNumber(v) then386 '' + v387 else if std.isString(v) then388 local len = std.length(v);389 if len == 0 then390 '""'391 else if v[len - 1] == '\n' then392 local split = std.split(v, '\n');393 std.join('\n' + cindent + ' ', ['|'] + split[0:std.length(split) - 1])394 else395 std.escapeStringJson(v)396 else if std.isFunction(v) then397 error 'Tried to manifest function at ' + path398 else if std.isArray(v) then399 if std.length(v) == 0 then400 '[]'401 else402 local params(value) =403 if std.isArray(value) && std.length(value) > 0 then {404 // While we could avoid the new line, it yields YAML that is405 // hard to read, e.g.:406 // - - - 1407 // - 2408 // - - 3409 // - 4410 new_indent: cindent + ' ',411 space: '\n' + self.new_indent,412 } else if std.isObject(value) && std.length(value) > 0 then {413 new_indent: cindent + ' ',414 // In this case we can start on the same line as the - because the indentation415 // matches up then. The converse is not true, because fields are not always416 // 1 character long.417 space: ' ',418 } else {419 // In this case, new_indent is only used in the case of multi-line strings.420 new_indent: cindent,421 space: ' ',422 };423 local range = std.range(0, std.length(v) - 1);424 local parts = [425 '-' + param.space + aux(v[i], path + [i], param.new_indent)426 for i in range427 for param in [params(v[i])]428 ];429 std.join('\n' + cindent, parts)430 else if std.isObject(v) then431 if std.length(v) == 0 then432 '{}'433 else434 local params(value) =435 if std.isArray(value) && std.length(value) > 0 then {436 // Not indenting allows e.g.437 // ports:438 // - 80439 // instead of440 // ports:441 // - 80442 new_indent: if indent_array_in_object then cindent + ' ' else cindent,443 space: '\n' + self.new_indent,444 } else if std.isObject(value) && std.length(value) > 0 then {445 new_indent: cindent + ' ',446 space: '\n' + self.new_indent,447 } else {448 // In this case, new_indent is only used in the case of multi-line strings.449 new_indent: cindent,450 space: ' ',451 };452 local lines = [453 std.escapeStringJson(k) + ':' + param.space + aux(v[k], path + [k], param.new_indent)454 for k in std.objectFields(v)455 for param in [params(v[k])]456 ];457 std.join('\n' + cindent, lines);458 aux(value, [], ''),459380460 manifestYamlStream(value, indent_array_in_object=false, c_document_end=true)::381 manifestYamlStream(value, indent_array_in_object=false, c_document_end=true)::461 if !std.isArray(value) then382 if !std.isArray(value) then