1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
pub mod ast;
pub mod fmt;

use axon_parseast_parser::parse as ap_parse;
use std::convert::TryInto;
use thiserror::Error;

/// If the axon input represents a function, return a `ast::Func`.
///
/// The `axon` argument to this function should be a string containing the
/// output of running `toAxonCode(parseAst( ... ))` in SkySpark.
///
/// # Example
/// ```rust
/// use axon_parser::parse_func;
///
/// let axon = r###"{type:"func", params:[], body:{type:"block", exprs:[{type:"literal", val:"hello world"}]}}"###;
/// let func = parse_func(axon).unwrap();
/// assert!(func.params.len() == 0)
/// ```
///
pub fn parse_func(axon: &str) -> Result<ast::Func, Error> {
    let val = &ap_parse(axon).map_err(|_| Error::Parse)?;
    let func: Result<ast::Func, ()> = val.try_into();
    let func = func.map_err(|_| Error::AstConversion)?;
    Ok(func)
}

/// If the axon input represents a comp, return a `ast::Comp`.
///
/// The `axon` argument to this function should be a string containing the
/// output of running `toAxonCode(parseAst( ... ))` in SkySpark.
///
/// # Example
/// ```rust
/// use axon_parser::parse_comp;
/// use raystack_core::TagName;
///
/// let axon = r###"{type:"compdef", params:[{name:"cells"}], body:{type:"block", exprs:[{type:"var", name:"in"}]}, cells:{in:{defVal:0}}}"###;
/// let comp = parse_comp(axon).unwrap();
/// let key = TagName::new("in".to_owned()).unwrap();
/// assert!(comp.cells.contains_key(&key))
/// ```
///
pub fn parse_comp(axon: &str) -> Result<ast::Comp, Error> {
    let val = &ap_parse(axon).map_err(|_| Error::Parse)?;
    let comp: Result<ast::Comp, ()> = val.try_into();
    let comp = comp.map_err(|_| Error::AstConversion)?;
    Ok(comp)
}

/// Encapsulates the types of errors that can occur in this library.
#[derive(Debug, Error)]
pub enum Error {
    #[error("Could not parse the representation of Axon code")]
    Parse,
    #[error(
        "Could not parse the loosely-typed Axon Val into a strongly-typed AST"
    )]
    AstConversion,
    #[error("Could not rewrite the AST")]
    Rewrite,
}

/// If the axon input represents a function or comp, parse it and return a `String`
/// which contains formatted code.
///
/// The `axon` argument to this function should be a string containing the
/// output of running `<some code>.parseAst.toAxonCode` in SkySpark.
///
/// # Example
/// ```rust
/// use axon_parser::format;
///
/// let desired_width = 80;
/// let axon = r###"{type:"func", params:[], body:{type:"block", exprs:[{type:"literal", val:"hello world"}]}}"###;
/// let formatted_code = format(axon, desired_width).unwrap();
/// let expected_code = "() => do\n    \"hello world\"\nend";
/// assert_eq!(formatted_code, expected_code);
/// ```
///
pub fn format(axon: &str, desired_width: usize) -> Result<String, Error> {
    let func = parse_func(axon);
    if func.is_ok() {
        return format_func(axon, desired_width);
    }

    let comp = parse_comp(axon);
    if comp.is_ok() {
        return format_comp(axon, desired_width);
    }

    Err(Error::Parse)
}

/// If the axon input represents a function, parse it and return a `String`
/// which contains formatted code.
///
/// The `axon` argument to this function should be a string containing the
/// output of running `<some function src>.parseAst.toAxonCode` in SkySpark.
///
/// # Example
/// ```rust
/// use axon_parser::format_func;
///
/// let desired_width = 80;
/// let axon = r###"{type:"func", params:[], body:{type:"block", exprs:[{type:"literal", val:"hello world"}]}}"###;
/// let formatted_code = format_func(axon, desired_width).unwrap();
/// let expected_code = "() => do\n    \"hello world\"\nend";
/// assert_eq!(formatted_code, expected_code);
/// ```
///
pub fn format_func(axon: &str, desired_width: usize) -> Result<String, Error> {
    let func = parse_func(axon)?;
    let context = fmt::Context::new(0, desired_width);
    let widen = true;
    let code = func.default_rewrite(context, widen);
    code.ok_or(Error::Rewrite)
}

/// If the axon input represents a comp, parse it and return a `String`
/// which contains formatted code.
///
/// The `axon` argument to this function should be a string containing the
/// output of running `<some comp src>.parseAst.toAxonCode` in SkySpark.
///
/// # Example
/// ```rust
/// use axon_parser::format_comp;
///
/// let desired_width = 80;
/// let axon = r###"{type:"compdef", params:[{name:"cells"}], body:{type:"block", exprs:[{type:"var", name:"in"}]}, cells:{in:{defVal:0}}}"###;
/// let formatted_code = format_comp(axon, desired_width).unwrap();
/// let expected_code = "defcomp\n    in: {defVal: 0}\n\n    do\n        in\n    end\nend";
/// assert_eq!(formatted_code, expected_code);
/// ```
///
pub fn format_comp(axon: &str, desired_width: usize) -> Result<String, Error> {
    let comp = parse_comp(axon)?;
    let context = fmt::Context::new(0, desired_width);
    let widen = true;
    let code = comp.default_rewrite(context, widen);
    code.ok_or(Error::Rewrite)
}

// #[cfg(test)]
// mod tests {
//     #[test]
//     fn it_works() {
//         let code = include_str!("./test2.txt");
//         let x = crate::format(code, 80).unwrap();
//         println!("{}", x);
//     }
// }