difftreelog
Merge pull request #64 from CertainLach/feat/manifest-yaml-doc-builtin
in: master
Make manifestYamlDoc builtin
6 files changed
crates/jrsonnet-cli/src/manifest.rsdiffbeforeafterboth--- a/crates/jrsonnet-cli/src/manifest.rs
+++ b/crates/jrsonnet-cli/src/manifest.rs
@@ -38,9 +38,9 @@
#[clap(long, short = 'y')]
yaml_stream: bool,
/// Number of spaces to pad output manifest with.
- /// `0` for hard tabs, `-1` for single line output
- #[clap(long, default_value = "3")]
- line_padding: usize,
+ /// `0` for hard tabs, `-1` for single line output [default: 3 for json, 2 for yaml]
+ #[clap(long)]
+ line_padding: Option<usize>,
}
impl ConfigureState for ManifestOpts {
fn configure(&self, state: &EvaluationState) -> Result<()> {
@@ -50,10 +50,10 @@
match self.format {
ManifestFormatName::String => state.set_manifest_format(ManifestFormat::String),
ManifestFormatName::Json => {
- state.set_manifest_format(ManifestFormat::Json(self.line_padding))
+ state.set_manifest_format(ManifestFormat::Json(self.line_padding.unwrap_or(3)))
}
ManifestFormatName::Yaml => {
- state.set_manifest_format(ManifestFormat::Yaml(self.line_padding))
+ state.set_manifest_format(ManifestFormat::Yaml(self.line_padding.unwrap_or(2)))
}
}
}
crates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs
@@ -156,3 +156,127 @@
}
buf.push('"');
}
+
+pub struct ManifestYamlOptions<'s> {
+ /// Padding before fields, i.e
+ /// ```yaml
+ /// a:
+ /// b:
+ /// ## <- this
+ /// ```
+ pub padding: &'s str,
+ /// Padding before array elements in objects
+ /// ```yaml
+ /// a:
+ /// - 1
+ /// ## <- this
+ /// ```
+ pub arr_element_padding: &'s str,
+}
+
+pub fn manifest_yaml_ex(val: &Val, options: &ManifestYamlOptions<'_>) -> Result<String> {
+ let mut out = String::new();
+ manifest_yaml_ex_buf(val, &mut out, &mut String::new(), options)?;
+ Ok(out)
+}
+fn manifest_yaml_ex_buf(
+ val: &Val,
+ buf: &mut String,
+ cur_padding: &mut String,
+ options: &ManifestYamlOptions<'_>,
+) -> Result<()> {
+ use std::fmt::Write;
+ match val {
+ Val::Bool(v) => {
+ if *v {
+ buf.push_str("true")
+ } else {
+ buf.push_str("false")
+ }
+ }
+ Val::Null => buf.push_str("null"),
+ Val::Str(s) => {
+ if s.is_empty() {
+ buf.push_str("\"\"");
+ } else if let Some(s) = s.strip_suffix('\n') {
+ buf.push('|');
+ for line in s.split('\n') {
+ buf.push('\n');
+ buf.push_str(options.padding);
+ buf.push_str(line);
+ }
+ } else {
+ escape_string_json_buf(s, buf)
+ }
+ }
+ Val::Num(n) => write!(buf, "{}", *n).unwrap(),
+ Val::Arr(a) => {
+ if a.is_empty() {
+ buf.push_str("[]");
+ } else {
+ for (i, item) in a.iter().enumerate() {
+ if i != 0 {
+ buf.push('\n');
+ buf.push_str(cur_padding);
+ }
+ let item = item?;
+ buf.push('-');
+ match &item {
+ Val::Arr(a) if !a.is_empty() => {
+ buf.push('\n');
+ buf.push_str(cur_padding);
+ buf.push_str(options.padding);
+ }
+ _ => buf.push(' '),
+ }
+ let extra_padding = match &item {
+ Val::Arr(a) => !a.is_empty(),
+ Val::Obj(o) => !o.is_empty(),
+ _ => false,
+ };
+ let prev_len = cur_padding.len();
+ if extra_padding {
+ cur_padding.push_str(options.padding);
+ }
+ manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;
+ cur_padding.truncate(prev_len);
+ }
+ }
+ }
+ Val::Obj(o) => {
+ if o.is_empty() {
+ buf.push_str("{}");
+ } else {
+ for (i, key) in o.fields().iter().enumerate() {
+ if i != 0 {
+ buf.push('\n');
+ buf.push_str(cur_padding);
+ }
+ escape_string_json_buf(key, buf);
+ buf.push(':');
+ let prev_len = cur_padding.len();
+ let item = o.get(key.clone())?.expect("field exists");
+ match &item {
+ Val::Arr(a) if !a.is_empty() => {
+ buf.push('\n');
+ buf.push_str(cur_padding);
+ buf.push_str(options.arr_element_padding);
+ cur_padding.push_str(options.arr_element_padding);
+ }
+ Val::Obj(o) if !o.is_empty() => {
+ buf.push('\n');
+ buf.push_str(cur_padding);
+ buf.push_str(options.padding);
+ cur_padding.push_str(options.padding);
+ }
+ _ => buf.push(' '),
+ }
+ manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;
+ cur_padding.truncate(prev_len);
+ }
+ }
+ }
+ Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),
+ }
+ Ok(())
+}
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -1,4 +1,5 @@
use crate::{
+ builtin::manifest::{manifest_yaml_ex, ManifestYamlOptions},
equals,
error::{Error::*, Result},
operator::evaluate_mod_op,
@@ -121,6 +122,7 @@
("join".into(), builtin_join),
("escapeStringJson".into(), builtin_escape_string_json),
("manifestJsonEx".into(), builtin_manifest_json_ex),
+ ("manifestYamlDocImpl".into(), builtin_manifest_yaml_doc),
("reverse".into(), builtin_reverse),
("id".into(), builtin_id),
("strReplace".into(), builtin_str_replace),
@@ -768,6 +770,22 @@
})
}
+fn builtin_manifest_yaml_doc(
+ context: Context,
+ _loc: Option<&ExprLocation>,
+ args: &ArgsDesc,
+) -> Result<Val> {
+ parse_args!(context, "manifestYamlDoc", args, 2, [
+ 0, value: ty!(any);
+ 1, indent_array_in_object: ty!(boolean) => Val::Bool;
+ ], {
+ Ok(Val::Str(manifest_yaml_ex(&value, &ManifestYamlOptions {
+ padding: " ",
+ arr_element_padding: if indent_array_in_object { " " } else { "" },
+ })?.into()))
+ })
+}
+
fn builtin_reverse(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "reverse", args, 1, [
0, value: ty!(array) => Val::Arr;
@@ -794,18 +812,7 @@
1, from: ty!(string) => Val::Str;
2, to: ty!(string) => Val::Str;
], {
- let mut out = String::new();
- let mut last_idx = 0;
- while let Some(idx) = (&str[last_idx..]).find(&from as &str) {
- out.push_str(&str[last_idx..last_idx+idx]);
- out.push_str(&to);
- last_idx += idx + from.len();
- }
- if last_idx == 0 {
- return Ok(Val::Str(str))
- }
- out.push_str(&str[last_idx..]);
- Ok(Val::Str(out.into()))
+ Ok(Val::Str(str.replace(&from as &str, &to as &str).into()))
})
}
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth1use crate::operator::evaluate_add_op;2use crate::{Bindable, LazyBinding, LazyVal, Result, Val};3use jrsonnet_gc::{Gc, GcCell, Trace};4use jrsonnet_interner::IStr;5use jrsonnet_parser::{ExprLocation, Visibility};6use rustc_hash::{FxHashMap, FxHashSet, FxHasher};7use std::collections::HashMap;8use std::hash::{Hash, Hasher};9use std::{fmt::Debug, hash::BuildHasherDefault};1011#[derive(Debug, Trace)]12#[trivially_drop]13pub struct ObjMember {14 pub add: bool,15 pub visibility: Visibility,16 pub invoke: LazyBinding,17 pub location: Option<ExprLocation>,18}1920pub trait ObjectAssertion: Trace {21 fn run(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<()>;22}2324// Field => This25type CacheKey = (IStr, ObjValue);26#[derive(Trace)]27#[trivially_drop]28pub struct ObjValueInternals {29 super_obj: Option<ObjValue>,30 assertions: Gc<Vec<Box<dyn ObjectAssertion>>>,31 assertions_ran: GcCell<FxHashSet<ObjValue>>,32 this_obj: Option<ObjValue>,33 this_entries: Gc<FxHashMap<IStr, ObjMember>>,34 value_cache: GcCell<FxHashMap<CacheKey, Option<Val>>>,35}3637#[derive(Clone, Trace)]38#[trivially_drop]39pub struct ObjValue(pub(crate) Gc<ObjValueInternals>);40impl Debug for ObjValue {41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {42 if let Some(super_obj) = self.0.super_obj.as_ref() {43 if f.alternate() {44 write!(f, "{:#?}", super_obj)?;45 } else {46 write!(f, "{:?}", super_obj)?;47 }48 write!(f, " + ")?;49 }50 let mut debug = f.debug_struct("ObjValue");51 for (name, member) in self.0.this_entries.iter() {52 debug.field(name, member);53 }54 #[cfg(feature = "unstable")]55 {56 debug.finish_non_exhaustive()57 }58 #[cfg(not(feature = "unstable"))]59 {60 debug.finish()61 }62 }63}6465impl ObjValue {66 pub fn new(67 super_obj: Option<Self>,68 this_entries: Gc<FxHashMap<IStr, ObjMember>>,69 assertions: Gc<Vec<Box<dyn ObjectAssertion>>>,70 ) -> Self {71 Self(Gc::new(ObjValueInternals {72 super_obj,73 assertions,74 assertions_ran: GcCell::new(FxHashSet::default()),75 this_obj: None,76 this_entries,77 value_cache: GcCell::new(FxHashMap::default()),78 }))79 }80 pub fn new_empty() -> Self {81 Self::new(None, Gc::new(FxHashMap::default()), Gc::new(Vec::new()))82 }83 pub fn extend_from(&self, super_obj: Self) -> Self {84 match &self.0.super_obj {85 None => Self::new(86 Some(super_obj),87 self.0.this_entries.clone(),88 self.0.assertions.clone(),89 ),90 Some(v) => Self::new(91 Some(v.extend_from(super_obj)),92 self.0.this_entries.clone(),93 self.0.assertions.clone(),94 ),95 }96 }97 pub fn with_this(&self, this_obj: Self) -> Self {98 Self(Gc::new(ObjValueInternals {99 super_obj: self.0.super_obj.clone(),100 assertions: self.0.assertions.clone(),101 assertions_ran: GcCell::new(FxHashSet::default()),102 this_obj: Some(this_obj),103 this_entries: self.0.this_entries.clone(),104 value_cache: GcCell::new(FxHashMap::default()),105 }))106 }107108 /// Run callback for every field found in object109 pub(crate) fn enum_fields(&self, handler: &mut impl FnMut(&IStr, &Visibility) -> bool) -> bool {110 if let Some(s) = &self.0.super_obj {111 if s.enum_fields(handler) {112 return true;113 }114 }115 for (name, member) in self.0.this_entries.iter() {116 if handler(name, &member.visibility) {117 return true;118 }119 }120 false121 }122123 pub fn fields_visibility(&self) -> FxHashMap<IStr, bool> {124 let mut out = FxHashMap::default();125 self.enum_fields(&mut |name, visibility| {126 match visibility {127 Visibility::Normal => {128 let entry = out.entry(name.to_owned());129 entry.or_insert(true);130 }131 Visibility::Hidden => {132 out.insert(name.to_owned(), false);133 }134 Visibility::Unhide => {135 out.insert(name.to_owned(), true);136 }137 };138 false139 });140 out141 }142 pub fn fields_ex(&self, include_hidden: bool) -> Vec<IStr> {143 let mut fields: Vec<_> = self144 .fields_visibility()145 .into_iter()146 .filter(|(_k, v)| include_hidden || *v)147 .map(|(k, _)| k)148 .collect();149 fields.sort_unstable();150 fields151 }152 pub fn fields(&self) -> Vec<IStr> {153 self.fields_ex(false)154 }155156 pub fn field_visibility(&self, name: IStr) -> Option<Visibility> {157 if let Some(m) = self.0.this_entries.get(&name) {158 Some(match &m.visibility {159 Visibility::Normal => self160 .0161 .super_obj162 .as_ref()163 .and_then(|super_obj| super_obj.field_visibility(name))164 .unwrap_or(Visibility::Normal),165 v => *v,166 })167 } else if let Some(super_obj) = &self.0.super_obj {168 super_obj.field_visibility(name)169 } else {170 None171 }172 }173174 fn has_field_include_hidden(&self, name: IStr) -> bool {175 if self.0.this_entries.contains_key(&name) {176 true177 } else if let Some(super_obj) = &self.0.super_obj {178 super_obj.has_field_include_hidden(name)179 } else {180 false181 }182 }183184 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {185 if include_hidden {186 self.has_field_include_hidden(name)187 } else {188 self.has_field(name)189 }190 }191 pub fn has_field(&self, name: IStr) -> bool {192 self.field_visibility(name)193 .map(|v| v.is_visible())194 .unwrap_or(false)195 }196197 pub fn get(&self, key: IStr) -> Result<Option<Val>> {198 self.run_assertions()?;199 self.get_raw(key, self.0.this_obj.as_ref())200 }201202 pub fn extend_with_field(self, key: IStr, value: ObjMember) -> Self {203 let mut new = FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());204 new.insert(key, value);205 Self::new(Some(self), Gc::new(new), Gc::new(Vec::new()))206 }207208 fn get_raw(&self, key: IStr, real_this: Option<&Self>) -> Result<Option<Val>> {209 let real_this = real_this.unwrap_or(self);210 let cache_key = (key.clone(), real_this.clone());211212 if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {213 return Ok(v.clone());214 }215 let value = match (self.0.this_entries.get(&key), &self.0.super_obj) {216 (Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),217 (Some(k), Some(s)) => {218 let our = self.evaluate_this(k, real_this)?;219 if k.add {220 s.get_raw(key, Some(real_this))?221 .map_or(Ok(Some(our.clone())), |v| {222 Ok(Some(evaluate_add_op(&v, &our)?))223 })224 } else {225 Ok(Some(our))226 }227 }228 (None, Some(s)) => s.get_raw(key, Some(real_this)),229 (None, None) => Ok(None),230 }?;231 self.0232 .value_cache233 .borrow_mut()234 .insert(cache_key, value.clone());235 Ok(value)236 }237 fn evaluate_this(&self, v: &ObjMember, real_this: &Self) -> Result<Val> {238 v.invoke239 .evaluate(Some(real_this.clone()), self.0.super_obj.clone())?240 .evaluate()241 }242243 fn run_assertions_raw(&self, real_this: &Self) -> Result<()> {244 if self.0.assertions_ran.borrow_mut().insert(real_this.clone()) {245 for assertion in self.0.assertions.iter() {246 if let Err(e) = assertion.run(Some(real_this.clone()), self.0.super_obj.clone()) {247 self.0.assertions_ran.borrow_mut().remove(real_this);248 return Err(e);249 }250 }251 if let Some(super_obj) = &self.0.super_obj {252 super_obj.run_assertions_raw(real_this)?;253 }254 }255 Ok(())256 }257 pub fn run_assertions(&self) -> Result<()> {258 self.run_assertions_raw(self)259 }260261 pub fn ptr_eq(a: &Self, b: &Self) -> bool {262 Gc::ptr_eq(&a.0, &b.0)263 }264}265266impl PartialEq for ObjValue {267 fn eq(&self, other: &Self) -> bool {268 Gc::ptr_eq(&self.0, &other.0)269 }270}271272impl Eq for ObjValue {}273impl Hash for ObjValue {274 fn hash<H: Hasher>(&self, hasher: &mut H) {275 hasher.write_usize(&*self.0 as *const _ as usize)276 }277}278279pub struct ObjValueBuilder {280 super_obj: Option<ObjValue>,281 map: FxHashMap<IStr, ObjMember>,282 assertions: Vec<Box<dyn ObjectAssertion>>,283}284impl ObjValueBuilder {285 pub fn new() -> Self {286 Self::with_capacity(0)287 }288 pub fn with_capacity(capacity: usize) -> Self {289 Self {290 super_obj: None,291 map: HashMap::with_capacity_and_hasher(292 capacity,293 BuildHasherDefault::<FxHasher>::default(),294 ),295 assertions: Vec::new(),296 }297 }298 pub fn reserve_asserts(&mut self, capacity: usize) -> &mut Self {299 self.assertions.reserve_exact(capacity);300 self301 }302 pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {303 self.super_obj = Some(super_obj);304 self305 }306307 pub fn assert(&mut self, assertion: Box<dyn ObjectAssertion>) -> &mut Self {308 self.assertions.push(assertion);309 self310 }311 pub fn member(&mut self, name: IStr) -> ObjMemberBuilder {312 ObjMemberBuilder {313 value: self,314 name,315 add: false,316 visibility: Visibility::Normal,317 location: None,318 }319 }320321 pub fn build(self) -> ObjValue {322 ObjValue::new(self.super_obj, Gc::new(self.map), Gc::new(self.assertions))323 }324}325impl Default for ObjValueBuilder {326 fn default() -> Self {327 Self::with_capacity(0)328 }329}330331#[must_use = "value not added unless binding() was called"]332pub struct ObjMemberBuilder<'v> {333 value: &'v mut ObjValueBuilder,334 name: IStr,335 add: bool,336 visibility: Visibility,337 location: Option<ExprLocation>,338}339340#[allow(clippy::missing_const_for_fn)]341impl<'v> ObjMemberBuilder<'v> {342 pub const fn with_add(mut self, add: bool) -> Self {343 self.add = add;344 self345 }346 pub fn add(self) -> Self {347 self.with_add(true)348 }349 pub fn with_visibility(mut self, visibility: Visibility) -> Self {350 self.visibility = visibility;351 self352 }353 pub fn hide(self) -> Self {354 self.with_visibility(Visibility::Hidden)355 }356 pub fn with_location(mut self, location: Option<ExprLocation>) -> Self {357 self.location = location;358 self359 }360 pub fn value(self, value: Val) -> &'v mut ObjValueBuilder {361 self.binding(LazyBinding::Bound(LazyVal::new_resolved(value)))362 }363 pub fn bindable(self, bindable: Box<dyn Bindable>) -> &'v mut ObjValueBuilder {364 self.binding(LazyBinding::Bindable(Gc::new(bindable)))365 }366 pub fn binding(self, binding: LazyBinding) -> &'v mut ObjValueBuilder {367 self.value.map.insert(368 self.name,369 ObjMember {370 add: self.add,371 visibility: self.visibility,372 invoke: binding,373 location: self.location,374 },375 );376 self.value377 }378}crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -1,17 +1,20 @@
use crate::{
builtin::{
call_builtin,
- manifest::{manifest_json_ex, ManifestJsonOptions, ManifestType},
+ manifest::{
+ manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType,
+ ManifestYamlOptions,
+ },
},
error::{Error::*, LocError},
evaluate,
function::{parse_function_call, parse_function_call_map, place_args},
native::NativeCallback,
- throw, with_state, Context, ObjValue, Result,
+ throw, Context, ObjValue, Result,
};
use jrsonnet_gc::{Gc, GcCell, Trace};
use jrsonnet_interner::IStr;
-use jrsonnet_parser::{el, ArgsDesc, Expr, ExprLocation, LiteralType, LocExpr, ParamsDesc};
+use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};
use jrsonnet_types::ValType;
use std::{collections::HashMap, fmt::Debug, rc::Rc};
@@ -393,6 +396,12 @@
pub fn unwrap_num(self) -> Result<f64> {
Ok(matches_unwrap!(self, Self::Num(v), v))
}
+ pub fn unwrap_str(self) -> Result<IStr> {
+ Ok(matches_unwrap!(self, Self::Str(v), v))
+ }
+ pub fn unwrap_arr(self) -> Result<ArrValue> {
+ Ok(matches_unwrap!(self, Self::Arr(v), v))
+ }
pub fn unwrap_func(self) -> Result<Gc<FuncVal>> {
Ok(matches_unwrap!(self, Self::Func(v), v))
}
@@ -544,33 +553,15 @@
}
pub fn to_yaml(&self, padding: usize) -> Result<IStr> {
- with_state(|s| {
- let ctx = s
- .create_default_context()
- .with_var("__tmp__to_json__".into(), self.clone());
- evaluate(
- ctx,
- &el!(Expr::Apply(
- el!(Expr::Index(
- el!(Expr::Var("std".into())),
- el!(Expr::Str("manifestYamlDoc".into()))
- )),
- ArgsDesc::new(
- vec![
- el!(Expr::Var("__tmp__to_json__".into())),
- el!(Expr::Literal(if padding != 0 {
- LiteralType::True
- } else {
- LiteralType::False
- })),
- ],
- vec![]
- ),
- false
- )),
- )?
- .try_cast_str("to json")
- })
+ let padding = &" ".repeat(padding);
+ manifest_yaml_ex(
+ self,
+ &ManifestYamlOptions {
+ padding,
+ arr_element_padding: padding,
+ },
+ )
+ .map(|s| s.into())
}
pub fn into_indexable(self) -> Result<IndexableVal> {
Ok(match self {
crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -374,88 +374,9 @@
manifestJsonEx:: $intrinsic(manifestJsonEx),
- manifestYamlDoc(value, indent_array_in_object=false)::
- local aux(v, path, cindent) =
- if v == true then
- 'true'
- else if v == false then
- 'false'
- else if v == null then
- 'null'
- else if std.isNumber(v) then
- '' + v
- else if std.isString(v) then
- local len = std.length(v);
- if len == 0 then
- '""'
- else if v[len - 1] == '\n' then
- local split = std.split(v, '\n');
- std.join('\n' + cindent + ' ', ['|'] + split[0:std.length(split) - 1])
- else
- std.escapeStringJson(v)
- else if std.isFunction(v) then
- error 'Tried to manifest function at ' + path
- else if std.isArray(v) then
- if std.length(v) == 0 then
- '[]'
- else
- local params(value) =
- if std.isArray(value) && std.length(value) > 0 then {
- // While we could avoid the new line, it yields YAML that is
- // hard to read, e.g.:
- // - - - 1
- // - 2
- // - - 3
- // - 4
- new_indent: cindent + ' ',
- space: '\n' + self.new_indent,
- } else if std.isObject(value) && std.length(value) > 0 then {
- new_indent: cindent + ' ',
- // In this case we can start on the same line as the - because the indentation
- // matches up then. The converse is not true, because fields are not always
- // 1 character long.
- space: ' ',
- } else {
- // In this case, new_indent is only used in the case of multi-line strings.
- new_indent: cindent,
- space: ' ',
- };
- local range = std.range(0, std.length(v) - 1);
- local parts = [
- '-' + param.space + aux(v[i], path + [i], param.new_indent)
- for i in range
- for param in [params(v[i])]
- ];
- std.join('\n' + cindent, parts)
- else if std.isObject(v) then
- if std.length(v) == 0 then
- '{}'
- else
- local params(value) =
- if std.isArray(value) && std.length(value) > 0 then {
- // Not indenting allows e.g.
- // ports:
- // - 80
- // instead of
- // ports:
- // - 80
- new_indent: if indent_array_in_object then cindent + ' ' else cindent,
- space: '\n' + self.new_indent,
- } else if std.isObject(value) && std.length(value) > 0 then {
- new_indent: cindent + ' ',
- space: '\n' + self.new_indent,
- } else {
- // In this case, new_indent is only used in the case of multi-line strings.
- new_indent: cindent,
- space: ' ',
- };
- local lines = [
- std.escapeStringJson(k) + ':' + param.space + aux(v[k], path + [k], param.new_indent)
- for k in std.objectFields(v)
- for param in [params(v[k])]
- ];
- std.join('\n' + cindent, lines);
- aux(value, [], ''),
+ manifestYamlDocImpl:: $intrinsic(manifestYamlDocImpl),
+
+ manifestYamlDoc(value, indent_array_in_object=false):: std.manifestYamlDocImpl(value, indent_array_in_object),
manifestYamlStream(value, indent_array_in_object=false, c_document_end=true)::
if !std.isArray(value) then