m-o 1.0.0

A command line tool for parsing Python dataclass reprs and pretty-printing them.
Documentation
use std::fmt;

use super::Value;
use crate::value::Arg;
use pretty::{BoxDoc, Doc};

pub struct PrintOptions {
    pub indent: usize,
    pub columns: usize,
}

impl Default for PrintOptions {
    fn default() -> Self {
        PrintOptions {
            indent: 4,
            columns: 80,
        }
    }
}

impl<'value> Value<'value> {
    fn seq_to_doc<'iter, I>(
        open: &'static str,
        xs: I,
        close: &'static str,
        options: &PrintOptions,
    ) -> Doc<'value, BoxDoc<'value, ()>>
    where
        I: Iterator<Item = Doc<'value, BoxDoc<'value, ()>>>,
        'value: 'iter,
    {
        Doc::text(open)
            .append(Doc::newline().flat_alt(Doc::nil()))
            .nest(options.indent)
            .append(Doc::intersperse(xs, Doc::text(",").append(Doc::space())).nest(options.indent))
            .append(Doc::newline().flat_alt(Doc::nil()))
            .append(Doc::text(close))
            .group()
    }

    fn dictionary_to_doc<'tmp>(
        pairs: &'tmp [(Value<'value>, Value<'value>)],
        options: &PrintOptions,
    ) -> Doc<'value, BoxDoc<'value, ()>>
    where
        'value: 'tmp,
    {
        Self::seq_to_doc(
            "{",
            pairs.iter().map(|(key, value)| {
                key.to_doc(options)
                    .append(Doc::text(": "))
                    .append(value.to_doc(options))
            }),
            "}",
            options,
        )
        .group()
    }

    fn constructor_to_doc<'tmp>(
        name: &'value str,
        args: &'tmp [Arg<'value>],
        options: &PrintOptions,
    ) -> Doc<'value, BoxDoc<'value, ()>>
    where
        'value: 'tmp,
    {
        Doc::text(name)
            .append(Self::seq_to_doc(
                "(",
                args.iter().map(|arg| match arg {
                    Arg::Arg(value) => value.to_doc(options),
                    Arg::Kwarg(key, value) => Doc::text(*key)
                        .append(Doc::text("="))
                        .append(value.to_doc(options)),
                }),
                ")",
                options,
            ))
            .group()
    }
}

impl<'value> Value<'value> {
    pub fn to_doc(&self, options: &PrintOptions) -> Doc<'value, BoxDoc<'value, ()>> {
        match *self {
            Value::Int(x) => Doc::text(x.to_string()),
            Value::Float(x) => Doc::text(x.to_string()),
            Value::Bool(x) => Doc::text(if x { "True" } else { "False" }),
            Value::Symbol(x) => Doc::text(x),
            Value::Str(x) => Doc::text(x),
            Value::List(ref xs) => {
                Self::seq_to_doc("[", xs.iter().map(|x| x.to_doc(options)), "]", options)
            }
            Value::Tuple(ref xs) => {
                Self::seq_to_doc("(", xs.iter().map(|x| x.to_doc(options)), ")", options)
            }
            Value::Set(ref xs) => {
                Self::seq_to_doc("{", xs.iter().map(|x| x.to_doc(options)), "}", options)
            }
            Value::Dict(ref pairs) => Self::dictionary_to_doc(pairs, options),
            Value::Constructor(name, ref args) => Self::constructor_to_doc(name, args, options),
        }
    }
}

impl<'a> fmt::Display for Value<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let options = PrintOptions::default();
        self.to_doc(&options).render_fmt(options.columns, f)
    }
}