12345678use alejandra::config::Indentation;9use linked_hash_map::LinkedHashMap;10use peg::str::LineCol;11use se_impl::MySerialize;12use serde::{Deserialize, Serialize};1314mod de_impl;15mod se_impl;16mod to_string;1718pub use to_string::escape_string;1920#[derive(thiserror::Error, Debug)]21pub enum Error {22 #[error("bad number")]23 BadNumber,24 #[error("expected {0}")]25 Expected(&'static str),26 #[error("parse error")]27 ParseError(#[from] peg::error::ParseError<LineCol>),28 #[error("{0}")]29 Custom(String),30 #[error("io: {0}")]31 Io(#[from] std::io::Error),32 #[error("fmt: {0}")]33 Fmt(#[from] std::fmt::Error),34}3536#[derive(Debug)]37pub enum Value {38 Number(i64),39 String(String),40 Boolean(bool),41 Object(LinkedHashMap<String, Value>),42 Array(Vec<Value>),43 Null,44}4546fn count_spaces(l: &str) -> usize {47 l.chars().take_while(|&c| c == ' ').count()48}49fn is_significant(l: &str) -> bool {50 count_spaces(l) != l.len()51}5253fn dedent(l: &str, by: usize) -> &str {54 assert!(55 l[0..by.min(l.len())].chars().all(|c| c == ' '),56 "dedent calculation is wrong"57 );58 &l[by.min(l.len())..]59}6061fn process_multiline(lines: Vec<&str>) -> String {62 63 64 let dedent_by = lines65 .iter()66 .copied()67 .filter(|c| is_significant(c))68 .map(count_spaces)69 .min()70 .unwrap_or(0);7172 let mut out = String::new();7374 let mut had_first = false;75 for (i, line) in lines.into_iter().enumerate() {76 77 if i == 0 && !is_significant(line) {78 continue;79 }80 if had_first {81 out.push('\n');82 }83 had_first = true;84 85 for (i, part) in dedent(line, dedent_by).split("'''").enumerate() {86 if i != 0 {87 out.push_str(r#"""""#);88 }89 90 out.push_str(&part.replace("''${", "${").replace("''\\t", "\t"));91 }92 }9394 out95}9697peg::parser! {98pub grammar nixlike() for str {99 rule number() -> i64100 = quiet! { v:$(['0'..='9' | '+' | '-']+) {? v.parse().map_err(|_| "<number>")} } / expected!("<number>")101 rule string_char() -> &'input str102 = "\\\"" { "\"" }103 / "\\\\" { "\\" }104 / "\\n" { "\n" }105 / "\\t" { "\t" }106 / "\\r" { "\r" }107 / "\\$" { "$" }108 / c:$([_]) { c }109 rule string() -> String = singleline_string() / multiline_string();110 rule singleline_string() -> String111 = quiet! { "\"" v:(!"\"" c:string_char() {c})* "\"" { v.into_iter().collect() } } / expected!("<string>")112 pub rule multiline_string() -> String113 = "''"114 115 116 lines:$(("'''" / !"''" [_])*) "''"117 {118 process_multiline(lines.split('\n').collect())119 }120 rule boolean() -> bool121 = quiet! { "true" {true}122 / "false" {false} } / expected!("<boolean>")123 rule indent() -> String124 = quiet! {125 s:$(['a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-']+) { s.to_owned() }126 / "\"" s:$(['a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' | '.']+) "\"" { s.to_owned() }127 } / expected!("<identifier>")128 rule object() -> LinkedHashMap<String, Value>129 = "{" _130 e:(k:indent()++(_ "." _) _ "=" _ v:value() _ ";" _ {(k, v)})*131 "}" {?132 let mut out = LinkedHashMap::new();133 for (k, v) in e {134 let mut map = &mut out;135 for v in k.iter().take(k.len() - 1) {136 map = match map.entry(v.clone()).or_insert_with(|| Value::Object(Default::default())) {137 Value::Object(v) => v,138 _ => return Err("expected object"),139 }140 }141142 let key = k.into_iter().next_back().unwrap();143 if map.contains_key(&key) {144 return Err("can't override object");145 }146 map.insert(key, v);147 }148 Ok(out)149 }150151 rule array() -> Vec<Value>152 = "[" _ v:value()**_ _ "]" {v}153154 rule value() -> Value155 = o:object() { Value::Object(o) }156 / a:array() { Value::Array(a) }157 / s:string() { Value::String(s) }158 / "null" { Value::Null }159 / b:boolean() { Value::Boolean(b) }160 / n:number() { Value::Number(n) }161162 pub rule root() -> Value163 = _ v:value() _ { v }164165 rule _()166 = ( quiet!{ [' ' | '\t' | '\n']+ }167 / "#" (!['\n'] [_])* "\n" )*168}169}170171pub fn parse_str<'de, D: Deserialize<'de>>(s: &str) -> Result<D, Error> {172 let value = nixlike::root(s)?;173 D::deserialize(value)174}175176pub fn parse_value<'de, D: Deserialize<'de>>(value: Value) -> Result<D, Error> {177 D::deserialize(value)178}179180pub fn serialize_value_pretty(value: Value) -> String {181 to_string::write_nix(&value)182}183184pub fn serialize<S: Serialize>(value: S) -> Result<String, Error> {185 let value: Value = value.serialize(MySerialize)?;186 Ok(serialize_value_pretty(value))187}188189pub fn format_identifier(i: &str) -> String {190 let mut out = String::new();191 to_string::write_identifier(i, &mut out);192 out193}194195#[test]196fn test() {197 assert_eq!(serialize("Hello\nworld").unwrap(), "\"Hello\\nworld\"\n");198}199pub fn format_nix(value: &String) -> String {200 let (_, out) = alejandra::format::in_memory(201 "".to_owned(),202 value.to_owned(),203 alejandra::config::Config {204 indentation: Indentation::TwoSpaces,205 },206 );207 out208}209210#[test]211fn parse_multiline() {212 213 assert_eq!(nixlike::multiline_string("''\n''").expect("parse"), "");214 215 assert_eq!(nixlike::multiline_string("''\n\n''").expect("parse"), "\n");216 217 assert_eq!(nixlike::multiline_string("''t\n''").expect("parse"), "t\n");218 219 assert_eq!(nixlike::multiline_string("''''").expect("parse"), "");220 221 222 assert_eq!(nixlike::multiline_string("'' ''").expect("parse"), "");223}