<commit_before>use std::collections::HashMap;
use std::iter::Iterator;

fn main() {
    println!("pub const INVALID_VALUE: u8 = 255;");

    // A-Z
    let standard_alphabet: Vec<u8> = (0x41..0x5B)
        // a-z
        .chain(0x61..0x7B)
        // 0-9
        .chain(0x30..0x3A)
        // +
        .chain(0x2B..0x2C)
        // /
        .chain(0x2F..0x30)
        .collect();
    print_encode_table(&standard_alphabet, "STANDARD_ENCODE", 0);
    print_decode_table(&standard_alphabet, "STANDARD_DECODE", 0);

    // A-Z
    let url_alphabet: Vec<u8> = (0x41..0x5B)
        // a-z
        .chain(0x61..0x7B)
        // 0-9
        .chain(0x30..0x3A)
        // -
        .chain(0x2D..0x2E)
        // _s
        .chain(0x5F..0x60)
        .collect();
    print_encode_table(&url_alphabet, "URL_SAFE_ENCODE", 0);
    print_decode_table(&url_alphabet, "URL_SAFE_DECODE", 0);

    // ./0123456789
    let crypt_alphabet: Vec<u8> = (b'.'..(b'9' + 1))
        // A-Z
        .chain(b'A'..(b'Z' + 1))
        // a-z
        .chain(b'a'..(b'z' + 1))
        .collect();
    print_encode_table(&crypt_alphabet, "CRYPT_ENCODE", 0);
    print_decode_table(&crypt_alphabet, "CRYPT_DECODE", 0);

    // A-Z
    let imap_alphabet: Vec<u8> = (0x41..0x5B)
        // a-z
        .chain(0x61..0x7B)
        // 0-9
        .chain(0x30..0x3A)
        // +
        .chain(0x2B..0x2C)
        // ,
        .chain(0x2C..0x2D)
        .collect();
    print_encode_table(&imap_alphabet, "IMAP_MUTF7_ENCODE", 0);
    print_decode_table(&imap_alphabet, "IMAP_MUTF7_DECODE", 0);
}

fn print_encode_table(alphabet: &[u8], const_name: &str, indent_depth: usize) {
    println!("#[rustfmt::skip]");
    println!(
        "{:width$}pub const {}: &[u8; 64] = &[",
        "",
        const_name,
        width = indent_depth
    );

    for (i, b) in alphabet.iter().enumerate() {
        println!(
            "{:width$}{}, // input {} (0x{:X}) => '{}' (0x{:X})",
            "",
            b,
            i,
            i,
            String::from_utf8(vec![*b as u8]).unwrap(),
            b,
            width = indent_depth + 4
        );
    }

    println!("{:width$}];", "", width = indent_depth);
}

fn print_decode_table(alphabet: &[u8], const_name: &str, indent_depth: usize) {
    // map of alphabet bytes to 6-bit morsels
    let mut input_to_morsel = HashMap::<u8, u8>::new();

    // standard base64 alphabet bytes, in order
    for (morsel, ascii_byte) in alphabet.iter().enumerate() {
        // truncation cast is fine here
        let _ = input_to_morsel.insert(*ascii_byte, morsel as u8);
    }

    println!("#[rustfmt::skip]");
    println!(
        "{:width$}pub const {}: &[u8; 256] = &[",
        "",
        const_name,
        width = indent_depth
    );
    for ascii_byte in 0..256 {
        let (value, comment) = match input_to_morsel.get(&(ascii_byte as u8)) {
            None => (
                "INVALID_VALUE".to_string(),
                format!("input {} (0x{:X})", ascii_byte, ascii_byte),
            ),
            Some(v) => (
                format!("{}", *v),
                format!(
                    "input {} (0x{:X} char '{}') => {} (0x{:X})",
                    ascii_byte,
                    ascii_byte,
                    String::from_utf8(vec![ascii_byte as u8]).unwrap(),
                    *v,
                    *v
                ),
            ),
        };

        println!(
            "{:width$}{}, // {}",
            "",
            value,
            comment,
            width = indent_depth + 4
        );
    }
    println!("{:width$}];", "", width = indent_depth);
}
<commit_msg>Fix stray character in comment<commit_after>use std::collections::HashMap;
use std::iter::Iterator;

fn main() {
    println!("pub const INVALID_VALUE: u8 = 255;");

    // A-Z
    let standard_alphabet: Vec<u8> = (0x41..0x5B)
        // a-z
        .chain(0x61..0x7B)
        // 0-9
        .chain(0x30..0x3A)
        // +
        .chain(0x2B..0x2C)
        // /
        .chain(0x2F..0x30)
        .collect();
    print_encode_table(&standard_alphabet, "STANDARD_ENCODE", 0);
    print_decode_table(&standard_alphabet, "STANDARD_DECODE", 0);

    // A-Z
    let url_alphabet: Vec<u8> = (0x41..0x5B)
        // a-z
        .chain(0x61..0x7B)
        // 0-9
        .chain(0x30..0x3A)
        // -
        .chain(0x2D..0x2E)
        // _
        .chain(0x5F..0x60)
        .collect();
    print_encode_table(&url_alphabet, "URL_SAFE_ENCODE", 0);
    print_decode_table(&url_alphabet, "URL_SAFE_DECODE", 0);

    // ./0123456789
    let crypt_alphabet: Vec<u8> = (b'.'..(b'9' + 1))
        // A-Z
        .chain(b'A'..(b'Z' + 1))
        // a-z
        .chain(b'a'..(b'z' + 1))
        .collect();
    print_encode_table(&crypt_alphabet, "CRYPT_ENCODE", 0);
    print_decode_table(&crypt_alphabet, "CRYPT_DECODE", 0);

    // A-Z
    let imap_alphabet: Vec<u8> = (0x41..0x5B)
        // a-z
        .chain(0x61..0x7B)
        // 0-9
        .chain(0x30..0x3A)
        // +
        .chain(0x2B..0x2C)
        // ,
        .chain(0x2C..0x2D)
        .collect();
    print_encode_table(&imap_alphabet, "IMAP_MUTF7_ENCODE", 0);
    print_decode_table(&imap_alphabet, "IMAP_MUTF7_DECODE", 0);
}

fn print_encode_table(alphabet: &[u8], const_name: &str, indent_depth: usize) {
    println!("#[rustfmt::skip]");
    println!(
        "{:width$}pub const {}: &[u8; 64] = &[",
        "",
        const_name,
        width = indent_depth
    );

    for (i, b) in alphabet.iter().enumerate() {
        println!(
            "{:width$}{}, // input {} (0x{:X}) => '{}' (0x{:X})",
            "",
            b,
            i,
            i,
            String::from_utf8(vec![*b as u8]).unwrap(),
            b,
            width = indent_depth + 4
        );
    }

    println!("{:width$}];", "", width = indent_depth);
}

fn print_decode_table(alphabet: &[u8], const_name: &str, indent_depth: usize) {
    // map of alphabet bytes to 6-bit morsels
    let mut input_to_morsel = HashMap::<u8, u8>::new();

    // standard base64 alphabet bytes, in order
    for (morsel, ascii_byte) in alphabet.iter().enumerate() {
        // truncation cast is fine here
        let _ = input_to_morsel.insert(*ascii_byte, morsel as u8);
    }

    println!("#[rustfmt::skip]");
    println!(
        "{:width$}pub const {}: &[u8; 256] = &[",
        "",
        const_name,
        width = indent_depth
    );
    for ascii_byte in 0..256 {
        let (value, comment) = match input_to_morsel.get(&(ascii_byte as u8)) {
            None => (
                "INVALID_VALUE".to_string(),
                format!("input {} (0x{:X})", ascii_byte, ascii_byte),
            ),
            Some(v) => (
                format!("{}", *v),
                format!(
                    "input {} (0x{:X} char '{}') => {} (0x{:X})",
                    ascii_byte,
                    ascii_byte,
                    String::from_utf8(vec![ascii_byte as u8]).unwrap(),
                    *v,
                    *v
                ),
            ),
        };

        println!(
            "{:width$}{}, // {}",
            "",
            value,
            comment,
            width = indent_depth + 4
        );
    }
    println!("{:width$}];", "", width = indent_depth);
}
<|endoftext|>


<commit_before>#![allow(unused_imports, dead_code)]
extern crate env_logger;
extern crate handlebars;
#[cfg(not(feature = "serde_type"))]
extern crate rustc_serialize;

use std::fs::File;
use std::path::Path;

use handlebars::{Handlebars, RenderError, RenderContext, Helper, Context, JsonRender};

fn format_helper (c: &Context, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let param = h.params().get(0).unwrap();
    let rendered = format!("{} pts", c.navigate(rc.get_path(), param).render());
    try!(rc.writer.write(rendered.into_bytes().as_ref()));
    Ok(())
}

#[cfg(not(feature = "serde_type"))]
mod render_example {
    use std::collections::BTreeMap;
    use rustc_serialize::json::{Json, ToJson};

    pub struct Team {
        name: String,
        pts: u16
    }

    impl ToJson for Team {
        fn to_json(&self) -> Json {
            let mut m: BTreeMap<String, Json> = BTreeMap::new();
            m.insert("name".to_string(), self.name.to_json());
            m.insert("pts".to_string(), self.pts.to_json());
            m.to_json()
        }
    }

    pub fn make_data () -> BTreeMap<String, Json> {
        let mut data = BTreeMap::new();

        data.insert("year".to_string(), "2015".to_json());

        let teams = vec![ Team { name: "Jiangsu Sainty".to_string(),
                                 pts: 43u16 },
                          Team { name: "Beijing Guoan".to_string(),
                                 pts: 27u16 },
                          Team { name: "Guangzhou Evergrand".to_string(),
                                 pts: 22u16 },
                          Team { name: "Shandong Luneng".to_string(),
                                 pts: 12u16 } ];

        data.insert("teams".to_string(), teams.to_json());
        data
    }
}

#[cfg(not(feature = "serde_type"))]
fn main() {
    use render_example::*;

    env_logger::init().unwrap();
    let mut handlebars = Handlebars::new();

    handlebars.register_template_file("table", &Path::new("./examples/template.hbs")).ok().unwrap();

    handlebars.register_helper("format", Box::new(format_helper));
    //    handlebars.register_helper("format", Box::new(FORMAT_HELPER));

    let data = {
        let data = make_data();
        Context::wraps(&data)
    };

    if let Ok(ref mut file) = File::create("target/table.html") {
        if let Ok(_) = handlebars.renderw("table", &data, file) {
            println!("target/table.html generated");
        } else {
            println!("Failed to geneate target/table.html");
        }
    } else {
        println!("Failed to create target/table.html");
    }
}

#[cfg(feature = "serde_type")]
fn main() {}
<commit_msg>(fix) fixed source trait missing error<commit_after>#![allow(unused_imports, dead_code)]
extern crate env_logger;
extern crate handlebars;
#[cfg(not(feature = "serde_type"))]
extern crate rustc_serialize;

use std::io::Write;
use std::fs::File;
use std::path::Path;

use handlebars::{Handlebars, RenderError, RenderContext, Helper, Context, JsonRender};

fn format_helper (c: &Context, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let param = h.params().get(0).unwrap();
    let rendered = format!("{} pts", c.navigate(rc.get_path(), param).render());
    try!(rc.writer.write(rendered.into_bytes().as_ref()));
    Ok(())
}

#[cfg(not(feature = "serde_type"))]
mod render_example {
    use std::collections::BTreeMap;
    use rustc_serialize::json::{Json, ToJson};

    pub struct Team {
        name: String,
        pts: u16
    }

    impl ToJson for Team {
        fn to_json(&self) -> Json {
            let mut m: BTreeMap<String, Json> = BTreeMap::new();
            m.insert("name".to_string(), self.name.to_json());
            m.insert("pts".to_string(), self.pts.to_json());
            m.to_json()
        }
    }

    pub fn make_data () -> BTreeMap<String, Json> {
        let mut data = BTreeMap::new();

        data.insert("year".to_string(), "2015".to_json());

        let teams = vec![ Team { name: "Jiangsu Sainty".to_string(),
                                 pts: 43u16 },
                          Team { name: "Beijing Guoan".to_string(),
                                 pts: 27u16 },
                          Team { name: "Guangzhou Evergrand".to_string(),
                                 pts: 22u16 },
                          Team { name: "Shandong Luneng".to_string(),
                                 pts: 12u16 } ];

        data.insert("teams".to_string(), teams.to_json());
        data
    }
}

#[cfg(not(feature = "serde_type"))]
fn main() {
    use render_example::*;

    env_logger::init().unwrap();
    let mut handlebars = Handlebars::new();

    handlebars.register_template_file("table", &Path::new("./examples/template.hbs")).ok().unwrap();

    handlebars.register_helper("format", Box::new(format_helper));
    //    handlebars.register_helper("format", Box::new(FORMAT_HELPER));

    let data = {
        let data = make_data();
        Context::wraps(&data)
    };

    if let Ok(ref mut file) = File::create("target/table.html") {
        if let Ok(_) = handlebars.renderw("table", &data, file) {
            println!("target/table.html generated");
        } else {
            println!("Failed to geneate target/table.html");
        }
    } else {
        println!("Failed to create target/table.html");
    }
}

#[cfg(feature = "serde_type")]
fn main() {}
<|endoftext|>


<commit_before><commit_msg>Fix clippy lint<commit_after><|endoftext|>


<commit_before>//! pretty printing implementation

use read_fonts::traversal::{FieldType, OffsetType, ResolvedOffset, SomeArray, SomeTable};

pub struct PrettyPrinter<'a> {
    depth: usize,
    indent_size: usize,
    writer: &'a mut (dyn std::io::Write + 'a),
}

impl<'a> PrettyPrinter<'a> {
    pub fn new(writer: &'a mut (dyn std::io::Write + 'a)) -> Self {
        PrettyPrinter {
            depth: 0,
            indent_size: 2,
            writer,
        }
    }

    fn print_indent(&mut self) -> std::io::Result<()> {
        static MANY_SPACES: [u8; 200] = [0x20; 200];
        let indent_len = (self.depth * self.indent_size).min(MANY_SPACES.len());
        self.writer.write_all(&MANY_SPACES[..indent_len])
    }

    fn indented(
        &mut self,
        f: impl FnOnce(&mut PrettyPrinter) -> std::io::Result<()>,
    ) -> std::io::Result<()> {
        self.depth += 1;
        let r = f(self);
        self.depth -= 1;
        r
    }

    pub fn print_newline(&mut self) -> std::io::Result<()> {
        self.writer.write_all(b"\n")
    }

    pub fn print_root_table<'b>(
        &mut self,
        table: &(dyn SomeTable<'b> + 'b),
    ) -> std::io::Result<()> {
        self.print_indent()?;
        self.writer.write_all(table.type_name().as_bytes())?;
        self.print_newline()?;
        self.indented(|this| this.print_fields(table))?;
        self.print_newline()
    }

    fn print_fields<'b>(&mut self, table: &(dyn SomeTable<'b> + 'b)) -> std::io::Result<()> {
        for (i, field) in table.iter().enumerate() {
            if i != 0 {
                self.print_newline()?;
            }
            self.print_indent()?;
            self.writer.write_all(field.name.as_bytes())?;
            self.writer.write_all(": ".as_bytes())?;
            self.print_field(&field.typ)?;
        }
        Ok(())
    }

    fn print_array<'b>(&mut self, array: &(dyn SomeArray<'b> + 'b)) -> std::io::Result<()> {
        self.writer
            .write_fmt(format_args!("[{}]\n", array.type_name()))?;
        self.indented(|this| {
            for (i, item) in array.iter().enumerate() {
                if i != 0 {
                    this.print_newline()?;
                }
                if is_scalar(&item) {
                    this.print_indent()?;
                }
                this.print_field(&item)?;
            }
            Ok(())
        })
    }

    fn print_offset(&mut self, offset: OffsetType) -> std::io::Result<()> {
        let offset = offset.to_u32();
        let hex_width = super::hex_width(offset);
        self.writer
            .write_fmt(format_args!("0x{offset:0hex_width$X}"))
    }

    pub fn print_field<'b>(&mut self, field: &FieldType<'b>) -> std::io::Result<()> {
        match &field {
            FieldType::I8(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::U8(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::I16(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::U16(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::I32(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::U32(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::U24(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::Tag(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::FWord(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::UfWord(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::MajorMinor(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::Version16Dot16(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::F2Dot14(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::Fixed(val) => self.writer.write_fmt(format_args!("{val}"))?,
            FieldType::LongDateTime(val) => self.writer.write_fmt(format_args!("{val:?}"))?,
            FieldType::GlyphId(val) => self.writer.write_fmt(format_args!("{}", val.to_u16()))?,
            FieldType::ResolvedOffset(ResolvedOffset { offset, target }) => {
                self.print_offset(*offset)?;
                self.print_newline()?;
                match target {
                    Ok(table) => self.indented(|this| this.print_fields(table)),
                    Err(e) => self.writer.write_fmt(format_args!("Error: '{e}'")),
                }?;
            }
            FieldType::BareOffset(offset) => self.print_offset(*offset)?,
            FieldType::Record(record) => self.print_fields(record)?,
            FieldType::ValueRecord(record) if record.get_field(0).is_none() => {
                self.writer.write_all(b"Null")?
            }
            FieldType::ValueRecord(record) => self.indented(|this| {
                this.print_newline()?;
                this.print_fields(record)
            })?,
            FieldType::Array(array) => self.print_array(array)?,
            FieldType::None => self.writer.write_all(b"None")?,
        }
        Ok(())
    }
}

fn is_scalar(field_type: &FieldType) -> bool {
    matches!(
        field_type,
        FieldType::I8(_)
            | FieldType::U8(_)
            | FieldType::I16(_)
            | FieldType::U16(_)
            | FieldType::I32(_)
            | FieldType::U32(_)
            | FieldType::U24(_)
            | FieldType::Tag(_)
            | FieldType::FWord(_)
            | FieldType::UfWord(_)
            | FieldType::MajorMinor(_)
            | FieldType::Version16Dot16(_)
            | FieldType::F2Dot14(_)
            | FieldType::Fixed(_)
            | FieldType::LongDateTime(_)
            | FieldType::GlyphId(_)
    )
}
<commit_msg>[otexplorer] Print facing hex<commit_after>//! pretty printing implementation

use std::io::Write;

use read_fonts::traversal::{FieldType, OffsetType, ResolvedOffset, SomeArray, SomeTable};

static MANY_SPACES: [u8; 200] = [0x20; 200];
// width of the left column, which contains the textual representation.
const L_COLUMN_WIDTH: usize = 59;
// position of array indexes, if they are printed
const ARRAY_POS_WIDTH: usize = 50;

pub struct PrettyPrinter<'a> {
    depth: usize,
    line_pos: usize,
    cur_array_item: Option<usize>,
    indent_size: usize,
    writer: &'a mut (dyn std::io::Write + 'a),
}

impl<'a> std::io::Write for PrettyPrinter<'a> {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        let len = self.writer.write(buf)?;
        let wrote_buf = &buf[..len];
        self.line_pos = match wrote_buf.iter().rev().position(|b| *b == b'\n') {
            Some(pos) => pos,
            None => self.line_pos + len,
        };
        Ok(len)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        self.writer.flush()
    }
}

impl<'a> PrettyPrinter<'a> {
    pub fn new(writer: &'a mut (dyn std::io::Write + 'a)) -> Self {
        PrettyPrinter {
            depth: 0,
            line_pos: 0,
            cur_array_item: None,
            indent_size: 2,
            writer,
        }
    }

    fn print_indent(&mut self) -> std::io::Result<()> {
        let indent_len = (self.depth * self.indent_size).min(MANY_SPACES.len());
        self.write_all(&MANY_SPACES[..indent_len])
    }

    fn indented(
        &mut self,
        f: impl FnOnce(&mut PrettyPrinter) -> std::io::Result<()>,
    ) -> std::io::Result<()> {
        self.depth += 1;
        let r = f(self);
        self.depth -= 1;
        r
    }

    pub fn print_newline(&mut self) -> std::io::Result<()> {
        writeln!(self)
    }

    pub fn print_root_table<'b>(
        &mut self,
        table: &(dyn SomeTable<'b> + 'b),
    ) -> std::io::Result<()> {
        self.print_indent()?;
        write!(self, "{}", table.type_name())?;
        self.print_newline()?;
        self.indented(|this| this.print_fields(table))?;
        self.print_newline()
    }

    fn print_fields<'b>(&mut self, table: &(dyn SomeTable<'b> + 'b)) -> std::io::Result<()> {
        for (i, field) in table.iter().enumerate() {
            if i != 0 {
                self.print_newline()?;
            }
            self.print_indent()?;
            write!(self, "{}: ", field.name)?;
            self.print_field(&field.typ)?;
        }
        Ok(())
    }

    fn print_array<'b>(&mut self, array: &(dyn SomeArray<'b> + 'b)) -> std::io::Result<()> {
        writeln!(self, "[{}]", array.type_name())?;
        self.indented(|this| {
            for (i, item) in array.iter().enumerate() {
                this.cur_array_item = Some(i);
                if i != 0 {
                    this.print_newline()?;
                }
                if is_scalar(&item) {
                    this.print_indent()?;
                }
                this.print_field(&item)?;
            }
            Ok(())
        })
    }

    pub fn print_field<'b>(&mut self, field: &FieldType<'b>) -> std::io::Result<()> {
        match &field {
            FieldType::I8(val) => write!(self, "{val}")?,
            FieldType::U8(val) => write!(self, "{val}")?,
            FieldType::I16(val) => write!(self, "{val}")?,
            FieldType::U16(val) => write!(self, "{val}")?,
            FieldType::I32(val) => write!(self, "{val}")?,
            FieldType::U32(val) => write!(self, "{val}")?,
            FieldType::U24(val) => write!(self, "{val}")?,
            FieldType::Tag(val) => write!(self, "{val}")?,
            FieldType::FWord(val) => write!(self, "{val}")?,
            FieldType::UfWord(val) => write!(self, "{val}")?,
            FieldType::MajorMinor(val) => write!(self, "{val}")?,
            FieldType::Version16Dot16(val) => write!(self, "{val}")?,
            FieldType::F2Dot14(val) => write!(self, "{val}")?,
            FieldType::Fixed(val) => write!(self, "{val}")?,
            FieldType::LongDateTime(val) => write!(self, "{val:?}")?,
            FieldType::GlyphId(val) => write!(self, "{}", val.to_u16())?,
            FieldType::ResolvedOffset(ResolvedOffset { offset, target }) => {
                match target {
                    Ok(table) => {
                        // only indent if we're on a newline, which means we're
                        // in an array
                        if self.line_pos == 0 {
                            self.print_indent()?;
                        }
                        write!(self, "+{}", offset.to_u32())?;
                        self.print_current_array_pos()?;
                        self.print_offset_hex(*offset)?;
                        self.print_newline()?;
                        self.indented(|this| this.print_fields(table))
                    }
                    Err(e) => write!(self, "Error: '{e}'"),
                }?;
            }
            FieldType::BareOffset(offset) => write!(self, "{}", offset.to_u32())?,
            FieldType::Record(record) => self.print_fields(record)?,
            FieldType::ValueRecord(record) if record.get_field(0).is_none() => {
                self.write_all(b"Null")?
            }
            FieldType::ValueRecord(record) => self.indented(|this| {
                this.print_newline()?;
                this.print_fields(record)
            })?,
            FieldType::Array(array) => self.print_array(array)?,
            FieldType::None => self.write_all(b"None")?,
        }

        self.print_current_array_pos()?;

        match &field {
            FieldType::I8(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::U8(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::I16(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::U16(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::I32(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::U32(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::U24(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::Tag(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::FWord(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::UfWord(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::MajorMinor(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::Version16Dot16(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::F2Dot14(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::Fixed(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::LongDateTime(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::GlyphId(val) => self.print_hex(&val.to_be_bytes())?,
            FieldType::BareOffset(offset) => self.print_offset_hex(*offset)?,
            _ => (),
        }
        Ok(())
    }

    fn print_offset_hex(&mut self, offset: OffsetType) -> std::io::Result<()> {
        match offset {
            OffsetType::None => Ok(()),
            OffsetType::Offset16(val) => self.print_hex(&val.to_be_bytes()),
            OffsetType::Offset24(val) => self.print_hex(&val.to_be_bytes()),
            OffsetType::Offset32(val) => self.print_hex(&val.to_be_bytes()),
        }
    }

    fn print_current_array_pos(&mut self) -> std::io::Result<()> {
        if let Some(idx) = self.cur_array_item.take() {
            let padding = ARRAY_POS_WIDTH.saturating_sub(self.line_pos);
            let wspace = &MANY_SPACES[..padding];
            self.write_all(wspace)?;
            write!(self, " {idx}")?;
        }
        Ok(())
    }

    fn print_hex(&mut self, bytes: &[u8]) -> std::io::Result<()> {
        let padding = L_COLUMN_WIDTH.saturating_sub(self.line_pos);
        let wspace = &MANY_SPACES[..padding];
        self.write_all(wspace)?;
        for b in bytes {
            write!(self, " {b:02X}")?
        }
        Ok(())
    }
}

fn is_scalar(field_type: &FieldType) -> bool {
    matches!(
        field_type,
        FieldType::I8(_)
            | FieldType::U8(_)
            | FieldType::I16(_)
            | FieldType::U16(_)
            | FieldType::I32(_)
            | FieldType::U32(_)
            | FieldType::U24(_)
            | FieldType::Tag(_)
            | FieldType::FWord(_)
            | FieldType::UfWord(_)
            | FieldType::MajorMinor(_)
            | FieldType::Version16Dot16(_)
            | FieldType::F2Dot14(_)
            | FieldType::Fixed(_)
            | FieldType::LongDateTime(_)
            | FieldType::GlyphId(_)
    )
}
<|endoftext|>


<commit_before>//! Removes markdown from strings.

use pulldown_cmark::{Event, Parser};

/// Removes all markdown, keeping the text and code blocks
pub fn remove_markdown(markdown: &str) -> String {
    let mut out = String::new();
    let parser = Parser::new(markdown);

    for event in parser {
        match event {
            Event::Text(text) | Event::Code(text) => out.push_str(&text),
            Event::SoftBreak | Event::HardBreak | Event::Rule => out.push('\n'),
            _ => {}
        }
    }

    out
}
<commit_msg>add doc describing limited capabilities<commit_after>//! Removes markdown from strings.

use pulldown_cmark::{Event, Parser};

/// Removes all markdown, keeping the text and code blocks
///
/// Currently limited in styling, i.e. no ascii tables or lists
pub fn remove_markdown(markdown: &str) -> String {
    let mut out = String::new();
    let parser = Parser::new(markdown);

    for event in parser {
        match event {
            Event::Text(text) | Event::Code(text) => out.push_str(&text),
            Event::SoftBreak | Event::HardBreak | Event::Rule => out.push('\n'),
            _ => {}
        }
    }

    out
}
<|endoftext|>


<commit_before>//! Works only with correct paths or no arguments at all

use std::path::Path;
use std::{env, fs};

fn main() -> Result<(), std::io::Error> {
    // ignoring all arguments except the 1st
    if let Some(path) = env::args().nth(1) {
        // check if the program received an argument
        print_files(Path::new(&path))?;
    } else {
        // note that current_dir value might be invalid, so it's a Result
        print_files(&env::current_dir()?)?;
    };
    Ok(())
}

fn print_files(path: &Path) -> std::io::Result<()> {
    let mut entries: Vec<_> = fs::read_dir(path)?
        .flat_map(|res| res)
        .map(|f| f.file_name())
        .collect();
    // read_dir does not guarantee order
    entries.sort();
    entries.iter().for_each(|f| {
        println!("{}", f.to_string_lossy());
    });
    Ok(())
}
<commit_msg>use for_each<commit_after>//! Works only with correct paths or no arguments at all

use std::path::Path;
use std::{env, fs};

fn main() -> Result<(), std::io::Error> {
    // ignoring all arguments except the 1st
    if let Some(path) = env::args().nth(1) {
        // check if the program received an argument
        print_files(Path::new(&path))?;
    } else {
        // note that current_dir value might be invalid, so it's a Result
        print_files(&env::current_dir()?)?;
    };
    Ok(())
}

fn print_files(path: &Path) -> std::io::Result<()> {
    // flatten entries to avoid calling .unwrap()
    let mut entries: Vec<_> = fs::read_dir(path)?
        .flat_map(|res| res)
        .map(|f| f.file_name())
        .collect();
    // read_dir does not guarantee order
    entries.sort();
    entries.iter().for_each(|f| {
        println!("{}", f.to_string_lossy());
    });
    Ok(())
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use anyhow::{anyhow, bail, Context, Result};
use std::{
    env,
    path::{Path, PathBuf},
    process::Command,
};

use ra_arena::{impl_arena_id, Arena, RawId};

#[derive(Default, Debug, Clone)]
pub struct Sysroot {
    crates: Arena<SysrootCrate, SysrootCrateData>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SysrootCrate(RawId);
impl_arena_id!(SysrootCrate);

#[derive(Debug, Clone)]
struct SysrootCrateData {
    name: String,
    root: PathBuf,
    deps: Vec<SysrootCrate>,
}

impl Sysroot {
    pub fn core(&self) -> Option<SysrootCrate> {
        self.by_name("core")
    }

    pub fn alloc(&self) -> Option<SysrootCrate> {
        self.by_name("alloc")
    }

    pub fn std(&self) -> Option<SysrootCrate> {
        self.by_name("std")
    }

    pub fn proc_macro(&self) -> Option<SysrootCrate> {
        self.by_name("proc_macro")
    }

    pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + 'a {
        self.crates.iter().map(|(id, _data)| id)
    }

    pub fn discover(cargo_toml: &Path) -> Result<Sysroot> {
        let mut src = try_find_src_path(cargo_toml)?;

        if !src.exists() {
            src = try_install_rust_src(cargo_toml)?;
            if !src.exists() {
                Err(anyhow!(
                    "can't load standard library from sysroot\n\
                    {}\n\
                    (discovered via `rustc --print sysroot`)\n\
                    try running `rustup component add rust-src` or set `RUST_SRC_PATH`",
                    src.display(),
                ))?;
            }
        }

        let mut sysroot = Sysroot { crates: Arena::default() };
        for name in SYSROOT_CRATES.trim().lines() {
            let root = src.join(format!("lib{}", name)).join("lib.rs");
            if root.exists() {
                sysroot.crates.alloc(SysrootCrateData {
                    name: name.into(),
                    root,
                    deps: Vec::new(),
                });
            }
        }
        if let Some(std) = sysroot.std() {
            for dep in STD_DEPS.trim().lines() {
                if let Some(dep) = sysroot.by_name(dep) {
                    sysroot.crates[std].deps.push(dep)
                }
            }
        }
        if let Some(alloc) = sysroot.by_name("alloc") {
            if let Some(core) = sysroot.core() {
                sysroot.crates[alloc].deps.push(core);
            }
        }
        Ok(sysroot)
    }

    fn by_name(&self, name: &str) -> Option<SysrootCrate> {
        self.crates.iter().find(|(_id, data)| data.name == name).map(|(id, _data)| id)
    }
}

fn try_find_src_path(cargo_toml: &Path) -> Result<PathBuf> {
    if let Ok(path) = env::var("RUST_SRC_PATH") {
        return Ok(path.into());
    }

    let rustc_output = Command::new("rustc")
        .current_dir(cargo_toml.parent().unwrap())
        .args(&["--print", "sysroot"])
        .output()
        .context("rustc --print sysroot failed")?;
    if !rustc_output.status.success() {
        match rustc_output.status.code() {
            Some(code) => {
                bail!("failed to locate sysroot: rustc --print sysroot exited with code {}", code)
            }
            None => bail!("failed to locate sysroot: rustc --print sysroot terminated by signal"),
        };
    }
    let stdout = String::from_utf8(rustc_output.stdout)?;
    let sysroot_path = Path::new(stdout.trim());
    Ok(sysroot_path.join("lib/rustlib/src/rust/src"))
}

fn try_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> {
    let rustup_output = Command::new("rustup")
        .current_dir(cargo_toml.parent().unwrap())
        .args(&["component", "add", "rust-src"])
        .output()
        .context("rustup component add rust-src failed")?;
    if !rustup_output.status.success() {
        match rustup_output.status.code() {
            Some(code) => bail!(
                "failed to install rust-src: rustup component add rust-src exited with code {}",
                code
            ),
            None => bail!(
                "failed to install rust-src: rustup component add rust-src terminated by signal"
            ),
        };
    }
    try_find_src_path(cargo_toml)
}

impl SysrootCrate {
    pub fn name(self, sysroot: &Sysroot) -> &str {
        &sysroot.crates[self].name
    }
    pub fn root(self, sysroot: &Sysroot) -> &Path {
        sysroot.crates[self].root.as_path()
    }
    pub fn root_dir(self, sysroot: &Sysroot) -> &Path {
        self.root(sysroot).parent().unwrap()
    }
    pub fn deps<'a>(self, sysroot: &'a Sysroot) -> impl Iterator<Item = SysrootCrate> + 'a {
        sysroot.crates[self].deps.iter().copied()
    }
}

const SYSROOT_CRATES: &str = "
std
core
alloc
collections
libc
panic_unwind
proc_macro
rustc_unicode
std_unicode
test
alloc_jemalloc
alloc_system
compiler_builtins
getopts
panic_unwind
panic_abort
rand
term
unwind
build_helper
rustc_asan
rustc_lsan
rustc_msan
rustc_tsan
syntax";

const STD_DEPS: &str = "
alloc
alloc_jemalloc
alloc_system
core
panic_abort
rand
compiler_builtins
unwind
rustc_asan
rustc_lsan
rustc_msan
rustc_tsan
build_helper";
<commit_msg>Idempotent location and installation of rust src<commit_after>//! FIXME: write short doc here

use anyhow::{anyhow, bail, Context, Result};
use std::{
    env,
    path::{Path, PathBuf},
    process::Command,
};

use ra_arena::{impl_arena_id, Arena, RawId};

#[derive(Default, Debug, Clone)]
pub struct Sysroot {
    crates: Arena<SysrootCrate, SysrootCrateData>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SysrootCrate(RawId);
impl_arena_id!(SysrootCrate);

#[derive(Debug, Clone)]
struct SysrootCrateData {
    name: String,
    root: PathBuf,
    deps: Vec<SysrootCrate>,
}

impl Sysroot {
    pub fn core(&self) -> Option<SysrootCrate> {
        self.by_name("core")
    }

    pub fn alloc(&self) -> Option<SysrootCrate> {
        self.by_name("alloc")
    }

    pub fn std(&self) -> Option<SysrootCrate> {
        self.by_name("std")
    }

    pub fn proc_macro(&self) -> Option<SysrootCrate> {
        self.by_name("proc_macro")
    }

    pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + 'a {
        self.crates.iter().map(|(id, _data)| id)
    }

    pub fn discover(cargo_toml: &Path) -> Result<Sysroot> {
        let src = get_or_install_rust_src(cargo_toml)?;

        if !src.exists() {
            Err(anyhow!(
                "can't load standard library from sysroot\n\
                {}\n\
                (discovered via `rustc --print sysroot`)\n\
                try running `rustup component add rust-src` or set `RUST_SRC_PATH`",
                src.display(),
            ))?;
        }

        let mut sysroot = Sysroot { crates: Arena::default() };
        for name in SYSROOT_CRATES.trim().lines() {
            let root = src.join(format!("lib{}", name)).join("lib.rs");
            if root.exists() {
                sysroot.crates.alloc(SysrootCrateData {
                    name: name.into(),
                    root,
                    deps: Vec::new(),
                });
            }
        }
        if let Some(std) = sysroot.std() {
            for dep in STD_DEPS.trim().lines() {
                if let Some(dep) = sysroot.by_name(dep) {
                    sysroot.crates[std].deps.push(dep)
                }
            }
        }
        if let Some(alloc) = sysroot.by_name("alloc") {
            if let Some(core) = sysroot.core() {
                sysroot.crates[alloc].deps.push(core);
            }
        }
        Ok(sysroot)
    }

    fn by_name(&self, name: &str) -> Option<SysrootCrate> {
        self.crates.iter().find(|(_id, data)| data.name == name).map(|(id, _data)| id)
    }
}

fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> {
    fn try_find_src_path(cargo_toml: &Path) -> Result<PathBuf> {
        if let Ok(path) = env::var("RUST_SRC_PATH") {
            return Ok(path.into());
        }

        let rustc_output = Command::new("rustc")
            .current_dir(cargo_toml.parent().unwrap())
            .args(&["--print", "sysroot"])
            .output()
            .context("rustc --print sysroot failed")?;
        if !rustc_output.status.success() {
            match rustc_output.status.code() {
                Some(code) => bail!(
                    "failed to locate sysroot: rustc --print sysroot exited with code {}",
                    code
                ),
                None => {
                    bail!("failed to locate sysroot: rustc --print sysroot terminated by signal")
                }
            };
        }
        let stdout = String::from_utf8(rustc_output.stdout)?;
        let sysroot_path = Path::new(stdout.trim());
        Ok(sysroot_path.join("lib/rustlib/src/rust/src"))
    }

    fn try_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> {
        let rustup_output = Command::new("rustup")
            .current_dir(cargo_toml.parent().unwrap())
            .args(&["component", "add", "rust-src"])
            .output()
            .context("rustup component add rust-src failed")?;
        if !rustup_output.status.success() {
            match rustup_output.status.code() {
                Some(code) => bail!(
                    "failed to install rust-src: rustup component add rust-src exited with code {}",
                    code
                ),
                None => bail!(
                    "failed to install rust-src: rustup component add rust-src terminated by signal"
                ),
            };
        }
        try_find_src_path(cargo_toml)
    }

    let src = try_find_src_path(cargo_toml)?;
    if !src.exists() {
        try_install_rust_src(cargo_toml)
    } else {
        Ok(src)
    }
}

impl SysrootCrate {
    pub fn name(self, sysroot: &Sysroot) -> &str {
        &sysroot.crates[self].name
    }
    pub fn root(self, sysroot: &Sysroot) -> &Path {
        sysroot.crates[self].root.as_path()
    }
    pub fn root_dir(self, sysroot: &Sysroot) -> &Path {
        self.root(sysroot).parent().unwrap()
    }
    pub fn deps<'a>(self, sysroot: &'a Sysroot) -> impl Iterator<Item = SysrootCrate> + 'a {
        sysroot.crates[self].deps.iter().copied()
    }
}

const SYSROOT_CRATES: &str = "
std
core
alloc
collections
libc
panic_unwind
proc_macro
rustc_unicode
std_unicode
test
alloc_jemalloc
alloc_system
compiler_builtins
getopts
panic_unwind
panic_abort
rand
term
unwind
build_helper
rustc_asan
rustc_lsan
rustc_msan
rustc_tsan
syntax";

const STD_DEPS: &str = "
alloc
alloc_jemalloc
alloc_system
core
panic_abort
rand
compiler_builtins
unwind
rustc_asan
rustc_lsan
rustc_msan
rustc_tsan
build_helper";
<|endoftext|>


<commit_before>//! Various extension methods to ast Nodes, which are hard to code-generate.
//! Extensions for various expressions live in a sibling `expr_extensions` module.

use crate::{
    ast::{self, child_opt, children, AstNode, AttrInput, SyntaxNode},
    SmolStr, SyntaxElement,
    SyntaxKind::*,
    SyntaxToken, T,
};
use ra_parser::SyntaxKind;

impl ast::Name {
    pub fn text(&self) -> &SmolStr {
        text_of_first_token(self.syntax())
    }
}

impl ast::NameRef {
    pub fn text(&self) -> &SmolStr {
        text_of_first_token(self.syntax())
    }

    pub fn as_tuple_field(&self) -> Option<usize> {
        self.syntax().children_with_tokens().find_map(|c| {
            if c.kind() == SyntaxKind::INT_NUMBER {
                c.as_token().and_then(|tok| tok.text().as_str().parse().ok())
            } else {
                None
            }
        })
    }
}

fn text_of_first_token(node: &SyntaxNode) -> &SmolStr {
    node.green().children().next().and_then(|it| it.into_token()).unwrap().text()
}

impl ast::Attr {
    pub fn as_simple_atom(&self) -> Option<SmolStr> {
        match self.input() {
            None => self.simple_name(),
            Some(_) => None,
        }
    }

    pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
        match self.input() {
            Some(AttrInput::TokenTree(tt)) => Some((self.simple_name()?, tt)),
            _ => None,
        }
    }

    pub fn as_simple_key_value(&self) -> Option<(SmolStr, SmolStr)> {
        match self.input() {
            Some(AttrInput::Literal(lit)) => {
                let key = self.simple_name()?;
                // FIXME: escape? raw string?
                let value = lit.syntax().first_token()?.text().trim_matches('"').into();
                Some((key, value))
            }
            _ => None,
        }
    }

    pub fn simple_name(&self) -> Option<SmolStr> {
        let path = self.path()?;
        match (path.segment(), path.qualifier()) {
            (Some(segment), None) => Some(segment.syntax().first_token()?.text().clone()),
            _ => None,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathSegmentKind {
    Name(ast::NameRef),
    Type { type_ref: Option<ast::TypeRef>, trait_ref: Option<ast::PathType> },
    SelfKw,
    SuperKw,
    CrateKw,
}

impl ast::PathSegment {
    pub fn parent_path(&self) -> ast::Path {
        self.syntax()
            .parent()
            .and_then(ast::Path::cast)
            .expect("segments are always nested in paths")
    }

    pub fn kind(&self) -> Option<PathSegmentKind> {
        let res = if let Some(name_ref) = self.name_ref() {
            PathSegmentKind::Name(name_ref)
        } else {
            match self.syntax().first_child_or_token()?.kind() {
                T![self] => PathSegmentKind::SelfKw,
                T![super] => PathSegmentKind::SuperKw,
                T![crate] => PathSegmentKind::CrateKw,
                T![<] => {
                    // <T> or <T as Trait>
                    // T is any TypeRef, Trait has to be a PathType
                    let mut type_refs =
                        self.syntax().children().filter(|node| ast::TypeRef::can_cast(node.kind()));
                    let type_ref = type_refs.next().and_then(ast::TypeRef::cast);
                    let trait_ref = type_refs.next().and_then(ast::PathType::cast);
                    PathSegmentKind::Type { type_ref, trait_ref }
                }
                _ => return None,
            }
        };
        Some(res)
    }

    pub fn has_colon_colon(&self) -> bool {
        match self.syntax.first_child_or_token().map(|s| s.kind()) {
            Some(T![::]) => true,
            _ => false,
        }
    }
}

impl ast::Path {
    pub fn parent_path(&self) -> Option<ast::Path> {
        self.syntax().parent().and_then(ast::Path::cast)
    }
}

impl ast::Module {
    pub fn has_semi(&self) -> bool {
        match self.syntax().last_child_or_token() {
            None => false,
            Some(node) => node.kind() == T![;],
        }
    }
}

impl ast::UseTree {
    pub fn has_star(&self) -> bool {
        self.syntax().children_with_tokens().any(|it| it.kind() == T![*])
    }
}

impl ast::UseTreeList {
    pub fn parent_use_tree(&self) -> ast::UseTree {
        self.syntax()
            .parent()
            .and_then(ast::UseTree::cast)
            .expect("UseTreeLists are always nested in UseTrees")
    }
}

impl ast::ImplBlock {
    pub fn target_type(&self) -> Option<ast::TypeRef> {
        match self.target() {
            (Some(t), None) | (_, Some(t)) => Some(t),
            _ => None,
        }
    }

    pub fn target_trait(&self) -> Option<ast::TypeRef> {
        match self.target() {
            (Some(t), Some(_)) => Some(t),
            _ => None,
        }
    }

    fn target(&self) -> (Option<ast::TypeRef>, Option<ast::TypeRef>) {
        let mut types = children(self);
        let first = types.next();
        let second = types.next();
        (first, second)
    }

    pub fn is_negative(&self) -> bool {
        self.syntax().children_with_tokens().any(|t| t.kind() == T![!])
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StructKind {
    Record(ast::RecordFieldDefList),
    Tuple(ast::TupleFieldDefList),
    Unit,
}

impl StructKind {
    fn from_node<N: AstNode>(node: &N) -> StructKind {
        if let Some(nfdl) = child_opt::<_, ast::RecordFieldDefList>(node) {
            StructKind::Record(nfdl)
        } else if let Some(pfl) = child_opt::<_, ast::TupleFieldDefList>(node) {
            StructKind::Tuple(pfl)
        } else {
            StructKind::Unit
        }
    }
}

impl ast::StructDef {
    pub fn kind(&self) -> StructKind {
        StructKind::from_node(self)
    }
}

impl ast::EnumVariant {
    pub fn parent_enum(&self) -> ast::EnumDef {
        self.syntax()
            .parent()
            .and_then(|it| it.parent())
            .and_then(ast::EnumDef::cast)
            .expect("EnumVariants are always nested in Enums")
    }
    pub fn kind(&self) -> StructKind {
        StructKind::from_node(self)
    }
}

impl ast::FnDef {
    pub fn semicolon_token(&self) -> Option<SyntaxToken> {
        self.syntax()
            .last_child_or_token()
            .and_then(|it| it.into_token())
            .filter(|it| it.kind() == T![;])
    }

    pub fn is_async(&self) -> bool {
        self.syntax().children_with_tokens().any(|it| it.kind() == T![async])
    }
}

impl ast::LetStmt {
    pub fn has_semi(&self) -> bool {
        match self.syntax().last_child_or_token() {
            None => false,
            Some(node) => node.kind() == T![;],
        }
    }

    pub fn eq_token(&self) -> Option<SyntaxToken> {
        self.syntax()
            .descendants_with_tokens()
            .find(|t| t.kind() == EQ)
            .and_then(|it| it.into_token())
    }
}

impl ast::ExprStmt {
    pub fn has_semi(&self) -> bool {
        match self.syntax().last_child_or_token() {
            None => false,
            Some(node) => node.kind() == T![;],
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FieldKind {
    Name(ast::NameRef),
    Index(SyntaxToken),
}

impl ast::FieldExpr {
    pub fn index_token(&self) -> Option<SyntaxToken> {
        self.syntax
            .children_with_tokens()
            // FIXME: Accepting floats here to reject them in validation later
            .find(|c| c.kind() == SyntaxKind::INT_NUMBER || c.kind() == SyntaxKind::FLOAT_NUMBER)
            .as_ref()
            .and_then(SyntaxElement::as_token)
            .cloned()
    }

    pub fn field_access(&self) -> Option<FieldKind> {
        if let Some(nr) = self.name_ref() {
            Some(FieldKind::Name(nr))
        } else if let Some(tok) = self.index_token() {
            Some(FieldKind::Index(tok))
        } else {
            None
        }
    }
}

impl ast::RefPat {
    pub fn is_mut(&self) -> bool {
        self.syntax().children_with_tokens().any(|n| n.kind() == T![mut])
    }
}

impl ast::BindPat {
    pub fn is_mutable(&self) -> bool {
        self.syntax().children_with_tokens().any(|n| n.kind() == T![mut])
    }

    pub fn is_ref(&self) -> bool {
        self.syntax().children_with_tokens().any(|n| n.kind() == T![ref])
    }
}

impl ast::PointerType {
    pub fn is_mut(&self) -> bool {
        self.syntax().children_with_tokens().any(|n| n.kind() == T![mut])
    }
}

impl ast::ReferenceType {
    pub fn is_mut(&self) -> bool {
        self.syntax().children_with_tokens().any(|n| n.kind() == T![mut])
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SelfParamKind {
    /// self
    Owned,
    /// &self
    Ref,
    /// &mut self
    MutRef,
}

impl ast::SelfParam {
    pub fn self_kw_token(&self) -> SyntaxToken {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == T![self])
            .expect("invalid tree: self param must have self")
    }

    pub fn kind(&self) -> SelfParamKind {
        let borrowed = self.syntax().children_with_tokens().any(|n| n.kind() == T![&]);
        if borrowed {
            // check for a `mut` coming after the & -- `mut &self` != `&mut self`
            if self
                .syntax()
                .children_with_tokens()
                .skip_while(|n| n.kind() != T![&])
                .any(|n| n.kind() == T![mut])
            {
                SelfParamKind::MutRef
            } else {
                SelfParamKind::Ref
            }
        } else {
            SelfParamKind::Owned
        }
    }
}

impl ast::LifetimeParam {
    pub fn lifetime_token(&self) -> Option<SyntaxToken> {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == LIFETIME)
    }
}

impl ast::TypeParam {
    pub fn colon_token(&self) -> Option<SyntaxToken> {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == T![:])
    }
}

impl ast::WherePred {
    pub fn lifetime_token(&self) -> Option<SyntaxToken> {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == LIFETIME)
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TypeBoundKind {
    /// Trait
    PathType(ast::PathType),
    /// for<'a> ...
    ForType(ast::ForType),
    /// 'a
    Lifetime(ast::SyntaxToken),
}

impl ast::TypeBound {
    pub fn kind(&self) -> TypeBoundKind {
        if let Some(path_type) = children(self).next() {
            TypeBoundKind::PathType(path_type)
        } else if let Some(for_type) = children(self).next() {
            TypeBoundKind::ForType(for_type)
        } else if let Some(lifetime) = self.lifetime() {
            TypeBoundKind::Lifetime(lifetime)
        } else {
            unreachable!()
        }
    }

    fn lifetime(&self) -> Option<SyntaxToken> {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == LIFETIME)
    }

    pub fn question_mark_token(&self) -> Option<SyntaxToken> {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == T![?])
    }
    pub fn has_question_mark(&self) -> bool {
        self.question_mark_token().is_some()
    }
}

impl ast::TraitDef {
    pub fn is_auto(&self) -> bool {
        self.syntax().children_with_tokens().any(|t| t.kind() == T![auto])
    }
}

pub enum VisibilityKind {
    In(ast::Path),
    PubCrate,
    PubSuper,
    Pub,
}

impl ast::Visibility {
    pub fn kind(&self) -> VisibilityKind {
        if let Some(path) = children(self).next() {
            VisibilityKind::In(path)
        } else if self.is_pub_crate() {
            VisibilityKind::PubCrate
        } else if self.is_pub_super() {
            VisibilityKind::PubSuper
        } else {
            VisibilityKind::Pub
        }
    }

    fn is_pub_crate(&self) -> bool {
        self.syntax().children_with_tokens().any(|it| it.kind() == T![crate])
    }

    fn is_pub_super(&self) -> bool {
        self.syntax().children_with_tokens().any(|it| it.kind() == T![super])
    }
}
<commit_msg>changed 'descendants()' to 'children()'<commit_after>//! Various extension methods to ast Nodes, which are hard to code-generate.
//! Extensions for various expressions live in a sibling `expr_extensions` module.

use crate::{
    ast::{self, child_opt, children, AstNode, AttrInput, SyntaxNode},
    SmolStr, SyntaxElement,
    SyntaxKind::*,
    SyntaxToken, T,
};
use ra_parser::SyntaxKind;

impl ast::Name {
    pub fn text(&self) -> &SmolStr {
        text_of_first_token(self.syntax())
    }
}

impl ast::NameRef {
    pub fn text(&self) -> &SmolStr {
        text_of_first_token(self.syntax())
    }

    pub fn as_tuple_field(&self) -> Option<usize> {
        self.syntax().children_with_tokens().find_map(|c| {
            if c.kind() == SyntaxKind::INT_NUMBER {
                c.as_token().and_then(|tok| tok.text().as_str().parse().ok())
            } else {
                None
            }
        })
    }
}

fn text_of_first_token(node: &SyntaxNode) -> &SmolStr {
    node.green().children().next().and_then(|it| it.into_token()).unwrap().text()
}

impl ast::Attr {
    pub fn as_simple_atom(&self) -> Option<SmolStr> {
        match self.input() {
            None => self.simple_name(),
            Some(_) => None,
        }
    }

    pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
        match self.input() {
            Some(AttrInput::TokenTree(tt)) => Some((self.simple_name()?, tt)),
            _ => None,
        }
    }

    pub fn as_simple_key_value(&self) -> Option<(SmolStr, SmolStr)> {
        match self.input() {
            Some(AttrInput::Literal(lit)) => {
                let key = self.simple_name()?;
                // FIXME: escape? raw string?
                let value = lit.syntax().first_token()?.text().trim_matches('"').into();
                Some((key, value))
            }
            _ => None,
        }
    }

    pub fn simple_name(&self) -> Option<SmolStr> {
        let path = self.path()?;
        match (path.segment(), path.qualifier()) {
            (Some(segment), None) => Some(segment.syntax().first_token()?.text().clone()),
            _ => None,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathSegmentKind {
    Name(ast::NameRef),
    Type { type_ref: Option<ast::TypeRef>, trait_ref: Option<ast::PathType> },
    SelfKw,
    SuperKw,
    CrateKw,
}

impl ast::PathSegment {
    pub fn parent_path(&self) -> ast::Path {
        self.syntax()
            .parent()
            .and_then(ast::Path::cast)
            .expect("segments are always nested in paths")
    }

    pub fn kind(&self) -> Option<PathSegmentKind> {
        let res = if let Some(name_ref) = self.name_ref() {
            PathSegmentKind::Name(name_ref)
        } else {
            match self.syntax().first_child_or_token()?.kind() {
                T![self] => PathSegmentKind::SelfKw,
                T![super] => PathSegmentKind::SuperKw,
                T![crate] => PathSegmentKind::CrateKw,
                T![<] => {
                    // <T> or <T as Trait>
                    // T is any TypeRef, Trait has to be a PathType
                    let mut type_refs =
                        self.syntax().children().filter(|node| ast::TypeRef::can_cast(node.kind()));
                    let type_ref = type_refs.next().and_then(ast::TypeRef::cast);
                    let trait_ref = type_refs.next().and_then(ast::PathType::cast);
                    PathSegmentKind::Type { type_ref, trait_ref }
                }
                _ => return None,
            }
        };
        Some(res)
    }

    pub fn has_colon_colon(&self) -> bool {
        match self.syntax.first_child_or_token().map(|s| s.kind()) {
            Some(T![::]) => true,
            _ => false,
        }
    }
}

impl ast::Path {
    pub fn parent_path(&self) -> Option<ast::Path> {
        self.syntax().parent().and_then(ast::Path::cast)
    }
}

impl ast::Module {
    pub fn has_semi(&self) -> bool {
        match self.syntax().last_child_or_token() {
            None => false,
            Some(node) => node.kind() == T![;],
        }
    }
}

impl ast::UseTree {
    pub fn has_star(&self) -> bool {
        self.syntax().children_with_tokens().any(|it| it.kind() == T![*])
    }
}

impl ast::UseTreeList {
    pub fn parent_use_tree(&self) -> ast::UseTree {
        self.syntax()
            .parent()
            .and_then(ast::UseTree::cast)
            .expect("UseTreeLists are always nested in UseTrees")
    }
}

impl ast::ImplBlock {
    pub fn target_type(&self) -> Option<ast::TypeRef> {
        match self.target() {
            (Some(t), None) | (_, Some(t)) => Some(t),
            _ => None,
        }
    }

    pub fn target_trait(&self) -> Option<ast::TypeRef> {
        match self.target() {
            (Some(t), Some(_)) => Some(t),
            _ => None,
        }
    }

    fn target(&self) -> (Option<ast::TypeRef>, Option<ast::TypeRef>) {
        let mut types = children(self);
        let first = types.next();
        let second = types.next();
        (first, second)
    }

    pub fn is_negative(&self) -> bool {
        self.syntax().children_with_tokens().any(|t| t.kind() == T![!])
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StructKind {
    Record(ast::RecordFieldDefList),
    Tuple(ast::TupleFieldDefList),
    Unit,
}

impl StructKind {
    fn from_node<N: AstNode>(node: &N) -> StructKind {
        if let Some(nfdl) = child_opt::<_, ast::RecordFieldDefList>(node) {
            StructKind::Record(nfdl)
        } else if let Some(pfl) = child_opt::<_, ast::TupleFieldDefList>(node) {
            StructKind::Tuple(pfl)
        } else {
            StructKind::Unit
        }
    }
}

impl ast::StructDef {
    pub fn kind(&self) -> StructKind {
        StructKind::from_node(self)
    }
}

impl ast::EnumVariant {
    pub fn parent_enum(&self) -> ast::EnumDef {
        self.syntax()
            .parent()
            .and_then(|it| it.parent())
            .and_then(ast::EnumDef::cast)
            .expect("EnumVariants are always nested in Enums")
    }
    pub fn kind(&self) -> StructKind {
        StructKind::from_node(self)
    }
}

impl ast::FnDef {
    pub fn semicolon_token(&self) -> Option<SyntaxToken> {
        self.syntax()
            .last_child_or_token()
            .and_then(|it| it.into_token())
            .filter(|it| it.kind() == T![;])
    }

    pub fn is_async(&self) -> bool {
        self.syntax().children_with_tokens().any(|it| it.kind() == T![async])
    }
}

impl ast::LetStmt {
    pub fn has_semi(&self) -> bool {
        match self.syntax().last_child_or_token() {
            None => false,
            Some(node) => node.kind() == T![;],
        }
    }

    pub fn eq_token(&self) -> Option<SyntaxToken> {
        self.syntax().children_with_tokens().find(|t| t.kind() == EQ).and_then(|it| it.into_token())
    }
}

impl ast::ExprStmt {
    pub fn has_semi(&self) -> bool {
        match self.syntax().last_child_or_token() {
            None => false,
            Some(node) => node.kind() == T![;],
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FieldKind {
    Name(ast::NameRef),
    Index(SyntaxToken),
}

impl ast::FieldExpr {
    pub fn index_token(&self) -> Option<SyntaxToken> {
        self.syntax
            .children_with_tokens()
            // FIXME: Accepting floats here to reject them in validation later
            .find(|c| c.kind() == SyntaxKind::INT_NUMBER || c.kind() == SyntaxKind::FLOAT_NUMBER)
            .as_ref()
            .and_then(SyntaxElement::as_token)
            .cloned()
    }

    pub fn field_access(&self) -> Option<FieldKind> {
        if let Some(nr) = self.name_ref() {
            Some(FieldKind::Name(nr))
        } else if let Some(tok) = self.index_token() {
            Some(FieldKind::Index(tok))
        } else {
            None
        }
    }
}

impl ast::RefPat {
    pub fn is_mut(&self) -> bool {
        self.syntax().children_with_tokens().any(|n| n.kind() == T![mut])
    }
}

impl ast::BindPat {
    pub fn is_mutable(&self) -> bool {
        self.syntax().children_with_tokens().any(|n| n.kind() == T![mut])
    }

    pub fn is_ref(&self) -> bool {
        self.syntax().children_with_tokens().any(|n| n.kind() == T![ref])
    }
}

impl ast::PointerType {
    pub fn is_mut(&self) -> bool {
        self.syntax().children_with_tokens().any(|n| n.kind() == T![mut])
    }
}

impl ast::ReferenceType {
    pub fn is_mut(&self) -> bool {
        self.syntax().children_with_tokens().any(|n| n.kind() == T![mut])
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SelfParamKind {
    /// self
    Owned,
    /// &self
    Ref,
    /// &mut self
    MutRef,
}

impl ast::SelfParam {
    pub fn self_kw_token(&self) -> SyntaxToken {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == T![self])
            .expect("invalid tree: self param must have self")
    }

    pub fn kind(&self) -> SelfParamKind {
        let borrowed = self.syntax().children_with_tokens().any(|n| n.kind() == T![&]);
        if borrowed {
            // check for a `mut` coming after the & -- `mut &self` != `&mut self`
            if self
                .syntax()
                .children_with_tokens()
                .skip_while(|n| n.kind() != T![&])
                .any(|n| n.kind() == T![mut])
            {
                SelfParamKind::MutRef
            } else {
                SelfParamKind::Ref
            }
        } else {
            SelfParamKind::Owned
        }
    }
}

impl ast::LifetimeParam {
    pub fn lifetime_token(&self) -> Option<SyntaxToken> {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == LIFETIME)
    }
}

impl ast::TypeParam {
    pub fn colon_token(&self) -> Option<SyntaxToken> {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == T![:])
    }
}

impl ast::WherePred {
    pub fn lifetime_token(&self) -> Option<SyntaxToken> {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == LIFETIME)
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TypeBoundKind {
    /// Trait
    PathType(ast::PathType),
    /// for<'a> ...
    ForType(ast::ForType),
    /// 'a
    Lifetime(ast::SyntaxToken),
}

impl ast::TypeBound {
    pub fn kind(&self) -> TypeBoundKind {
        if let Some(path_type) = children(self).next() {
            TypeBoundKind::PathType(path_type)
        } else if let Some(for_type) = children(self).next() {
            TypeBoundKind::ForType(for_type)
        } else if let Some(lifetime) = self.lifetime() {
            TypeBoundKind::Lifetime(lifetime)
        } else {
            unreachable!()
        }
    }

    fn lifetime(&self) -> Option<SyntaxToken> {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == LIFETIME)
    }

    pub fn question_mark_token(&self) -> Option<SyntaxToken> {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token())
            .find(|it| it.kind() == T![?])
    }
    pub fn has_question_mark(&self) -> bool {
        self.question_mark_token().is_some()
    }
}

impl ast::TraitDef {
    pub fn is_auto(&self) -> bool {
        self.syntax().children_with_tokens().any(|t| t.kind() == T![auto])
    }
}

pub enum VisibilityKind {
    In(ast::Path),
    PubCrate,
    PubSuper,
    Pub,
}

impl ast::Visibility {
    pub fn kind(&self) -> VisibilityKind {
        if let Some(path) = children(self).next() {
            VisibilityKind::In(path)
        } else if self.is_pub_crate() {
            VisibilityKind::PubCrate
        } else if self.is_pub_super() {
            VisibilityKind::PubSuper
        } else {
            VisibilityKind::Pub
        }
    }

    fn is_pub_crate(&self) -> bool {
        self.syntax().children_with_tokens().any(|it| it.kind() == T![crate])
    }

    fn is_pub_super(&self) -> bool {
        self.syntax().children_with_tokens().any(|it| it.kind() == T![super])
    }
}
<|endoftext|>


<commit_before>//! Types for extensible image message events ([MSC3552]).
//!
//! [MSC3552]: https://github.com/matrix-org/matrix-spec-proposals/pull/3552

use js_int::UInt;
use ruma_macros::EventContent;
use serde::{Deserialize, Serialize};

use super::{
    file::{EncryptedContent, FileContent},
    message::MessageContent,
    room::message::Relation,
};
use crate::MxcUri;

/// The payload for an extensible image message.
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.image", kind = MessageLike)]
pub struct ImageEventContent {
    /// The text representation of the message.
    #[serde(flatten)]
    pub message: MessageContent,

    /// The file content of the message.
    #[serde(rename = "m.file")]
    pub file: FileContent,

    /// The image content of the message.
    #[serde(rename = "m.image")]
    pub image: Box<ImageContent>,

    /// The thumbnails of the message.
    #[serde(rename = "m.thumbnail", default, skip_serializing_if = "Vec::is_empty")]
    pub thumbnail: Vec<ThumbnailContent>,

    /// The captions of the message.
    #[serde(
        rename = "m.caption",
        with = "super::message::content_serde::as_vec",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub caption: Option<MessageContent>,

    /// Information about related messages for [rich replies].
    ///
    /// [rich replies]: https://spec.matrix.org/v1.2/client-server-api/#rich-replies
    #[serde(flatten, skip_serializing_if = "Option::is_none")]
    pub relates_to: Option<Relation>,
}

impl ImageEventContent {
    /// Creates a new `ImageEventContent` with the given plain text message and file.
    pub fn plain(message: impl Into<String>, file: FileContent) -> Self {
        Self {
            message: MessageContent::plain(message),
            file,
            image: Default::default(),
            thumbnail: Default::default(),
            caption: Default::default(),
            relates_to: None,
        }
    }

    /// Creates a new non-encrypted `ImageEventContent` with the given message and file.
    pub fn with_message(message: MessageContent, file: FileContent) -> Self {
        Self {
            message,
            file,
            image: Default::default(),
            thumbnail: Default::default(),
            caption: Default::default(),
            relates_to: None,
        }
    }
}

/// Information about a thumbnail file content.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct ThumbnailFileContentInfo {
    /// The mimetype of the thumbnail, e.g. `image/png`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub mimetype: Option<String>,

    /// The size of the thumbnail in bytes.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub size: Option<UInt>,
}

impl ThumbnailFileContentInfo {
    /// Creates an empty `ThumbnailFileContentInfo`.
    pub fn new() -> Self {
        Self::default()
    }
}

/// Thumbnail file content.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct ThumbnailFileContent {
    /// The URL to the thumbnail.
    pub url: Box<MxcUri>,

    /// Information about the uploaded thumbnail.
    #[serde(flatten, skip_serializing_if = "Option::is_none")]
    pub info: Option<Box<ThumbnailFileContentInfo>>,

    /// Information on the encrypted thumbnail.
    ///
    /// Required if the thumbnail is encrypted.
    #[serde(flatten, skip_serializing_if = "Option::is_none")]
    pub encryption_info: Option<Box<EncryptedContent>>,
}

impl ThumbnailFileContent {
    /// Creates a new non-encrypted `ThumbnailFileContent` with the given url and file info.
    pub fn plain(url: Box<MxcUri>, info: Option<Box<ThumbnailFileContentInfo>>) -> Self {
        Self { url, info, encryption_info: None }
    }

    /// Creates a new encrypted `ThumbnailFileContent` with the given url, encryption info and
    /// thumbnail file info.
    pub fn encrypted(
        url: Box<MxcUri>,
        encryption_info: EncryptedContent,
        info: Option<Box<ThumbnailFileContentInfo>>,
    ) -> Self {
        Self { url, info, encryption_info: Some(Box::new(encryption_info)) }
    }

    /// Whether the thumbnail file is encrypted.
    pub fn is_encrypted(&self) -> bool {
        self.encryption_info.is_some()
    }
}

/// Thumbnail content.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct ThumbnailContent {
    /// The file info of the thumbnail.
    #[serde(flatten)]
    pub file: ThumbnailFileContent,

    /// The image info of the thumbnail.
    #[serde(flatten, skip_serializing_if = "Option::is_none")]
    pub image: Option<Box<ImageContent>>,
}

impl ThumbnailContent {
    /// Creates a `ThumbnailContent` with the given file and image info.
    pub fn new(file: ThumbnailFileContent, image: Option<Box<ImageContent>>) -> Self {
        Self { file, image }
    }
}

/// Image content.
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct ImageContent {
    /// The height of the image in pixels.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub height: Option<UInt>,

    /// The width of the image in pixels.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub width: Option<UInt>,
}

impl ImageContent {
    /// Creates a new empty `ImageContent`.
    pub fn new() -> Self {
        Self::default()
    }

    /// Creates a new `ImageContent` with the given width and height.
    pub fn with_size(width: UInt, height: UInt) -> Self {
        Self { height: Some(height), width: Some(width) }
    }
}
<commit_msg>events: reorder types in image module<commit_after>//! Types for extensible image message events ([MSC3552]).
//!
//! [MSC3552]: https://github.com/matrix-org/matrix-spec-proposals/pull/3552

use js_int::UInt;
use ruma_macros::EventContent;
use serde::{Deserialize, Serialize};

use super::{
    file::{EncryptedContent, FileContent},
    message::MessageContent,
    room::message::Relation,
};
use crate::MxcUri;

/// The payload for an extensible image message.
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.image", kind = MessageLike)]
pub struct ImageEventContent {
    /// The text representation of the message.
    #[serde(flatten)]
    pub message: MessageContent,

    /// The file content of the message.
    #[serde(rename = "m.file")]
    pub file: FileContent,

    /// The image content of the message.
    #[serde(rename = "m.image")]
    pub image: Box<ImageContent>,

    /// The thumbnails of the message.
    #[serde(rename = "m.thumbnail", default, skip_serializing_if = "Vec::is_empty")]
    pub thumbnail: Vec<ThumbnailContent>,

    /// The captions of the message.
    #[serde(
        rename = "m.caption",
        with = "super::message::content_serde::as_vec",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub caption: Option<MessageContent>,

    /// Information about related messages for [rich replies].
    ///
    /// [rich replies]: https://spec.matrix.org/v1.2/client-server-api/#rich-replies
    #[serde(flatten, skip_serializing_if = "Option::is_none")]
    pub relates_to: Option<Relation>,
}

impl ImageEventContent {
    /// Creates a new `ImageEventContent` with the given plain text message and file.
    pub fn plain(message: impl Into<String>, file: FileContent) -> Self {
        Self {
            message: MessageContent::plain(message),
            file,
            image: Default::default(),
            thumbnail: Default::default(),
            caption: Default::default(),
            relates_to: None,
        }
    }

    /// Creates a new non-encrypted `ImageEventContent` with the given message and file.
    pub fn with_message(message: MessageContent, file: FileContent) -> Self {
        Self {
            message,
            file,
            image: Default::default(),
            thumbnail: Default::default(),
            caption: Default::default(),
            relates_to: None,
        }
    }
}

/// Image content.
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct ImageContent {
    /// The height of the image in pixels.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub height: Option<UInt>,

    /// The width of the image in pixels.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub width: Option<UInt>,
}

impl ImageContent {
    /// Creates a new empty `ImageContent`.
    pub fn new() -> Self {
        Self::default()
    }

    /// Creates a new `ImageContent` with the given width and height.
    pub fn with_size(width: UInt, height: UInt) -> Self {
        Self { height: Some(height), width: Some(width) }
    }
}

/// Thumbnail content.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct ThumbnailContent {
    /// The file info of the thumbnail.
    #[serde(flatten)]
    pub file: ThumbnailFileContent,

    /// The image info of the thumbnail.
    #[serde(flatten, skip_serializing_if = "Option::is_none")]
    pub image: Option<Box<ImageContent>>,
}

impl ThumbnailContent {
    /// Creates a `ThumbnailContent` with the given file and image info.
    pub fn new(file: ThumbnailFileContent, image: Option<Box<ImageContent>>) -> Self {
        Self { file, image }
    }
}

/// Thumbnail file content.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct ThumbnailFileContent {
    /// The URL to the thumbnail.
    pub url: Box<MxcUri>,

    /// Information about the uploaded thumbnail.
    #[serde(flatten, skip_serializing_if = "Option::is_none")]
    pub info: Option<Box<ThumbnailFileContentInfo>>,

    /// Information on the encrypted thumbnail.
    ///
    /// Required if the thumbnail is encrypted.
    #[serde(flatten, skip_serializing_if = "Option::is_none")]
    pub encryption_info: Option<Box<EncryptedContent>>,
}

impl ThumbnailFileContent {
    /// Creates a new non-encrypted `ThumbnailFileContent` with the given url and file info.
    pub fn plain(url: Box<MxcUri>, info: Option<Box<ThumbnailFileContentInfo>>) -> Self {
        Self { url, info, encryption_info: None }
    }

    /// Creates a new encrypted `ThumbnailFileContent` with the given url, encryption info and
    /// thumbnail file info.
    pub fn encrypted(
        url: Box<MxcUri>,
        encryption_info: EncryptedContent,
        info: Option<Box<ThumbnailFileContentInfo>>,
    ) -> Self {
        Self { url, info, encryption_info: Some(Box::new(encryption_info)) }
    }

    /// Whether the thumbnail file is encrypted.
    pub fn is_encrypted(&self) -> bool {
        self.encryption_info.is_some()
    }
}

/// Information about a thumbnail file content.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct ThumbnailFileContentInfo {
    /// The mimetype of the thumbnail, e.g. `image/png`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub mimetype: Option<String>,

    /// The size of the thumbnail in bytes.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub size: Option<UInt>,
}

impl ThumbnailFileContentInfo {
    /// Creates an empty `ThumbnailFileContentInfo`.
    pub fn new() -> Self {
        Self::default()
    }
}
<|endoftext|>


<commit_before>use super::*;
use pact_matching::models::*;
use pact_matching::models::matchingrules::*;
use pact_matching::models::generators::*;
use std::str::FromStr;
use std::collections::hash_map::HashMap;
use std::io::Read;
use tokio::runtime::Runtime;
use hyper::client::Client;
use hyper::{Request as HyperRequest, Response as HyperResponse};
use hyper::http::request::{Builder as RequestBuilder};
use hyper::Body;
use hyper::error::Error as HyperError;
use hyper::client::ResponseFuture;
use hyper::Method;
use hyper::http::header::{HeaderMap, HeaderName};
use hyper::http::header::CONTENT_TYPE;
use hyper::rt::{Future, Stream};

pub fn join_paths(base: &String, path: String) -> String {
    let mut full_path = s!(base.trim_right_matches("/"));
    full_path.push('/');
    full_path.push_str(path.trim_left_matches("/"));
    full_path
}

fn setup_headers(builder: &mut RequestBuilder, headers: &Option<HashMap<String, String>>) -> Result<(), Box<Error>> {
    let mut hyper_headers = builder.headers_mut().unwrap();
    match headers {
        Some(header_map) => {
            for (k, v) in header_map {
                // FIXME?: Headers are not sent in "raw" mode.
                // Names are converted to lower case and values are parsed.
                hyper_headers.insert(
                    HeaderName::from_bytes(k.as_bytes())?,
                    v.parse()?
                );
            }

            if !header_map.keys().any(|k| k.to_lowercase() == "content-type") {
                hyper_headers.insert(CONTENT_TYPE, "application/json".parse()?);
            }
        }
    }
    Ok(())
}

fn make_request(base_url: &String, request: &Request) -> Result<ResponseFuture, Box<Error>> {
    match Method::from_str(&request.method) {
        Ok(method) => {
            let mut url = join_paths(base_url, request.path.clone());
            if request.query.is_some() {
                url.push('?');
                url.push_str(&build_query_string(request.query.clone().unwrap()));
            }
            debug!("Making request to '{}'", url);
            let mut builder = HyperRequest::builder()
                .method(method)
                .uri(url);
            setup_headers(&mut builder, &request.headers())?;

            let mut hyper_request = builder
                .body(match request.body {
                    OptionalBody::Present(ref s) => Body::from(s.clone()),
                    OptionalBody::Null => {
                        if request.content_type() == "application/json" {
                            Body::from("null")
                        } else {
                            Body::empty()
                        }
                    },
                    _ => Body::empty()
                })?;

            Ok(Client::new().request(hyper_request))
        },
        Err(err) => Err(err.into())
    }
}

fn extract_headers(headers: &HeaderMap) -> Option<HashMap<String, String>> {
    if headers.len() > 0 {
        Some(headers.iter()
            .map(|(name, value)| {
                (name.as_str().into(), value.to_str().unwrap().into())
            })
            .collect()
        )
    } else {
        None
    }
}

pub fn extract_body(hyper_body: Result<hyper::Chunk, HyperError>) -> Result<OptionalBody, HyperError> {
    match hyper_body {
        Ok(chunk) => {
            let bytes = chunk.into_bytes();
            if bytes.len() > 0 {
                Ok(OptionalBody::Present(bytes.to_vec()))
            } else {
                Ok(OptionalBody::Empty)
            }
        },
        Err(err) => {
            warn!("Failed to read request body: {}", err);
            Ok(OptionalBody::Missing)
        }
    }
}

fn hyper_response_to_pact_response(response: HyperResponse<Body>) -> impl Future<Item = Response, Error = HyperError> {
    debug!("Received response: {:?}", response);

    let status = response.status().as_u16();
    let headers = extract_headers(response.headers());

    response.into_body()
        .concat2()
        .then(extract_body)
        .map(|body| {
            Response {
                status: status,
                headers: headers,
                body: body,
                matching_rules: MatchingRules::default(),
                generators: Generators::default()
            }
        })
}

pub fn make_provider_request(provider: &ProviderInfo, request: &Request, runtime: &mut Runtime) -> Result<Response, Box<Error>> {
    debug!("Sending {:?} to provider", request);
    match make_request(&format!("{}://{}:{}{}", provider.protocol, provider.host, provider.port,
        provider.path), request) {
        Ok(ref mut response_future) => {
            let pact_response_future = response_future
                .and_then(hyper_response_to_pact_response);

            runtime.block_on(pact_response_future)
                .map_err(|err| err.into())
        },
        Err(err) => {
            debug!("Request failed: {}", err);
            Err(err)
        }
    }
}

pub fn make_state_change_request(provider: &ProviderInfo, request: &Request, runtime: &mut Runtime) -> Result<(), String> {
    debug!("Sending {:?} to state change handler", request);
    let client = Client::new();
    match make_request(&provider.state_change_url.clone().unwrap(), request) {
        Ok(ref mut response) => {
            debug!("Received response: {:?}", response);
            if response.status.is_success() {
                Ok(())
            } else {
                debug!("Request failed: {}", response.status);
                Err(format!("State change request failed: {}", response.status))
            }
        },
        Err(err) => {
            debug!("Request failed: {}", err);
            Err(format!("State change request failed: {}", err))
        }
    }
}

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

    #[test]
    fn join_paths_test() {
        expect!(join_paths(&s!(""), s!(""))).to(be_equal_to(s!("/")));
        expect!(join_paths(&s!("/"), s!(""))).to(be_equal_to(s!("/")));
        expect!(join_paths(&s!(""), s!("/"))).to(be_equal_to(s!("/")));
        expect!(join_paths(&s!("/"), s!("/"))).to(be_equal_to(s!("/")));
        expect!(join_paths(&s!("/a/b"), s!("/c/d"))).to(be_equal_to(s!("/a/b/c/d")));
    }

}
<commit_msg>Work on making the state change async<commit_after>use super::*;
use pact_matching::models::*;
use pact_matching::models::matchingrules::*;
use pact_matching::models::generators::*;
use std::str::FromStr;
use std::collections::hash_map::HashMap;
use std::io::Read;
use tokio::runtime::Runtime;
use hyper::client::Client;
use hyper::{Request as HyperRequest, Response as HyperResponse};
use hyper::http::request::{Builder as RequestBuilder};
use hyper::Body;
use hyper::error::Error as HyperError;
use hyper::client::ResponseFuture;
use hyper::Method;
use hyper::http::header::{HeaderMap, HeaderName};
use hyper::http::header::CONTENT_TYPE;
use hyper::rt::{Future, Stream};

pub fn join_paths(base: &String, path: String) -> String {
    let mut full_path = s!(base.trim_right_matches("/"));
    full_path.push('/');
    full_path.push_str(path.trim_left_matches("/"));
    full_path
}

fn setup_headers(builder: &mut RequestBuilder, headers: &Option<HashMap<String, String>>) -> Result<(), Box<Error>> {
    let mut hyper_headers = builder.headers_mut().unwrap();
    match headers {
        Some(header_map) => {
            for (k, v) in header_map {
                // FIXME?: Headers are not sent in "raw" mode.
                // Names are converted to lower case and values are parsed.
                hyper_headers.insert(
                    HeaderName::from_bytes(k.as_bytes())?,
                    v.parse()?
                );
            }

            if !header_map.keys().any(|k| k.to_lowercase() == "content-type") {
                hyper_headers.insert(CONTENT_TYPE, "application/json".parse()?);
            }
        }
    }
    Ok(())
}

fn make_request(base_url: &String, request: &Request) -> Result<ResponseFuture, Box<Error>> {
    match Method::from_str(&request.method) {
        Ok(method) => {
            let mut url = join_paths(base_url, request.path.clone());
            if request.query.is_some() {
                url.push('?');
                url.push_str(&build_query_string(request.query.clone().unwrap()));
            }
            debug!("Making request to '{}'", url);
            let mut builder = HyperRequest::builder()
                .method(method)
                .uri(url);
            setup_headers(&mut builder, &request.headers())?;

            let mut hyper_request = builder
                .body(match request.body {
                    OptionalBody::Present(ref s) => Body::from(s.clone()),
                    OptionalBody::Null => {
                        if request.content_type() == "application/json" {
                            Body::from("null")
                        } else {
                            Body::empty()
                        }
                    },
                    _ => Body::empty()
                })?;

            Ok(Client::new().request(hyper_request))
        },
        Err(err) => Err(err.into())
    }
}

fn extract_headers(headers: &HeaderMap) -> Option<HashMap<String, String>> {
    if headers.len() > 0 {
        Some(headers.iter()
            .map(|(name, value)| {
                (name.as_str().into(), value.to_str().unwrap().into())
            })
            .collect()
        )
    } else {
        None
    }
}

pub fn extract_body(hyper_body: Result<hyper::Chunk, HyperError>) -> Result<OptionalBody, HyperError> {
    match hyper_body {
        Ok(chunk) => {
            let bytes = chunk.into_bytes();
            if bytes.len() > 0 {
                Ok(OptionalBody::Present(bytes.to_vec()))
            } else {
                Ok(OptionalBody::Empty)
            }
        },
        Err(err) => {
            warn!("Failed to read request body: {}", err);
            Ok(OptionalBody::Missing)
        }
    }
}

fn hyper_response_to_pact_response(response: HyperResponse<Body>) -> impl Future<Item = Response, Error = HyperError> {
    debug!("Received response: {:?}", response);

    let status = response.status().as_u16();
    let headers = extract_headers(response.headers());

    response.into_body()
        .concat2()
        .then(extract_body)
        .map(|body| {
            Response {
                status: status,
                headers: headers,
                body: body,
                matching_rules: MatchingRules::default(),
                generators: Generators::default()
            }
        })
}

pub fn make_provider_request(provider: &ProviderInfo, request: &Request, runtime: &mut Runtime) -> Result<Response, Box<Error>> {
    debug!("Sending {:?} to provider", request);
    match make_request(&format!("{}://{}:{}{}", provider.protocol, provider.host, provider.port,
        provider.path), request) {
        Ok(ref mut response_future) => {
            let pact_response_future = response_future
                .and_then(hyper_response_to_pact_response);

            runtime.block_on(pact_response_future)
                .map_err(|err| {
                    debug!("Response processing failed: {}", err);
                    err.into()
                })
        },
        Err(err) => {
            debug!("Request failed: {}", err);
            Err(err)
        }
    }
}

pub fn make_state_change_request(provider: &ProviderInfo, request: &Request, runtime: &mut Runtime) -> Result<(), String> {
    debug!("Sending {:?} to state change handler", request);
    let client = Client::new();
    match make_request(&provider.state_change_url.clone().unwrap(), request) {
        Ok(ref mut response_future) => {
            let response = runtime.block_on(response_future);
            debug!("Received response: {:?}", response);
            if response.status.is_success() {
                Ok(())
            } else {
                debug!("Request failed: {}", response.status);
                Err(format!("State change request failed: {}", response.status))
            }
        },
        Err(err) => {
            debug!("Request failed: {}", err);
            Err(format!("State change request failed: {}", err))
        }
    }
}

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

    #[test]
    fn join_paths_test() {
        expect!(join_paths(&s!(""), s!(""))).to(be_equal_to(s!("/")));
        expect!(join_paths(&s!("/"), s!(""))).to(be_equal_to(s!("/")));
        expect!(join_paths(&s!(""), s!("/"))).to(be_equal_to(s!("/")));
        expect!(join_paths(&s!("/"), s!("/"))).to(be_equal_to(s!("/")));
        expect!(join_paths(&s!("/a/b"), s!("/c/d"))).to(be_equal_to(s!("/a/b/c/d")));
    }

}
<|endoftext|>