<commit_before>//! Compatibility between the `tokio::io` and `futures-io` versions of the
//! `AsyncRead` and `AsyncWrite` traits.
use futures_core::ready;
use pin_project_lite::pin_project;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};

pin_project! {
    /// A compatibility layer that allows conversion between the
    /// `tokio::io` and `futures-io` `AsyncRead` and `AsyncWrite` traits.
    #[derive(Copy, Clone, Debug)]
    pub struct Compat<T> {
        #[pin]
        inner: T,
    }
}

/// Extension trait that allows converting a type implementing
/// `futures_io::AsyncRead` to implement `tokio::io::AsyncRead`.
pub trait FuturesAsyncReadCompatExt: futures_io::AsyncRead {
    /// Wraps `self` with a compatibility layer that implements
    /// `tokio_io::AsyncRead`.
    fn compat(self) -> Compat<Self>
    where
        Self: Sized,
    {
        Compat::new(self)
    }
}

impl<T: futures_io::AsyncRead> FuturesAsyncReadCompatExt for T {}

/// Extension trait that allows converting a type implementing
/// `futures_io::AsyncWrite` to implement `tokio::io::AsyncWrite`.
pub trait FuturesAsyncWriteCompatExt: futures_io::AsyncWrite {
    /// Wraps `self` with a compatibility layer that implements
    /// `tokio::io::AsyncWrite`.
    fn compat_write(self) -> Compat<Self>
    where
        Self: Sized,
    {
        Compat::new(self)
    }
}

impl<T: futures_io::AsyncWrite> FuturesAsyncWriteCompatExt for T {}

/// Extension trait that allows converting a type implementing
/// `tokio::io::AsyncRead` to implement `futures_io::AsyncRead`.
pub trait TokioAsyncReadCompatExt: tokio::io::AsyncRead {
    /// Wraps `self` with a compatibility layer that implements
    /// `futures_io::AsyncRead`.
    fn compat(self) -> Compat<Self>
    where
        Self: Sized,
    {
        Compat::new(self)
    }
}

impl<T: tokio::io::AsyncRead> TokioAsyncReadCompatExt for T {}

/// Extension trait that allows converting a type implementing
/// `tokio::io::AsyncWrite` to implement `futures_io::AsyncWrite`.
pub trait TokioAsyncWriteCompatExt: tokio::io::AsyncWrite {
    /// Wraps `self` with a compatibility layer that implements
    /// `futures_io::AsyncWrite`.
    fn compat_write(self) -> Compat<Self>
    where
        Self: Sized,
    {
        Compat::new(self)
    }
}

impl<T: tokio::io::AsyncWrite> TokioAsyncWriteCompatExt for T {}

// === impl Compat ===

impl<T> Compat<T> {
    fn new(inner: T) -> Self {
        Self { inner }
    }

    /// Get a reference to the `Future`, `Stream`, `AsyncRead`, or `AsyncWrite` object
    /// contained within.
    pub fn get_ref(&self) -> &T {
        &self.inner
    }

    /// Get a mutable reference to the `Future`, `Stream`, `AsyncRead`, or `AsyncWrite` object
    /// contained within.
    pub fn get_mut(&mut self) -> &mut T {
        &mut self.inner
    }

    /// Returns the wrapped item.
    pub fn into_inner(self) -> T {
        self.inner
    }
}

impl<T> tokio::io::AsyncRead for Compat<T>
where
    T: futures_io::AsyncRead,
{
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut tokio::io::ReadBuf<'_>,
    ) -> Poll<io::Result<()>> {
        // We can't trust the inner type to not peak at the bytes,
        // so we must defensively initialize the buffer.
        let slice = buf.initialize_unfilled();
        let n = ready!(futures_io::AsyncRead::poll_read(
            self.project().inner,
            cx,
            slice
        ))?;
        buf.advance(n);
        Poll::Ready(Ok(()))
    }
}

impl<T> futures_io::AsyncRead for Compat<T>
where
    T: tokio::io::AsyncRead,
{
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        slice: &mut [u8],
    ) -> Poll<io::Result<usize>> {
        let mut buf = tokio::io::ReadBuf::new(slice);
        ready!(tokio::io::AsyncRead::poll_read(
            self.project().inner,
            cx,
            &mut buf
        ))?;
        Poll::Ready(Ok(buf.filled().len()))
    }
}

impl<T> tokio::io::AsyncBufRead for Compat<T>
where
    T: futures_io::AsyncBufRead,
{
    fn poll_fill_buf<'a>(
        self: Pin<&'a mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<io::Result<&'a [u8]>> {
        futures_io::AsyncBufRead::poll_fill_buf(self.project().inner, cx)
    }

    fn consume(self: Pin<&mut Self>, amt: usize) {
        futures_io::AsyncBufRead::consume(self.project().inner, amt)
    }
}

impl<T> futures_io::AsyncBufRead for Compat<T>
where
    T: tokio::io::AsyncBufRead,
{
    fn poll_fill_buf<'a>(
        self: Pin<&'a mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<io::Result<&'a [u8]>> {
        tokio::io::AsyncBufRead::poll_fill_buf(self.project().inner, cx)
    }

    fn consume(self: Pin<&mut Self>, amt: usize) {
        tokio::io::AsyncBufRead::consume(self.project().inner, amt)
    }
}

impl<T> tokio::io::AsyncWrite for Compat<T>
where
    T: futures_io::AsyncWrite,
{
    fn poll_write(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<io::Result<usize>> {
        futures_io::AsyncWrite::poll_write(self.project().inner, cx, buf)
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        futures_io::AsyncWrite::poll_flush(self.project().inner, cx)
    }

    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        futures_io::AsyncWrite::poll_close(self.project().inner, cx)
    }
}

impl<T> futures_io::AsyncWrite for Compat<T>
where
    T: tokio::io::AsyncWrite,
{
    fn poll_write(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<io::Result<usize>> {
        tokio::io::AsyncWrite::poll_write(self.project().inner, cx, buf)
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        tokio::io::AsyncWrite::poll_flush(self.project().inner, cx)
    }

    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        tokio::io::AsyncWrite::poll_shutdown(self.project().inner, cx)
    }
}

#[cfg(unix)]
impl<T: std::os::unix::io::AsRawFd> std::os::unix::io::AsRawFd for Compat<T> {
    fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
        self.inner.as_raw_fd()
    }
}

#[cfg(windows)]
impl<T: std::os::windows::io::AsRawHandle> std::os::windows::io::AsRawHandle for Compat<T> {
    fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
        self.inner.as_raw_handle()
    }
}
<commit_msg>compat: added AsyncSeek compat (#4078)<commit_after>//! Compatibility between the `tokio::io` and `futures-io` versions of the
//! `AsyncRead` and `AsyncWrite` traits.
use futures_core::ready;
use pin_project_lite::pin_project;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};

pin_project! {
    /// A compatibility layer that allows conversion between the
    /// `tokio::io` and `futures-io` `AsyncRead` and `AsyncWrite` traits.
    #[derive(Copy, Clone, Debug)]
    pub struct Compat<T> {
        #[pin]
        inner: T,
        seek_pos: Option<io::SeekFrom>,
    }
}

/// Extension trait that allows converting a type implementing
/// `futures_io::AsyncRead` to implement `tokio::io::AsyncRead`.
pub trait FuturesAsyncReadCompatExt: futures_io::AsyncRead {
    /// Wraps `self` with a compatibility layer that implements
    /// `tokio_io::AsyncRead`.
    fn compat(self) -> Compat<Self>
    where
        Self: Sized,
    {
        Compat::new(self)
    }
}

impl<T: futures_io::AsyncRead> FuturesAsyncReadCompatExt for T {}

/// Extension trait that allows converting a type implementing
/// `futures_io::AsyncWrite` to implement `tokio::io::AsyncWrite`.
pub trait FuturesAsyncWriteCompatExt: futures_io::AsyncWrite {
    /// Wraps `self` with a compatibility layer that implements
    /// `tokio::io::AsyncWrite`.
    fn compat_write(self) -> Compat<Self>
    where
        Self: Sized,
    {
        Compat::new(self)
    }
}

impl<T: futures_io::AsyncWrite> FuturesAsyncWriteCompatExt for T {}

/// Extension trait that allows converting a type implementing
/// `tokio::io::AsyncRead` to implement `futures_io::AsyncRead`.
pub trait TokioAsyncReadCompatExt: tokio::io::AsyncRead {
    /// Wraps `self` with a compatibility layer that implements
    /// `futures_io::AsyncRead`.
    fn compat(self) -> Compat<Self>
    where
        Self: Sized,
    {
        Compat::new(self)
    }
}

impl<T: tokio::io::AsyncRead> TokioAsyncReadCompatExt for T {}

/// Extension trait that allows converting a type implementing
/// `tokio::io::AsyncWrite` to implement `futures_io::AsyncWrite`.
pub trait TokioAsyncWriteCompatExt: tokio::io::AsyncWrite {
    /// Wraps `self` with a compatibility layer that implements
    /// `futures_io::AsyncWrite`.
    fn compat_write(self) -> Compat<Self>
    where
        Self: Sized,
    {
        Compat::new(self)
    }
}

impl<T: tokio::io::AsyncWrite> TokioAsyncWriteCompatExt for T {}

// === impl Compat ===

impl<T> Compat<T> {
    fn new(inner: T) -> Self {
        Self {
            inner,
            seek_pos: None,
        }
    }

    /// Get a reference to the `Future`, `Stream`, `AsyncRead`, or `AsyncWrite` object
    /// contained within.
    pub fn get_ref(&self) -> &T {
        &self.inner
    }

    /// Get a mutable reference to the `Future`, `Stream`, `AsyncRead`, or `AsyncWrite` object
    /// contained within.
    pub fn get_mut(&mut self) -> &mut T {
        &mut self.inner
    }

    /// Returns the wrapped item.
    pub fn into_inner(self) -> T {
        self.inner
    }
}

impl<T> tokio::io::AsyncRead for Compat<T>
where
    T: futures_io::AsyncRead,
{
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut tokio::io::ReadBuf<'_>,
    ) -> Poll<io::Result<()>> {
        // We can't trust the inner type to not peak at the bytes,
        // so we must defensively initialize the buffer.
        let slice = buf.initialize_unfilled();
        let n = ready!(futures_io::AsyncRead::poll_read(
            self.project().inner,
            cx,
            slice
        ))?;
        buf.advance(n);
        Poll::Ready(Ok(()))
    }
}

impl<T> futures_io::AsyncRead for Compat<T>
where
    T: tokio::io::AsyncRead,
{
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        slice: &mut [u8],
    ) -> Poll<io::Result<usize>> {
        let mut buf = tokio::io::ReadBuf::new(slice);
        ready!(tokio::io::AsyncRead::poll_read(
            self.project().inner,
            cx,
            &mut buf
        ))?;
        Poll::Ready(Ok(buf.filled().len()))
    }
}

impl<T> tokio::io::AsyncBufRead for Compat<T>
where
    T: futures_io::AsyncBufRead,
{
    fn poll_fill_buf<'a>(
        self: Pin<&'a mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<io::Result<&'a [u8]>> {
        futures_io::AsyncBufRead::poll_fill_buf(self.project().inner, cx)
    }

    fn consume(self: Pin<&mut Self>, amt: usize) {
        futures_io::AsyncBufRead::consume(self.project().inner, amt)
    }
}

impl<T> futures_io::AsyncBufRead for Compat<T>
where
    T: tokio::io::AsyncBufRead,
{
    fn poll_fill_buf<'a>(
        self: Pin<&'a mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<io::Result<&'a [u8]>> {
        tokio::io::AsyncBufRead::poll_fill_buf(self.project().inner, cx)
    }

    fn consume(self: Pin<&mut Self>, amt: usize) {
        tokio::io::AsyncBufRead::consume(self.project().inner, amt)
    }
}

impl<T> tokio::io::AsyncWrite for Compat<T>
where
    T: futures_io::AsyncWrite,
{
    fn poll_write(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<io::Result<usize>> {
        futures_io::AsyncWrite::poll_write(self.project().inner, cx, buf)
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        futures_io::AsyncWrite::poll_flush(self.project().inner, cx)
    }

    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        futures_io::AsyncWrite::poll_close(self.project().inner, cx)
    }
}

impl<T> futures_io::AsyncWrite for Compat<T>
where
    T: tokio::io::AsyncWrite,
{
    fn poll_write(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<io::Result<usize>> {
        tokio::io::AsyncWrite::poll_write(self.project().inner, cx, buf)
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        tokio::io::AsyncWrite::poll_flush(self.project().inner, cx)
    }

    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        tokio::io::AsyncWrite::poll_shutdown(self.project().inner, cx)
    }
}

impl<T: tokio::io::AsyncSeek> futures_io::AsyncSeek for Compat<T> {
    fn poll_seek(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        pos: io::SeekFrom,
    ) -> Poll<io::Result<u64>> {
        if self.seek_pos != Some(pos) {
            self.as_mut().project().inner.start_seek(pos)?;
            *self.as_mut().project().seek_pos = Some(pos);
        }
        let res = ready!(self.as_mut().project().inner.poll_complete(cx));
        *self.as_mut().project().seek_pos = None;
        Poll::Ready(res.map(|p| p as u64))
    }
}

impl<T: futures_io::AsyncSeek> tokio::io::AsyncSeek for Compat<T> {
    fn start_seek(mut self: Pin<&mut Self>, pos: io::SeekFrom) -> io::Result<()> {
        *self.as_mut().project().seek_pos = Some(pos);
        Ok(())
    }

    fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
        let pos = match self.seek_pos {
            None => {
                // tokio 1.x AsyncSeek recommends calling poll_complete before start_seek.
                // We don't have to guarantee that the value returned by
                // poll_complete called without start_seek is correct,
                // so we'll return 0.
                return Poll::Ready(Ok(0));
            }
            Some(pos) => pos,
        };
        let res = ready!(self.as_mut().project().inner.poll_seek(cx, pos));
        *self.as_mut().project().seek_pos = None;
        Poll::Ready(res.map(|p| p as u64))
    }
}

#[cfg(unix)]
impl<T: std::os::unix::io::AsRawFd> std::os::unix::io::AsRawFd for Compat<T> {
    fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
        self.inner.as_raw_fd()
    }
}

#[cfg(windows)]
impl<T: std::os::windows::io::AsRawHandle> std::os::windows::io::AsRawHandle for Compat<T> {
    fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
        self.inner.as_raw_handle()
    }
}
<|endoftext|>


<commit_before>use std::str;

static TABLE: [uint, ..10] = [119, 36, 93, 109, 46, 107, 123, 37, 127, 111];
static H_SYM: &'static str = "-";
static V_SYM: &'static str = "|";
static S_SYM: &'static str = " ";

struct LCD {
    numbers: ~[uint],
    segments: uint
}

impl LCD {

    fn new(input: ~str, segments: uint) -> LCD {
        let mut numbers = ~[];
        for input.iter().advance |c| {
            let option = std::int::from_str(str::from_char(c));
            let n : uint = match option {
                None => 0,
                Some(y) => { y }
            } as uint;
            numbers.push(n);
        }
        LCD { numbers: numbers, segments: segments }
    }

    fn test(&self, number: uint, position: int) -> bool {
        return (TABLE[number] & std::int::pow(2, position as uint) as uint) > 0;
    }

    fn draw(&self) {
        let mut offset = 0;
        for std::int::range(0, 5) |row| {
            if (row % 2 == 0) {
                for self.numbers.iter().advance |&number| {
                    let symbol = if (self.test(number, row + offset)) { H_SYM } else { S_SYM };
                    let mut symbols = ~"";
                    for self.segments.times {
                        symbols = symbols + symbol;
                    }
                    print(S_SYM + symbols + S_SYM + S_SYM);
                }
                print("\n");
            } else {
                for self.segments.times {
                    for self.numbers.iter().advance |&number| {
                        for std::int::range(0, 2) |column| {
                            let symbol = if (self.test(number, row + column + offset)) { V_SYM } else { S_SYM };
                            let mut spaces = ~"";
                            let spaces_count = if (column == 0) { self.segments } else { 1 };
                            for spaces_count.times {
                                spaces = spaces + S_SYM;
                            }
                            print(symbol + spaces);
                        }
                    }
                    print("\n");
                }
                offset = offset + 1;
            }
        }
    }
}

fn main() {
    let args = std::os::args();
    if (args.len() > 1) {
        let mut input: ~str;
        let mut segments: uint = 2;
        if (args[1] == ~"-s") {
            match std::int::from_str(args[2]) {
                Some(n) => { segments = n as uint },
                None => {}
            };
            input = args[3];
        } else {
            input = args[1];
        }
        let lcd = LCD::new(input, segments);
        lcd.draw();
    } else {
        println("Usage: LCD [-s SEGMENTS] NUMBER");
    }
}
<commit_msg>Binary notation and bitshift for Rust<commit_after>use std::str;

static TABLE: [uint, ..10] = [
  0b1110111,
  0b0100100,
  0b1011101,
  0b1101101,
  0b0101110,
  0b1101011,
  0b1111011,
  0b0100101,
  0b1111111,
  0b1101111
];
static H_SYM: &'static str = "-";
static V_SYM: &'static str = "|";
static S_SYM: &'static str = " ";

struct LCD {
    numbers: ~[uint],
    segments: uint
}

impl LCD {

    fn new(input: ~str, segments: uint) -> LCD {
        let mut numbers = ~[];
        for input.iter().advance |c| {
            let option = std::int::from_str(str::from_char(c));
            let n : uint = match option {
                None => 0,
                Some(y) => { y }
            } as uint;
            numbers.push(n);
        }
        LCD { numbers: numbers, segments: segments }
    }

    fn test(&self, number: uint, position: int) -> bool {
        return (TABLE[number] & (0b0000001 << position)) > 0;
    }

    fn draw(&self) {
        let mut offset = 0;
        for std::int::range(0, 5) |row| {
            if (row % 2 == 0) {
                for self.numbers.iter().advance |&number| {
                    let symbol = if (self.test(number, row + offset)) { H_SYM } else { S_SYM };
                    let mut symbols = ~"";
                    for self.segments.times {
                        symbols = symbols + symbol;
                    }
                    print(S_SYM + symbols + S_SYM + S_SYM);
                }
                print("\n");
            } else {
                for self.segments.times {
                    for self.numbers.iter().advance |&number| {
                        for std::int::range(0, 2) |column| {
                            let symbol = if (self.test(number, row + column + offset)) { V_SYM } else { S_SYM };
                            let mut spaces = ~"";
                            let spaces_count = if (column == 0) { self.segments } else { 1 };
                            for spaces_count.times {
                                spaces = spaces + S_SYM;
                            }
                            print(symbol + spaces);
                        }
                    }
                    print("\n");
                }
                offset = offset + 1;
            }
        }
    }
}

fn main() {
    let args = std::os::args();
    if (args.len() > 1) {
        let mut input: ~str;
        let mut segments: uint = 2;
        if (args[1] == ~"-s") {
            match std::int::from_str(args[2]) {
                Some(n) => { segments = n as uint },
                None => {}
            };
            input = args[3];
        } else {
            input = args[1];
        }
        let lcd = LCD::new(input, segments);
        lcd.draw();
    } else {
        println("Usage: LCD [-s SEGMENTS] NUMBER");
    }
}
<|endoftext|>


<commit_before>
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use regex::Regex;

#[macro_use]
extern crate lazy_static;

pub fn get_number<P: AsRef<Path>>(filename: P) -> io::Result<u32> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"NUMBER=(\d+)").unwrap();
    }
    let mut matches = match_lines(filename, &*RE)?;
    return Ok(matches.next().unwrap().parse().unwrap());
}

pub fn get_output<'a, P:'a + AsRef<Path>>(filename: P) -> io::Result<impl Iterator<Item = u32> + 'a> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"OUTPUT:(.*)").unwrap();
    }
    get_numbers(filename, &*RE)
}

pub fn get_collection<'a, P:'a + AsRef<Path>>(filename: P, idx: u32) -> io::Result<impl Iterator<Item = u32> + 'a> {
    let regex: Regex = {
        let r = format!("COLLECTION{}:(.*)", idx);
        Regex::new(&r).unwrap()
    };
    get_numbers(filename, &regex)
}

pub fn get_numbers<'a, P:'a + AsRef<Path>>(filename: P, regex: &'a Regex) -> io::Result<impl Iterator<Item = u32> + 'a> {
    let matches = match_lines(filename, regex)?;
    let values = matches.map(|s| {
        let numbers = s.split(',').map(|v| v.parse::<u32>().unwrap());
        numbers.collect::<Vec<_>>()
    }).flatten();
    Ok(values)
}

fn match_lines<'a, P: 'a + AsRef<Path>>(filename: P, regex: &'a Regex) -> io::Result<impl Iterator<Item = String> + 'a> {
    let read_lines = move || -> io::Result<_> {
        let file = File::open(filename)?;
        Ok(io::BufReader::new(file).lines().map(|line|
            line.unwrap()))
    };
    let lines = read_lines()?;
    let matches = lines.filter_map(move |line| {
        regex.captures(&line).map(|captures|
            captures[1].to_string()
        )
    });
    Ok(matches)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_number() {
        assert_eq!(get_number("../data/test.txt").unwrap() , 3);
    }

    #[test]
    fn test_output() {
        let output = [1,1,2,2,3,4,4,6,7,9,9,20,21];
        assert_eq!(get_output("../data/test.txt").unwrap().collect::<Vec<_>>(), output);
    }

    #[test]
    fn test_collection() {
        let output = [1,1,2,2,3,4,4,6,7,9,9,20,21];
        assert_eq!(get_collection("../data/test.txt", 1).unwrap().collect::<Vec<_>>(), output);
    }
}
<commit_msg>cloned Regex<commit_after>
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use regex::Regex;

#[macro_use]
extern crate lazy_static;

pub fn get_number<P: AsRef<Path>>(filename: P) -> io::Result<u32> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"NUMBER=(\d+)").unwrap();
    }
    let mut matches = match_lines(filename, &*RE)?;
    return Ok(matches.next().unwrap().parse().unwrap());
}

pub fn get_output<'a, P:'a + AsRef<Path>>(filename: P) -> io::Result<impl Iterator<Item = u32> + 'a> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"OUTPUT:(.*)").unwrap();
    }
    get_numbers(filename, &*RE)
}

pub fn get_collection<'a, P:'a + AsRef<Path>>(filename: P, idx: u32) -> io::Result<impl Iterator<Item = u32> + 'a> {
    let regex: Regex = {
        let r = format!("COLLECTION{}:(.*)", idx);
        Regex::new(&r).unwrap()
    };
    get_numbers(filename, &regex)
}

pub fn get_numbers<'a, P:'a + AsRef<Path>>(filename: P, regex: &Regex) -> io::Result<impl Iterator<Item = u32> + 'a> {
    let matches = match_lines(filename, regex)?;
    let values = matches.map(|s| {
        let numbers = s.split(',').map(|v| v.parse::<u32>().unwrap());
        numbers.collect::<Vec<_>>()
    }).flatten();
    Ok(values)
}

fn match_lines<'a, P: 'a + AsRef<Path>>(filename: P, regex: &Regex) -> io::Result<impl Iterator<Item = String> + 'a> {
    let read_lines = move || -> io::Result<_> {
        let file = File::open(filename)?;
        Ok(io::BufReader::new(file).lines().map(|line|
            line.unwrap()))
    };
    let lines = read_lines()?;
    let r = regex.clone(); // TODO
    let matches = lines.filter_map(move |line| {
        r.captures(&line).map(|captures|
            captures[1].to_string()
        )
    });
    Ok(matches)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_number() {
        assert_eq!(get_number("../data/test.txt").unwrap() , 3);
    }

    #[test]
    fn test_output() {
        let output = [1,1,2,2,3,4,4,6,7,9,9,20,21];
        assert_eq!(get_output("../data/test.txt").unwrap().collect::<Vec<_>>(), output);
    }

    #[test]
    fn test_collection() {
        let output = [2, 4, 6, 9, 20];
        assert_eq!(get_collection("../data/test.txt", 1).unwrap().collect::<Vec<_>>(), output);
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use regex::Regex;

#[macro_use]
extern crate lazy_static;
pub fn get_number<P: AsRef<Path>>(filename: P) -> io::Result<i32> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"NUMBER=(\d+)").unwrap();
    }
    let lines = read_lines(filename)?;
    for line in lines {
        if let Some(n) = RE.captures(&line.unwrap()) {
            if let Some(n) = n.get(1) {
                return Ok(n.as_str().parse().unwrap());
            }
        }
    }
    Ok(0)
}

fn read_lines<P: AsRef<Path>>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>> {
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn get_test_number() {
        assert_eq!(get_number("../data/test.txt").unwrap() , 3);
    }
}
<commit_msg>get_number shorted<commit_after>use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use regex::Regex;

#[macro_use]
extern crate lazy_static;
pub fn get_number<P: AsRef<Path>>(filename: P) -> io::Result<u32> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"NUMBER=(\d+)").unwrap();
    }
    if let Ok(lines) = read_lines(filename) {
        for line in lines {
            if let Some(n) = RE.captures(&line.unwrap()) {
                return Ok(n.get(1).unwrap().as_str().parse().unwrap());
            }
        }
    }
    Ok(0)
}

fn read_lines<P: AsRef<Path>>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>> {
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn get_test_number() {
        assert_eq!(get_number("../data/test.txt").unwrap() , 3);
    }
}
<|endoftext|>


<commit_before>//! Crate to `#include` C++ headers in your Rust code, and generate
//! idiomatic bindings using `cxx`. See [include_cpp] for details.

// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{
    fs::File,
    io::Write,
    os::unix::prelude::PermissionsExt,
    path::{Path, PathBuf},
    process::Command,
};

use autocxx_engine::preprocess;
use clap::{crate_authors, crate_version, App, Arg, ArgMatches};
use indoc::indoc;
use itertools::Itertools;
use tempfile::TempDir;

static LONG_HELP: &str = indoc! {"
Command line utility to minimize autocxx bug cases.

This is a wrapper for creduce.

Example command-line:
autocxx-reduce -I my-inc-dir -h my-header -d 'generate!(\"MyClass\")' -k -- --n 64 --remove-pass pass_line_markers
"};

fn main() {
    let matches = App::new("autocxx-reduce")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Reduce a C++ test case")
        .long_about(LONG_HELP)
        .arg(
            Arg::with_name("inc")
                .short("I")
                .long("inc")
                .multiple(true)
                .number_of_values(1)
                .value_name("INCLUDE DIRS")
                .help("include path")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("define")
                .short("D")
                .long("define")
                .multiple(true)
                .number_of_values(1)
                .value_name("DEFINE")
                .help("macro definition")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("header")
                .short("h")
                .long("header")
                .multiple(true)
                .number_of_values(1)
                .required(true)
                .value_name("HEADER")
                .help("header file name")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("directive")
                .short("d")
                .long("directive")
                .multiple(true)
                .number_of_values(1)
                .value_name("DIRECTIVE")
                .help("directives to put within include_cpp!")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("problem")
                .short("p")
                .long("problem")
                .required(true)
                .value_name("PROBLEM")
                .help("problem string we're looking for... may be in logs, or in generated C++, or generated .rs")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("creduce")
                .long("creduce")
                .required(true)
                .value_name("PATH")
                .help("creduce binary location")
                .default_value("/usr/bin/creduce")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("output")
                .short("o")
                .long("output")
                .value_name("OUTPUT")
                .help("where to write minimized output")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("keep")
                .short("k")
                .long("keep-dir")
                .help("keep the temporary directory for debugging purposes"),
        )
        .arg(
            Arg::with_name("clang-args")
                .short("c")
                .long("clang-arg")
                .multiple(true)
                .value_name("CLANG_ARG")
                .help("Extra arguments to pass to Clang"),
        )
        .arg(Arg::with_name("creduce-args").last(true).multiple(true))
        .get_matches();
    run(matches).unwrap();
}

fn run(matches: ArgMatches) -> Result<(), std::io::Error> {
    let keep_tmp = matches.is_present("keep");
    let tmp_dir = TempDir::new()?;
    let r = do_run(matches, &tmp_dir);
    if keep_tmp {
        println!(
            "Keeping temp dir created at: {}",
            tmp_dir.into_path().to_str().unwrap()
        );
    }
    r
}

fn do_run(matches: ArgMatches, tmp_dir: &TempDir) -> Result<(), std::io::Error> {
    let incs: Vec<_> = matches
        .values_of("inc")
        .unwrap_or_default()
        .map(PathBuf::from)
        .collect();
    let defs: Vec<_> = matches.values_of("define").unwrap_or_default().collect();
    let headers: Vec<_> = matches.values_of("header").unwrap_or_default().collect();
    let listing_path = tmp_dir.path().join("listing.h");
    create_concatenated_header(&headers, &listing_path)?;
    let concat_path = tmp_dir.path().join("concat.h");
    announce_progress(&format!(
        "Preprocessing {:?} to {:?}",
        listing_path, concat_path
    ));
    preprocess(&listing_path, &concat_path, &incs, &defs)?;
    let rs_path = tmp_dir.path().join("input.rs");
    let directives: Vec<_> = std::iter::once("#include \"concat.h\"\n".to_string())
        .chain(
            matches
                .values_of("directive")
                .unwrap_or_default()
                .map(|s| format!("{}\n", s)),
        )
        .collect();
    create_rs_file(&rs_path, &directives)?;
    let extra_clang_args: Vec<_> = matches
        .values_of("clang-args")
        .unwrap_or_default()
        .collect();
    run_sample_gen_cmd(&rs_path, &tmp_dir.path(), &extra_clang_args)?;
    let interestingness_test = tmp_dir.path().join("test.sh");
    create_interestingness_test(
        &interestingness_test,
        matches.value_of("problem").unwrap(),
        &rs_path,
        &extra_clang_args,
    )?;
    run_interestingness_test(&interestingness_test);
    run_creduce(
        matches.value_of("creduce").unwrap(),
        &interestingness_test,
        &concat_path,
        matches.values_of("creduce-args").unwrap_or_default(),
    );
    let output_path = matches.value_of("output");
    match output_path {
        None => print_minimized_case(&concat_path)?,
        Some(output_path) => {
            std::fs::copy(&concat_path, &PathBuf::from(output_path))?;
        }
    };
    Ok(())
}

fn announce_progress(msg: &str) {
    println!("=== {} ===", msg);
}

fn print_minimized_case(concat_path: &Path) -> Result<(), std::io::Error> {
    announce_progress("Completed. Minimized test case:");
    let contents = std::fs::read_to_string(concat_path)?;
    println!("{}", contents);
    Ok(())
}

/// Arguments we always pass to creduce. This pass always seems to cause a crash
/// as far as I can tell, so always exclude it. It may be environment-dependent,
/// of course, but as I'm the primary user of this tool I am ruthlessly removing it.
const CREDUCE_STANDARD_ARGS: &[&str] = &["--remove-pass", "pass_line_markers"];

fn run_creduce<'a>(
    creduce_cmd: &str,
    interestingness_test: &Path,
    concat_path: &Path,
    creduce_args: impl Iterator<Item = &'a str>,
) {
    announce_progress("creduce");
    Command::new(creduce_cmd)
        .arg(interestingness_test.to_str().unwrap())
        .arg(concat_path.to_str().unwrap())
        .args(CREDUCE_STANDARD_ARGS)
        .args(creduce_args)
        .status()
        .expect("failed to creduce");
}

fn run_sample_gen_cmd(
    rs_file: &Path,
    tmp_dir: &Path,
    extra_clang_args: &[&str],
) -> Result<(), std::io::Error> {
    let (gen_cmd, args) = format_gen_cmd(rs_file, tmp_dir.to_str().unwrap(), extra_clang_args)?;
    let args = args.collect::<Vec<_>>();
    let args_str = args.join(" ");
    announce_progress(&format!(
        "Running sample gen cmd: {} {}",
        gen_cmd.to_str().unwrap(),
        args_str
    ));
    Command::new(gen_cmd).args(args).status()?;
    Ok(())
}

fn format_gen_cmd<'a>(
    rs_file: &Path,
    dir: &str,
    extra_clang_args: &'a [&str],
) -> Result<(PathBuf, impl Iterator<Item = String> + 'a), std::io::Error> {
    let me = std::env::current_exe()?;
    let gen = me.parent().unwrap().join("autocxx-gen");
    let args = [
        "-o".to_string(),
        dir.to_string(),
        "-I".to_string(),
        dir.to_string(),
        rs_file.to_str().unwrap().to_string(),
        "--gen-rs-complete".to_string(),
        "--gen-cpp".to_string(),
        "--".to_string(),
    ]
    .to_vec();
    Ok((
        gen,
        args.into_iter()
            .chain(extra_clang_args.iter().map(|s| s.to_string())),
    ))
}

fn create_interestingness_test(
    test_path: &Path,
    problem: &str,
    rs_file: &Path,
    extra_clang_args: &[&str],
) -> Result<(), std::io::Error> {
    announce_progress("Creating interestingness test");
    // Ensure we refer to the input header by relative path
    // because creduce will invoke us in some other directory with
    // a copy thereof.
    let (gen_cmd, mut args) = format_gen_cmd(rs_file, "$(pwd)", extra_clang_args)?;
    let args = args.join(" ");
    let content = format!(
        indoc! {"
        #!/bin/sh
        ({} {} 2>&1 && cat gen.complete.rs && cat autocxxgen.h) | grep \"{}\"  >/dev/null 2>&1
    "},
        gen_cmd.to_str().unwrap(),
        args,
        problem
    );
    println!("Interestingness test:\n{}", content);
    {
        let mut file = File::create(test_path)?;
        file.write_all(content.as_bytes())?;
    }

    let mut perms = std::fs::metadata(&test_path)?.permissions();
    perms.set_mode(0o700);
    std::fs::set_permissions(&test_path, perms)?;
    Ok(())
}

fn run_interestingness_test(test_path: &Path) {
    announce_progress("Running interestingness test");
    let status = Command::new(test_path).status().unwrap();
    announce_progress(&format!(
        "Have run interestingness test - result is {:?}",
        status.code()
    ));
}

fn create_rs_file(rs_path: &Path, directives: &[String]) -> Result<(), std::io::Error> {
    announce_progress("Creating Rust input file");
    let mut file = File::create(rs_path)?;
    file.write_all("use autocxx::include_cpp;\ninclude_cpp! (\n".as_bytes())?;
    for directive in directives {
        file.write_all(directive.as_bytes())?;
    }
    file.write_all(");\n".as_bytes())?;
    Ok(())
}

fn create_concatenated_header(headers: &[&str], listing_path: &Path) -> Result<(), std::io::Error> {
    announce_progress("Creating preprocessed header");
    let mut file = File::create(listing_path)?;
    for header in headers {
        file.write_all(format!("#include \"{}\"\n", header).as_bytes())?;
    }
    Ok(())
}
<commit_msg>Allow customizing gen-cmd.<commit_after>//! Crate to `#include` C++ headers in your Rust code, and generate
//! idiomatic bindings using `cxx`. See [include_cpp] for details.

// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{
    fs::File,
    io::Write,
    os::unix::prelude::PermissionsExt,
    path::{Path, PathBuf},
    process::Command,
};

use autocxx_engine::preprocess;
use clap::{crate_authors, crate_version, App, Arg, ArgMatches};
use indoc::indoc;
use itertools::Itertools;
use tempfile::TempDir;

static LONG_HELP: &str = indoc! {"
Command line utility to minimize autocxx bug cases.

This is a wrapper for creduce.

Example command-line:
autocxx-reduce -I my-inc-dir -h my-header -d 'generate!(\"MyClass\")' -k -- --n 64 --remove-pass pass_line_markers
"};

fn main() {
    let matches = App::new("autocxx-reduce")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Reduce a C++ test case")
        .long_about(LONG_HELP)
        .arg(
            Arg::with_name("inc")
                .short("I")
                .long("inc")
                .multiple(true)
                .number_of_values(1)
                .value_name("INCLUDE DIRS")
                .help("include path")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("define")
                .short("D")
                .long("define")
                .multiple(true)
                .number_of_values(1)
                .value_name("DEFINE")
                .help("macro definition")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("header")
                .short("h")
                .long("header")
                .multiple(true)
                .number_of_values(1)
                .required(true)
                .value_name("HEADER")
                .help("header file name")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("directive")
                .short("d")
                .long("directive")
                .multiple(true)
                .number_of_values(1)
                .value_name("DIRECTIVE")
                .help("directives to put within include_cpp!")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("problem")
                .short("p")
                .long("problem")
                .required(true)
                .value_name("PROBLEM")
                .help("problem string we're looking for... may be in logs, or in generated C++, or generated .rs")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("creduce")
                .long("creduce")
                .required(true)
                .value_name("PATH")
                .help("creduce binary location")
                .default_value("/usr/bin/creduce")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("output")
                .short("o")
                .long("output")
                .value_name("OUTPUT")
                .help("where to write minimized output")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("gen-cmd")
                .short("g")
                .long("gen-cmd")
                .value_name("GEN-CMD")
                .help("where to find autocxx-gen")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("keep")
                .short("k")
                .long("keep-dir")
                .help("keep the temporary directory for debugging purposes"),
        )
        .arg(
            Arg::with_name("clang-args")
                .short("c")
                .long("clang-arg")
                .multiple(true)
                .value_name("CLANG_ARG")
                .help("Extra arguments to pass to Clang"),
        )
        .arg(Arg::with_name("creduce-args").last(true).multiple(true))
        .get_matches();
    run(matches).unwrap();
}

fn run(matches: ArgMatches) -> Result<(), std::io::Error> {
    let keep_tmp = matches.is_present("keep");
    let tmp_dir = TempDir::new()?;
    let r = do_run(matches, &tmp_dir);
    if keep_tmp {
        println!(
            "Keeping temp dir created at: {}",
            tmp_dir.into_path().to_str().unwrap()
        );
    }
    r
}

fn do_run(matches: ArgMatches, tmp_dir: &TempDir) -> Result<(), std::io::Error> {
    let incs: Vec<_> = matches
        .values_of("inc")
        .unwrap_or_default()
        .map(PathBuf::from)
        .collect();
    let defs: Vec<_> = matches.values_of("define").unwrap_or_default().collect();
    let headers: Vec<_> = matches.values_of("header").unwrap_or_default().collect();
    let listing_path = tmp_dir.path().join("listing.h");
    create_concatenated_header(&headers, &listing_path)?;
    let concat_path = tmp_dir.path().join("concat.h");
    announce_progress(&format!(
        "Preprocessing {:?} to {:?}",
        listing_path, concat_path
    ));
    preprocess(&listing_path, &concat_path, &incs, &defs)?;
    let rs_path = tmp_dir.path().join("input.rs");
    let directives: Vec<_> = std::iter::once("#include \"concat.h\"\n".to_string())
        .chain(
            matches
                .values_of("directive")
                .unwrap_or_default()
                .map(|s| format!("{}\n", s)),
        )
        .collect();
    create_rs_file(&rs_path, &directives)?;
    let extra_clang_args: Vec<_> = matches
        .values_of("clang-args")
        .unwrap_or_default()
        .collect();
    let default_gen_cmd = std::env::current_exe()?
        .parent()
        .unwrap()
        .join("autocxx-gen")
        .to_str()
        .unwrap()
        .to_string();
    let gen_cmd = matches.value_of("gen-cmd").unwrap_or(&default_gen_cmd);
    run_sample_gen_cmd(gen_cmd, &rs_path, &tmp_dir.path(), &extra_clang_args)?;
    let interestingness_test = tmp_dir.path().join("test.sh");
    create_interestingness_test(
        gen_cmd,
        &interestingness_test,
        matches.value_of("problem").unwrap(),
        &rs_path,
        &extra_clang_args,
    )?;
    run_interestingness_test(&interestingness_test);
    run_creduce(
        matches.value_of("creduce").unwrap(),
        &interestingness_test,
        &concat_path,
        matches.values_of("creduce-args").unwrap_or_default(),
    );
    let output_path = matches.value_of("output");
    match output_path {
        None => print_minimized_case(&concat_path)?,
        Some(output_path) => {
            std::fs::copy(&concat_path, &PathBuf::from(output_path))?;
        }
    };
    Ok(())
}

fn announce_progress(msg: &str) {
    println!("=== {} ===", msg);
}

fn print_minimized_case(concat_path: &Path) -> Result<(), std::io::Error> {
    announce_progress("Completed. Minimized test case:");
    let contents = std::fs::read_to_string(concat_path)?;
    println!("{}", contents);
    Ok(())
}

/// Arguments we always pass to creduce. This pass always seems to cause a crash
/// as far as I can tell, so always exclude it. It may be environment-dependent,
/// of course, but as I'm the primary user of this tool I am ruthlessly removing it.
const CREDUCE_STANDARD_ARGS: &[&str] = &["--remove-pass", "pass_line_markers"];

fn run_creduce<'a>(
    creduce_cmd: &str,
    interestingness_test: &Path,
    concat_path: &Path,
    creduce_args: impl Iterator<Item = &'a str>,
) {
    announce_progress("creduce");
    Command::new(creduce_cmd)
        .arg(interestingness_test.to_str().unwrap())
        .arg(concat_path.to_str().unwrap())
        .args(CREDUCE_STANDARD_ARGS)
        .args(creduce_args)
        .status()
        .expect("failed to creduce");
}

fn run_sample_gen_cmd(
    gen_cmd: &str,
    rs_file: &Path,
    tmp_dir: &Path,
    extra_clang_args: &[&str],
) -> Result<(), std::io::Error> {
    let args = format_gen_cmd(rs_file, tmp_dir.to_str().unwrap(), extra_clang_args);
    let args = args.collect::<Vec<_>>();
    let args_str = args.join(" ");
    announce_progress(&format!("Running sample gen cmd: {} {}", gen_cmd, args_str));
    Command::new(gen_cmd).args(args).status()?;
    Ok(())
}

fn format_gen_cmd<'a>(
    rs_file: &Path,
    dir: &str,
    extra_clang_args: &'a [&str],
) -> impl Iterator<Item = String> + 'a {
    let args = [
        "-o".to_string(),
        dir.to_string(),
        "-I".to_string(),
        dir.to_string(),
        rs_file.to_str().unwrap().to_string(),
        "--gen-rs-complete".to_string(),
        "--gen-cpp".to_string(),
        "--".to_string(),
    ]
    .to_vec();
    args.into_iter()
        .chain(extra_clang_args.iter().map(|s| s.to_string()))
}

fn create_interestingness_test(
    gen_cmd: &str,
    test_path: &Path,
    problem: &str,
    rs_file: &Path,
    extra_clang_args: &[&str],
) -> Result<(), std::io::Error> {
    announce_progress("Creating interestingness test");
    // Ensure we refer to the input header by relative path
    // because creduce will invoke us in some other directory with
    // a copy thereof.
    let mut args = format_gen_cmd(rs_file, "$(pwd)", extra_clang_args);
    let args = args.join(" ");
    let content = format!(
        indoc! {"
        #!/bin/sh
        ({} {} 2>&1 && cat gen.complete.rs && cat autocxxgen.h) | grep \"{}\"  >/dev/null 2>&1
    "},
        gen_cmd, args, problem
    );
    println!("Interestingness test:\n{}", content);
    {
        let mut file = File::create(test_path)?;
        file.write_all(content.as_bytes())?;
    }

    let mut perms = std::fs::metadata(&test_path)?.permissions();
    perms.set_mode(0o700);
    std::fs::set_permissions(&test_path, perms)?;
    Ok(())
}

fn run_interestingness_test(test_path: &Path) {
    announce_progress("Running interestingness test");
    let status = Command::new(test_path).status().unwrap();
    announce_progress(&format!(
        "Have run interestingness test - result is {:?}",
        status.code()
    ));
}

fn create_rs_file(rs_path: &Path, directives: &[String]) -> Result<(), std::io::Error> {
    announce_progress("Creating Rust input file");
    let mut file = File::create(rs_path)?;
    file.write_all("use autocxx::include_cpp;\ninclude_cpp! (\n".as_bytes())?;
    for directive in directives {
        file.write_all(directive.as_bytes())?;
    }
    file.write_all(");\n".as_bytes())?;
    Ok(())
}

fn create_concatenated_header(headers: &[&str], listing_path: &Path) -> Result<(), std::io::Error> {
    announce_progress("Creating preprocessed header");
    let mut file = File::create(listing_path)?;
    for header in headers {
        file.write_all(format!("#include \"{}\"\n", header).as_bytes())?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A type-checked scaling factor between units.

/// A scaling factor between two different units of measurement.
///
/// This is effectively a type-safe float, intended to be used in combination with other types like
/// `length::Length` to enforce conversion between systems of measurement at compile time.
///
/// `Src` and `Dst` represent the units before and after multiplying a value by a ScaleFactor.  They
/// may be types without values, such as empty enums.  For example:
///
/// ```rust
/// enum Mm {};
/// enum Inch {};
///
/// let mm_per_inch: ScaleFactor<Inch, Mm> = ScaleFactor(25.4);
///
/// let l0: Length<Inch, f32> = Length(12.0);
/// let l1: Length<Mm, f32> = one_foot * mm_per_inch;
/// ```
#[deriving(Clone, Decodable, Encodable, Eq)]
pub struct ScaleFactor<Src, Dst>(pub f32);

// *scale
impl<Src, Dst> Deref<f32> for ScaleFactor<Src, Dst> {
    fn deref<'a>(&'a self) -> &'a f32 {
        match *self {
            ScaleFactor(ref x) => x
        }
    }
}

impl<Src, Dst> ScaleFactor<Src, Dst> {
    /// The inverse ScaleFactor (1.0 / self).
    pub fn inv(&self) -> ScaleFactor<Dst, Src> {
        ScaleFactor(1.0 / **self)
    }
}

// scale0 * scale1
impl<A,B,C> Mul<ScaleFactor<B, C>, ScaleFactor<A, C>> for ScaleFactor<A, B> {
    #[inline]
    fn mul(&self, other: &ScaleFactor<B, C>) -> ScaleFactor<A, C> {
        ScaleFactor(**self * **other)
    }
}

// scale0 + scale1
impl<Src, Dst> Add<ScaleFactor<Src, Dst>, ScaleFactor<Src, Dst>> for ScaleFactor<Src, Dst> {
    #[inline]
    fn add(&self, other: &ScaleFactor<Src, Dst>) -> ScaleFactor<Src, Dst> {
        ScaleFactor(**self + **other)
    }
}

// scale0 - scale1
impl<Src, Dst> Sub<ScaleFactor<Src, Dst>, ScaleFactor<Src, Dst>> for ScaleFactor<Src, Dst> {
    #[inline]
    fn sub(&self, other: &ScaleFactor<Src, Dst>) -> ScaleFactor<Src, Dst> {
        ScaleFactor(**self - **other)
    }
}
<commit_msg>Implement Eq for ScaleFactor<commit_after>// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A type-checked scaling factor between units.

/// A scaling factor between two different units of measurement.
///
/// This is effectively a type-safe float, intended to be used in combination with other types like
/// `length::Length` to enforce conversion between systems of measurement at compile time.
///
/// `Src` and `Dst` represent the units before and after multiplying a value by a ScaleFactor.  They
/// may be types without values, such as empty enums.  For example:
///
/// ```rust
/// enum Mm {};
/// enum Inch {};
///
/// let mm_per_inch: ScaleFactor<Inch, Mm> = ScaleFactor(25.4);
///
/// let l0: Length<Inch, f32> = Length(12.0);
/// let l1: Length<Mm, f32> = one_foot * mm_per_inch;
/// ```
#[deriving(Clone, Decodable, Encodable)]
pub struct ScaleFactor<Src, Dst>(pub f32);

// *scale
impl<Src, Dst> Deref<f32> for ScaleFactor<Src, Dst> {
    fn deref<'a>(&'a self) -> &'a f32 {
        match *self {
            ScaleFactor(ref x) => x
        }
    }
}

impl<Src, Dst> ScaleFactor<Src, Dst> {
    /// The inverse ScaleFactor (1.0 / self).
    pub fn inv(&self) -> ScaleFactor<Dst, Src> {
        ScaleFactor(1.0 / **self)
    }
}

// scale0 * scale1
impl<A,B,C> Mul<ScaleFactor<B, C>, ScaleFactor<A, C>> for ScaleFactor<A, B> {
    #[inline]
    fn mul(&self, other: &ScaleFactor<B, C>) -> ScaleFactor<A, C> {
        ScaleFactor(**self * **other)
    }
}

// scale0 + scale1
impl<Src, Dst> Add<ScaleFactor<Src, Dst>, ScaleFactor<Src, Dst>> for ScaleFactor<Src, Dst> {
    #[inline]
    fn add(&self, other: &ScaleFactor<Src, Dst>) -> ScaleFactor<Src, Dst> {
        ScaleFactor(**self + **other)
    }
}

// scale0 - scale1
impl<Src, Dst> Sub<ScaleFactor<Src, Dst>, ScaleFactor<Src, Dst>> for ScaleFactor<Src, Dst> {
    #[inline]
    fn sub(&self, other: &ScaleFactor<Src, Dst>) -> ScaleFactor<Src, Dst> {
        ScaleFactor(**self - **other)
    }
}

// FIXME: Switch to `deriving(Eq)` after this Rust issue is fixed:
// https://github.com/mozilla/rust/issues/7671

impl<Src, Dst> Eq for ScaleFactor<Src, Dst> {
    fn eq(&self, other: &ScaleFactor<Src, Dst>) -> bool { (**self).eq(&**other) }
}
<|endoftext|>


<commit_before>use common::{
    eyre::{bail, Result},
    regex::Regex,
    serde_json, tracing,
};
use node_dispatch::dispatch_validator;
use stencila_schema::{
    self, ArrayValidator, BooleanValidator, ConstantValidator, EnumValidator, IntegerValidator,
    Node, Null, Number, NumberValidator, StringValidator, TupleValidator, ValidatorTypes,
};

/// A trait for applying different [`ValidatorTypes`] to other nodes
pub trait Validator {
    /// Check that `node` is valid against the validator
    ///
    /// Returns `Ok` if the node is valid and a descriptive, user facing `Error`
    /// if it is not.
    fn validate(&self, node: &Node) -> Result<()>;

    /// Parse a string to a `Node` that is valid against the validator
    ///
    /// Returns `Ok(node)` if the string can be parsed, an error otherwise.
    fn parse(&self, string: &str) -> Result<Node> {
        let node =
            serde_json::from_str(string).unwrap_or_else(|_| Node::String(string.to_string()));
        self.validate(&node)?;
        Ok(node)
    }

    /// Coerce the `node` to be valid against the validator
    ///
    /// Returns the coerced node.
    fn coerce(&self, node: &Node) -> Node;

    /// Get the default node for the validator
    fn default_(&self) -> Node;
}

/// [`Validator`] implementation for `ValidatorTypes`: just dispatches to variants
impl Validator for ValidatorTypes {
    fn validate(&self, node: &Node) -> Result<()> {
        dispatch_validator!(self, validate, node)
    }

    fn parse(&self, string: &str) -> Result<Node> {
        dispatch_validator!(self, parse, string)
    }

    fn coerce(&self, node: &Node) -> Node {
        dispatch_validator!(self, coerce, node)
    }

    fn default_(&self) -> Node {
        dispatch_validator!(self, default_)
    }
}

/// [`Validator`] implementation for abstract base `Validator`: does nothing
impl Validator for stencila_schema::Validator {
    fn validate(&self, _node: &Node) -> Result<()> {
        Ok(())
    }

    fn parse(&self, string: &str) -> Result<Node> {
        Ok(serde_json::from_str(string).unwrap_or_else(|_| Node::String(string.to_string())))
    }

    fn coerce(&self, node: &Node) -> Node {
        node.clone()
    }

    fn default_(&self) -> Node {
        Node::Null(Null {})
    }
}

/// [`Validator`] implementation for `ConstantValidator`: checks against constant value
impl Validator for ConstantValidator {
    fn validate(&self, node: &Node) -> Result<()> {
        match *node == *self.value {
            true => Ok(()),
            false => bail!("Value is not equal to constant"),
        }
    }

    fn coerce(&self, _node: &Node) -> Node {
        (*self.value).clone()
    }

    fn default_(&self) -> Node {
        (*self.value).clone()
    }
}

/// [`Validator`] implementation for `EnumValidator`: checks that value is in `values`
impl Validator for EnumValidator {
    fn validate(&self, node: &Node) -> Result<()> {
        match self.values.contains(node) {
            true => Ok(()),
            false => bail!("Value is not in enumeration"),
        }
    }

    fn coerce(&self, node: &Node) -> Node {
        match self.values.contains(node) {
            true => node.clone(),
            false => self.default_(),
        }
    }

    fn default_(&self) -> Node {
        self.values.first().cloned().unwrap_or(Node::Null(Null {}))
    }
}

/// [`Validator`] implementation for `BooleanValidator`: coerces values to a boolean
impl Validator for BooleanValidator {
    fn validate(&self, node: &Node) -> Result<()> {
        if matches!(node, Node::Boolean(..)) {
            Ok(())
        } else {
            bail!("Expected a `Boolean` got a `{}` value", node.as_ref())
        }
    }

    fn parse(&self, string: &str) -> Result<Node> {
        Ok(match string {
            "0" | "false" | "no" | "off" => Node::Boolean(false),
            "1" | "true" | "yes" | "on" => Node::Boolean(true),
            _ => bail!("Unable to parse string as `Boolean`: {}", string),
        })
    }

    fn coerce(&self, node: &Node) -> Node {
        Node::Boolean(match node {
            Node::Null(..) => false,
            Node::Boolean(bool) => *bool,
            Node::Number(number) => number.0 > 0f64,
            Node::String(string) => {
                !(string.to_lowercase() == "false"
                    || string == "0"
                    || string.to_lowercase() == "no"
                    || string.to_lowercase() == "off")
            }
            _ => true,
        })
    }

    fn default_(&self) -> Node {
        Node::Boolean(false)
    }
}

impl Validator for NumberValidator {
    fn validate(&self, node: &Node) -> Result<()> {
        let num = if let Node::Number(Number(num)) = node {
            *num
        } else if let Node::Integer(num) = node {
            *num as f64
        } else {
            bail!("Expected a `Number` got a `{}`", node.as_ref())
        };

        if let Some(Number(min)) = self.minimum {
            if num < min {
                bail!("Number is less than minimum `{}`", min)
            }
        }

        if let Some(Number(min)) = self.exclusive_minimum {
            if num <= min {
                bail!(
                    "Number is less than, or equal to, exclusive minimum `{}`",
                    min
                )
            }
        }

        if let Some(Number(max)) = self.maximum {
            if num > max {
                bail!("Number is greater than maximum `{}`", max)
            }
        }

        if let Some(Number(max)) = self.exclusive_maximum {
            if num >= max {
                bail!(
                    "Number is greater than, or equal to, exclusive maximum `{}`",
                    max
                )
            }
        }

        if let Some(Number(modulus)) = self.multiple_of {
            if Number(num % modulus) != Number(0.0) {
                bail!("Number is not a multiple of `{}`", modulus)
            }
        }

        Ok(())
    }

    fn coerce(&self, node: &Node) -> Node {
        Node::Number(Number(match node {
            Node::Null(..) => 0.,
            Node::Boolean(bool) => match bool {
                true => 1.0,
                false => 0.0,
            },
            Node::Number(number) => number.0,
            Node::String(string) => string.parse().unwrap_or_default(),
            _ => return self.default_(),
        }))
    }

    fn default_(&self) -> Node {
        let min = self.minimum.as_ref().map(|min| min.0).unwrap_or_default();
        Node::Number(Number(min))
    }
}

impl Validator for IntegerValidator {
    fn validate(&self, node: &Node) -> Result<()> {
        if matches!(node, Node::Integer(..)) {
            NumberValidator {
                minimum: self.minimum,
                exclusive_minimum: self.exclusive_minimum,
                maximum: self.maximum,
                exclusive_maximum: self.exclusive_maximum,
                multiple_of: self.multiple_of,
                ..Default::default()
            }
            .validate(node)
        } else {
            bail!("Expected an `Integer` got a `{}`", node.as_ref())
        }
    }

    fn coerce(&self, node: &Node) -> Node {
        Node::Integer(match node {
            Node::Null(..) => 0,
            Node::Boolean(bool) => match bool {
                true => 1,
                false => 0,
            },
            Node::Number(number) => number.0 as i64,
            Node::String(string) => string.parse().unwrap_or_default(),
            _ => return self.default_(),
        })
    }

    fn default_(&self) -> Node {
        let min = self.minimum.as_ref().map(|min| min.0).unwrap_or_default();
        Node::Integer(min as i64)
    }
}

impl Validator for StringValidator {
    fn validate(&self, node: &Node) -> Result<()> {
        let string = if let Node::String(string) = node {
            string
        } else {
            bail!("Expected a `String` got a `{}`", node.as_ref())
        };

        if let Some(min) = self.min_length {
            if string.len() < min as usize {
                bail!("String is shorter than minimum length `{}`", min)
            }
        }

        if let Some(max) = self.max_length {
            if string.len() > max as usize {
                bail!("String is longer than maximum length `{}`", max)
            }
        }

        if let Some(pattern) = &self.pattern {
            let regex = Regex::new(pattern)?;
            if !regex.is_match(string) {
                bail!("String does not match pattern `{}`", pattern)
            }
        }

        Ok(())
    }

    fn parse(&self, string: &str) -> Result<Node> {
        let node = Node::String(string.to_owned());
        self.validate(&node)?;
        Ok(node)
    }

    fn coerce(&self, node: &Node) -> Node {
        let mut string = match node {
            Node::String(string) => string.clone(),
            _ => serde_json::to_string(node).unwrap_or_default(),
        };

        if let Some(min) = self.min_length {
            if string.len() < min as usize {
                string = format!("{:width$}", string, width = min as usize);
            }
        }

        if let Some(max) = self.max_length {
            if string.len() > max as usize {
                string.truncate(max as usize);
            }
        }

        if let Some(pattern) = &self.pattern {
            if let Ok(regex) = Regex::new(pattern) {
                if !regex.is_match(&string) {
                    return self.default_();
                }
            }
        }

        Node::String(string)
    }

    fn default_(&self) -> Node {
        Node::String(if let Some(min) = self.min_length {
            " ".repeat(min as usize)
        } else {
            String::new()
        })
    }
}

impl Validator for ArrayValidator {
    fn validate(&self, _node: &Node) -> Result<()> {
        tracing::error!("ArrayValidator.validate not yet implemented");
        Ok(())
    }

    fn coerce(&self, _node: &Node) -> Node {
        tracing::error!("ArrayValidator.coerce not yet implemented");
        self.default_()
    }

    fn default_(&self) -> Node {
        Node::Array(Vec::new())
    }
}

impl Validator for TupleValidator {
    fn validate(&self, _node: &Node) -> Result<()> {
        tracing::error!("TupleValidator.validate not yet implemented");
        Ok(())
    }

    fn coerce(&self, _node: &Node) -> Node {
        tracing::error!("TupleValidator.validate not yet implemented");
        self.default_()
    }

    fn default_(&self) -> Node {
        Node::Array(Vec::new())
    }
}
<commit_msg>fix(Node validation): Use empty string as default for `EnumValidator` (if no `values`)<commit_after>use common::{
    eyre::{bail, Result},
    regex::Regex,
    serde_json, tracing,
};
use node_dispatch::dispatch_validator;
use stencila_schema::{
    self, ArrayValidator, BooleanValidator, ConstantValidator, EnumValidator, IntegerValidator,
    Node, Number, NumberValidator, StringValidator, TupleValidator, ValidatorTypes,
};

/// A trait for applying different [`ValidatorTypes`] to other nodes
pub trait Validator {
    /// Check that `node` is valid against the validator
    ///
    /// Returns `Ok` if the node is valid and a descriptive, user facing `Error`
    /// if it is not.
    fn validate(&self, node: &Node) -> Result<()>;

    /// Parse a string to a `Node` that is valid against the validator
    ///
    /// Returns `Ok(node)` if the string can be parsed, an error otherwise.
    fn parse(&self, string: &str) -> Result<Node> {
        let node =
            serde_json::from_str(string).unwrap_or_else(|_| Node::String(string.to_string()));
        self.validate(&node)?;
        Ok(node)
    }

    /// Coerce the `node` to be valid against the validator
    ///
    /// Returns the coerced node.
    fn coerce(&self, node: &Node) -> Node;

    /// Get the default node for the validator
    fn default_(&self) -> Node;
}

/// [`Validator`] implementation for `ValidatorTypes`: just dispatches to variants
impl Validator for ValidatorTypes {
    fn validate(&self, node: &Node) -> Result<()> {
        dispatch_validator!(self, validate, node)
    }

    fn parse(&self, string: &str) -> Result<Node> {
        dispatch_validator!(self, parse, string)
    }

    fn coerce(&self, node: &Node) -> Node {
        dispatch_validator!(self, coerce, node)
    }

    fn default_(&self) -> Node {
        dispatch_validator!(self, default_)
    }
}

/// [`Validator`] implementation for abstract base `Validator`: does nothing
impl Validator for stencila_schema::Validator {
    fn validate(&self, _node: &Node) -> Result<()> {
        Ok(())
    }

    fn parse(&self, string: &str) -> Result<Node> {
        Ok(serde_json::from_str(string).unwrap_or_else(|_| Node::String(string.to_string())))
    }

    fn coerce(&self, node: &Node) -> Node {
        node.clone()
    }

    fn default_(&self) -> Node {
        Node::String(String::new())
    }
}

/// [`Validator`] implementation for `ConstantValidator`: checks against constant value
impl Validator for ConstantValidator {
    fn validate(&self, node: &Node) -> Result<()> {
        match *node == *self.value {
            true => Ok(()),
            false => bail!("Value is not equal to constant"),
        }
    }

    fn coerce(&self, _node: &Node) -> Node {
        (*self.value).clone()
    }

    fn default_(&self) -> Node {
        (*self.value).clone()
    }
}

/// [`Validator`] implementation for `EnumValidator`: checks that value is in `values`
impl Validator for EnumValidator {
    fn validate(&self, node: &Node) -> Result<()> {
        match self.values.contains(node) {
            true => Ok(()),
            false => bail!("Value is not in enumeration"),
        }
    }

    fn coerce(&self, node: &Node) -> Node {
        match self.values.contains(node) {
            true => node.clone(),
            false => self.default_(),
        }
    }

    fn default_(&self) -> Node {
        // Previously we used `Null` if there were no `values`. However, that is
        // problematic for serialization-deserialization (e.g. JSON) as `null` implies
        // the property is `None`.
        self.values
            .first()
            .cloned()
            .unwrap_or(Node::String(String::new()))
    }
}

/// [`Validator`] implementation for `BooleanValidator`: coerces values to a boolean
impl Validator for BooleanValidator {
    fn validate(&self, node: &Node) -> Result<()> {
        if matches!(node, Node::Boolean(..)) {
            Ok(())
        } else {
            bail!("Expected a `Boolean` got a `{}` value", node.as_ref())
        }
    }

    fn parse(&self, string: &str) -> Result<Node> {
        Ok(match string {
            "0" | "false" | "no" | "off" => Node::Boolean(false),
            "1" | "true" | "yes" | "on" => Node::Boolean(true),
            _ => bail!("Unable to parse string as `Boolean`: {}", string),
        })
    }

    fn coerce(&self, node: &Node) -> Node {
        Node::Boolean(match node {
            Node::Null(..) => false,
            Node::Boolean(bool) => *bool,
            Node::Number(number) => number.0 > 0f64,
            Node::String(string) => {
                !(string.to_lowercase() == "false"
                    || string == "0"
                    || string.to_lowercase() == "no"
                    || string.to_lowercase() == "off")
            }
            _ => true,
        })
    }

    fn default_(&self) -> Node {
        Node::Boolean(false)
    }
}

impl Validator for NumberValidator {
    fn validate(&self, node: &Node) -> Result<()> {
        let num = if let Node::Number(Number(num)) = node {
            *num
        } else if let Node::Integer(num) = node {
            *num as f64
        } else {
            bail!("Expected a `Number` got a `{}`", node.as_ref())
        };

        if let Some(Number(min)) = self.minimum {
            if num < min {
                bail!("Number is less than minimum `{}`", min)
            }
        }

        if let Some(Number(min)) = self.exclusive_minimum {
            if num <= min {
                bail!(
                    "Number is less than, or equal to, exclusive minimum `{}`",
                    min
                )
            }
        }

        if let Some(Number(max)) = self.maximum {
            if num > max {
                bail!("Number is greater than maximum `{}`", max)
            }
        }

        if let Some(Number(max)) = self.exclusive_maximum {
            if num >= max {
                bail!(
                    "Number is greater than, or equal to, exclusive maximum `{}`",
                    max
                )
            }
        }

        if let Some(Number(modulus)) = self.multiple_of {
            if Number(num % modulus) != Number(0.0) {
                bail!("Number is not a multiple of `{}`", modulus)
            }
        }

        Ok(())
    }

    fn coerce(&self, node: &Node) -> Node {
        Node::Number(Number(match node {
            Node::Null(..) => 0.,
            Node::Boolean(bool) => match bool {
                true => 1.0,
                false => 0.0,
            },
            Node::Number(number) => number.0,
            Node::String(string) => string.parse().unwrap_or_default(),
            _ => return self.default_(),
        }))
    }

    fn default_(&self) -> Node {
        let min = self.minimum.as_ref().map(|min| min.0).unwrap_or_default();
        Node::Number(Number(min))
    }
}

impl Validator for IntegerValidator {
    fn validate(&self, node: &Node) -> Result<()> {
        if matches!(node, Node::Integer(..)) {
            NumberValidator {
                minimum: self.minimum,
                exclusive_minimum: self.exclusive_minimum,
                maximum: self.maximum,
                exclusive_maximum: self.exclusive_maximum,
                multiple_of: self.multiple_of,
                ..Default::default()
            }
            .validate(node)
        } else {
            bail!("Expected an `Integer` got a `{}`", node.as_ref())
        }
    }

    fn coerce(&self, node: &Node) -> Node {
        Node::Integer(match node {
            Node::Null(..) => 0,
            Node::Boolean(bool) => match bool {
                true => 1,
                false => 0,
            },
            Node::Number(number) => number.0 as i64,
            Node::String(string) => string.parse().unwrap_or_default(),
            _ => return self.default_(),
        })
    }

    fn default_(&self) -> Node {
        let min = self.minimum.as_ref().map(|min| min.0).unwrap_or_default();
        Node::Integer(min as i64)
    }
}

impl Validator for StringValidator {
    fn validate(&self, node: &Node) -> Result<()> {
        let string = if let Node::String(string) = node {
            string
        } else {
            bail!("Expected a `String` got a `{}`", node.as_ref())
        };

        if let Some(min) = self.min_length {
            if string.len() < min as usize {
                bail!("String is shorter than minimum length `{}`", min)
            }
        }

        if let Some(max) = self.max_length {
            if string.len() > max as usize {
                bail!("String is longer than maximum length `{}`", max)
            }
        }

        if let Some(pattern) = &self.pattern {
            let regex = Regex::new(pattern)?;
            if !regex.is_match(string) {
                bail!("String does not match pattern `{}`", pattern)
            }
        }

        Ok(())
    }

    fn parse(&self, string: &str) -> Result<Node> {
        let node = Node::String(string.to_owned());
        self.validate(&node)?;
        Ok(node)
    }

    fn coerce(&self, node: &Node) -> Node {
        let mut string = match node {
            Node::String(string) => string.clone(),
            _ => serde_json::to_string(node).unwrap_or_default(),
        };

        if let Some(min) = self.min_length {
            if string.len() < min as usize {
                string = format!("{:width$}", string, width = min as usize);
            }
        }

        if let Some(max) = self.max_length {
            if string.len() > max as usize {
                string.truncate(max as usize);
            }
        }

        if let Some(pattern) = &self.pattern {
            if let Ok(regex) = Regex::new(pattern) {
                if !regex.is_match(&string) {
                    return self.default_();
                }
            }
        }

        Node::String(string)
    }

    fn default_(&self) -> Node {
        Node::String(if let Some(min) = self.min_length {
            " ".repeat(min as usize)
        } else {
            String::new()
        })
    }
}

impl Validator for ArrayValidator {
    fn validate(&self, _node: &Node) -> Result<()> {
        tracing::error!("ArrayValidator.validate not yet implemented");
        Ok(())
    }

    fn coerce(&self, _node: &Node) -> Node {
        tracing::error!("ArrayValidator.coerce not yet implemented");
        self.default_()
    }

    fn default_(&self) -> Node {
        Node::Array(Vec::new())
    }
}

impl Validator for TupleValidator {
    fn validate(&self, _node: &Node) -> Result<()> {
        tracing::error!("TupleValidator.validate not yet implemented");
        Ok(())
    }

    fn coerce(&self, _node: &Node) -> Node {
        tracing::error!("TupleValidator.validate not yet implemented");
        self.default_()
    }

    fn default_(&self) -> Node {
        Node::Array(Vec::new())
    }
}
<|endoftext|>