<commit_before>extern crate gtk;
extern crate cairo;

extern crate ffmpeg;

use std::ops::{Deref, DerefMut};

use std::rc::Rc;
use std::cell::RefCell;

use gtk::prelude::*;
use cairo::enums::{FontSlant, FontWeight};

use ::media::Context;
use ::media::PacketNotifiable;

use super::MediaNotifiable;
use super::MediaController;

pub struct AudioController {
    media_ctl: MediaController,
    drawingarea: gtk::DrawingArea,
    message: String,
    frame: Option<ffmpeg::frame::Audio>,
    graph: Option<ffmpeg::filter::Graph>,
}

impl AudioController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<AudioController>> {
        // need a RefCell because the callbacks will use immutable versions of ac
        // when the UI controllers will get a mutable version from time to time
        let ac = Rc::new(RefCell::new(AudioController {
            media_ctl: MediaController::new(builder.get_object("audio-container").unwrap()),
            drawingarea: builder.get_object("audio-drawingarea").unwrap(),
            message: "audio place holder".to_owned(),
            frame: None,
            graph: None,
        }));

        let ac_for_cb = ac.clone();
        ac.borrow().drawingarea.connect_draw(move |_, cairo_ctx| {
            ac_for_cb.borrow().draw(&cairo_ctx);
            Inhibit(false)
        });

        ac
    }

    fn build_graph(&mut self, decoder: &ffmpeg::codec::decoder::Audio) -> Result<bool, String> { // TODO: check how to return Ok() only
        match self.graph {
            Some(_) => (),
            None => {
                let mut graph = ffmpeg::filter::Graph::new();

	            let args = format!("time_base={}:sample_rate={}:sample_fmt={}:channel_layout=0x{:x}",
		            decoder.time_base(), decoder.rate(), decoder.format().name(), decoder.channel_layout().bits());

                let in_filter = ffmpeg::filter::find("abuffer").unwrap();
                match graph.add(&in_filter, "in", &args) {
                    Ok(_) => (),
                    Err(error) => return Err(format!("Error adding in pad: {:?}", error)),
                }

                let out_filter = ffmpeg::filter::find("abuffersink").unwrap();
                match graph.add(&out_filter, "out", "") {
                    Ok(_) => (),
                    Err(error) => return Err(format!("Error adding out pad: {:?}", error)),
                }
                {
                    let mut out_pad = graph.get("out").unwrap();
                    out_pad.set_sample_format(ffmpeg::format::Sample::I16(ffmpeg::format::sample::Type::Packed));
                }

                {
                    let in_parser;
                    match graph.output("in", 0) {
                        Ok(value) => in_parser = value,
                        Err(error) => return Err(format!("Error getting output for in pad: {:?}", error)),
                    }
                    let out_parser;
                    match in_parser.input("out", 0) {
                        Ok(value) => out_parser = value,
                        Err(error) => return Err(format!("Error getting input for out pad: {:?}", error)),
                    }
                    match out_parser.parse("anull") {
                        Ok(_) => (),
                        Err(error) => return Err(format!("Error parsing format: {:?}", error)),
                    }
                }

                match graph.validate() {
                    Ok(_) => self.graph = Some(graph),
                    Err(error) => return Err(format!("Error validating graph: {:?}", error)),
                }
            },
        }

        Ok(true)
    }

    fn convert_to_pcm16(&mut self, decoder: &ffmpeg::codec::decoder::Audio,
                        frame_in: &mut ffmpeg::frame::Audio) -> Result<ffmpeg::frame::Audio, String> {
        match self.build_graph(decoder) {
            Ok(_) => {
                let mut graph = self.graph.as_mut().unwrap();
                graph.get("in").unwrap().source().add(&frame_in).unwrap();

                let mut frame_pcm = ffmpeg::frame::Audio::empty();
                while let Ok(..) = graph.get("out").unwrap().sink().frame(&mut frame_pcm) {
                }

                assert!(frame_pcm.planes() == 1); // samples converted to I16::Packed
                {
                    let mut samples_str = String::new();
                    let mut sample_iter = frame_pcm.data(0).iter();
                    // TODO: loop and handle borrowing...
                    let mut sample: u16 = 0;
                    {
                        if let Some(sample_byte) = sample_iter.next() {
                            // TODO: separate samples according to channel_layout
                            sample = *sample_byte as u16;
                        }
                    }

                    if let Some(sample_byte) = sample_iter.next() {
                        // TODO: validate this
                        sample += (*sample_byte as u16) << 8;
                        samples_str += &format!("{:x} ", sample)[..];
                    }
                    println!("Samples: {}", samples_str);
                }

                Ok(frame_pcm)
            },
            Err(error) => Err(error),
        }
    }

    fn draw(&self, cr: &cairo::Context) {
        let allocation = self.drawingarea.get_allocation();
        cr.scale(allocation.width as f64, allocation.height as f64);

        cr.select_font_face("Sans", FontSlant::Normal, FontWeight::Normal);
        cr.set_font_size(0.07);

        cr.move_to(0.1, 0.53);
        cr.show_text(&self.message);
    }
}

impl Deref for AudioController {
	type Target = MediaController;

	fn deref(&self) -> &Self::Target {
		&self.media_ctl
	}
}

impl DerefMut for AudioController {
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.media_ctl
	}
}

impl MediaNotifiable for AudioController {
    fn new_media(&mut self, context: &mut Context) {
        self.frame = None;
        self.graph = None;
        self.message = match context.audio_stream.as_mut() {
            Some(stream) => {
                self.set_index(stream.index);

                self.show();
                println!("\n** Audio stream\n{:?}", &stream);
                format!("audio stream {}", self.stream_index().unwrap())
            },
            None => {
                self.hide();
                "no audio stream".to_owned()
            },
        };

        self.drawingarea.queue_draw();
    }
}

impl PacketNotifiable for AudioController {
    fn new_packet(&mut self, stream: &ffmpeg::format::stream::Stream, packet: &ffmpeg::codec::packet::Packet) {
        self.print_packet_content(stream, packet);

        let decoder = stream.codec().decoder();
        match decoder.audio() {
            Ok(mut audio) => {
                let mut frame = ffmpeg::frame::Audio::empty();
                match audio.decode(packet, &mut frame) {
                    Ok(result) => if result {
                            let planes = frame.planes();
                            println!("\tdecoded audio frame, found {} planes", planes);
                            if planes > 0 {
                                for plane in 0..planes {
                                    println!("\tplane {}: data len: {}", plane, frame.data(plane).len());
                                }
                                match self.convert_to_pcm16(&audio, &mut frame) {
                                    Ok(frame_pcm) => self.frame = Some(frame_pcm),
                                    Err(error) =>  println!("\tError converting to pcm: {:?}", error),
                                }
                            }
                            else {
                                println!("\tno planes found in frame");
                            }
                        }
                        else {
                            println!("\tfailed to decode audio frame");
                        }
                    ,
                    Err(error) => println!("Error decoding audio: {:?}", error),
                }
            },
            Err(error) => println!("Error getting audio decoder: {:?}", error),
        }
    }
}
<commit_msg>Audio contoller: gather samples by channels (#1)<commit_after>extern crate gtk;
extern crate cairo;

extern crate ffmpeg;

use std::ops::{Deref, DerefMut};

use std::rc::Rc;
use std::cell::RefCell;

use gtk::prelude::*;
use cairo::enums::{FontSlant, FontWeight};

use ::media::Context;
use ::media::PacketNotifiable;

use super::MediaNotifiable;
use super::MediaController;

pub struct AudioController {
    media_ctl: MediaController,
    drawingarea: gtk::DrawingArea,
    message: String,
    frame: Option<ffmpeg::frame::Audio>,
    graph: Option<ffmpeg::filter::Graph>,
}

impl AudioController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<AudioController>> {
        // need a RefCell because the callbacks will use immutable versions of ac
        // when the UI controllers will get a mutable version from time to time
        let ac = Rc::new(RefCell::new(AudioController {
            media_ctl: MediaController::new(builder.get_object("audio-container").unwrap()),
            drawingarea: builder.get_object("audio-drawingarea").unwrap(),
            message: "audio place holder".to_owned(),
            frame: None,
            graph: None,
        }));

        let ac_for_cb = ac.clone();
        ac.borrow().drawingarea.connect_draw(move |_, cairo_ctx| {
            ac_for_cb.borrow().draw(&cairo_ctx);
            Inhibit(false)
        });

        ac
    }

    fn build_graph(&mut self, decoder: &ffmpeg::codec::decoder::Audio) -> Result<bool, String> { // TODO: check how to return Ok() only
        match self.graph {
            Some(_) => (),
            None => {
                let mut graph = ffmpeg::filter::Graph::new();

	            let args = format!("time_base={}:sample_rate={}:sample_fmt={}:channel_layout=0x{:x}",
		            decoder.time_base(), decoder.rate(), decoder.format().name(), decoder.channel_layout().bits());

                let in_filter = ffmpeg::filter::find("abuffer").unwrap();
                match graph.add(&in_filter, "in", &args) {
                    Ok(_) => (),
                    Err(error) => return Err(format!("Error adding in pad: {:?}", error)),
                }

                let out_filter = ffmpeg::filter::find("abuffersink").unwrap();
                match graph.add(&out_filter, "out", "") {
                    Ok(_) => (),
                    Err(error) => return Err(format!("Error adding out pad: {:?}", error)),
                }
                {
                    let mut out_pad = graph.get("out").unwrap();
                    out_pad.set_sample_format(ffmpeg::format::Sample::I16(ffmpeg::format::sample::Type::Packed));
                }

                {
                    let in_parser;
                    match graph.output("in", 0) {
                        Ok(value) => in_parser = value,
                        Err(error) => return Err(format!("Error getting output for in pad: {:?}", error)),
                    }
                    let out_parser;
                    match in_parser.input("out", 0) {
                        Ok(value) => out_parser = value,
                        Err(error) => return Err(format!("Error getting input for out pad: {:?}", error)),
                    }
                    match out_parser.parse("anull") {
                        Ok(_) => (),
                        Err(error) => return Err(format!("Error parsing format: {:?}", error)),
                    }
                }

                match graph.validate() {
                    Ok(_) => self.graph = Some(graph),
                    Err(error) => return Err(format!("Error validating graph: {:?}", error)),
                }
            },
        }

        Ok(true)
    }

    fn convert_to_pcm16(&mut self, decoder: &ffmpeg::codec::decoder::Audio,
                        frame_in: &mut ffmpeg::frame::Audio) -> Result<ffmpeg::frame::Audio, String> {
        match self.build_graph(decoder) {
            Ok(_) => {
                let mut graph = self.graph.as_mut().unwrap();
                graph.get("in").unwrap().source().add(&frame_in).unwrap();

                let mut frame_pcm = ffmpeg::frame::Audio::empty();
                while let Ok(..) = graph.get("out").unwrap().sink().frame(&mut frame_pcm) {
                }

                assert!(frame_pcm.planes() == 1); // samples converted to I16::Packed
                {
                    let channels_nb = frame_pcm.channels() as usize;
                    let mut channels = Vec::with_capacity(channels_nb);
                    for index in 0..channels_nb {
                        // TODO: reserve target capacity
                        channels.push(Vec::new());
                    }
                    // FIXME: this doesn't seem rust like iteration to me
                    let mut keep_going = true;
                    let mut sample_iter = frame_pcm.data(0).iter();
                    while keep_going {
                        for index in 0..channels_nb {
                            let mut sample: i16 = 0;
                            if let Some(sample_byte) = sample_iter.next() {
                                sample = *sample_byte as i16;
                            }
                            else {
                                keep_going = false;
                                break;
                            }

                            if let Some(sample_byte) = sample_iter.next() {
                                // TODO: validate this
                                channels[index].push(sample + ((*sample_byte as i16) << 8));
                            }
                            else {
                                keep_going = false;
                                break;
                            }
                        }
                    }

                    for index in 0..channels_nb {
                        println!("\tChannel {}", index);
                        let mut sample_str = String::new();
                        for sample in &channels[index] {
                            sample_str += &format!("{:4x} ", sample);
                        }
                        println!("\t\tsamples {}", sample_str);
                    }
                }

                Ok(frame_pcm)
            },
            Err(error) => Err(error),
        }
    }

    fn draw(&self, cr: &cairo::Context) {
        let allocation = self.drawingarea.get_allocation();
        cr.scale(allocation.width as f64, allocation.height as f64);

        cr.select_font_face("Sans", FontSlant::Normal, FontWeight::Normal);
        cr.set_font_size(0.07);

        cr.move_to(0.1, 0.53);
        cr.show_text(&self.message);
    }
}

impl Deref for AudioController {
	type Target = MediaController;

	fn deref(&self) -> &Self::Target {
		&self.media_ctl
	}
}

impl DerefMut for AudioController {
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.media_ctl
	}
}

impl MediaNotifiable for AudioController {
    fn new_media(&mut self, context: &mut Context) {
        self.frame = None;
        self.graph = None;
        self.message = match context.audio_stream.as_mut() {
            Some(stream) => {
                self.set_index(stream.index);

                self.show();
                println!("\n** Audio stream\n{:?}", &stream);
                format!("audio stream {}", self.stream_index().unwrap())
            },
            None => {
                self.hide();
                "no audio stream".to_owned()
            },
        };

        self.drawingarea.queue_draw();
    }
}

impl PacketNotifiable for AudioController {
    fn new_packet(&mut self, stream: &ffmpeg::format::stream::Stream, packet: &ffmpeg::codec::packet::Packet) {
        self.print_packet_content(stream, packet);

        let decoder = stream.codec().decoder();
        match decoder.audio() {
            Ok(mut audio) => {
                let mut frame = ffmpeg::frame::Audio::empty();
                match audio.decode(packet, &mut frame) {
                    Ok(result) => if result {
                            let planes = frame.planes();
                            println!("\tdecoded audio frame, found {} planes", planes);
                            if planes > 0 {
                                match self.convert_to_pcm16(&audio, &mut frame) {
                                    Ok(frame_pcm) => self.frame = Some(frame_pcm),
                                    Err(error) =>  println!("\tError converting to pcm: {:?}", error),
                                }
                            }
                            else {
                                println!("\tno planes found in frame");
                            }
                        }
                        else {
                            println!("\tfailed to decode audio frame");
                        }
                    ,
                    Err(error) => println!("Error decoding audio: {:?}", error),
                }
            },
            Err(error) => println!("Error getting audio decoder: {:?}", error),
        }
    }
}
<|endoftext|>


<commit_before>/*

  This struct essentially encapsulates the "instance variables"

*/
pub struct SimpleHdrHistogram {
    pub leading_zeros_count_base: usize,
    pub sub_bucket_mask: i64,
    pub unit_magnitude: usize,
    pub sub_bucket_count: usize,
    pub sub_bucket_half_count: usize,
    pub sub_bucket_half_count_magnitude: i32,
    pub counts: Vec<u64>,
    pub counts_array_length: usize,
    pub normalizing_index_offset: usize,
}

/*

  Implementing this trait (Default) for our struct gives us a nice way to
  initialize an instance using default args instead of having to provide all of them

 */
impl Default for SimpleHdrHistogram {
    fn default () -> SimpleHdrHistogram {
        SimpleHdrHistogram {
            leading_zeros_count_base: 0,
            sub_bucket_mask: 0,
            unit_magnitude: 0,
            sub_bucket_count: 0,
            sub_bucket_half_count: 0,
            sub_bucket_half_count_magnitude: 0,
            counts: Vec::new(),
            counts_array_length: 0,
            normalizing_index_offset: 0,
        }
    }
}

pub trait HistogramBase {

    //FIXME this stuff could be mostly unsigned

    //TODO should be default impl of this trait
    fn record_single_value(&mut self, value: i64) -> Result<(), String>;

    //TODO should be default impl of this trait
    fn counts_array_index(&self, value: i64) -> Result<usize, String>;

    //TODO should be default impl of this trait
    fn counts_array_index_sub(&self, bucket_index: usize, sub_bucket_index: usize) -> usize;

    //TODO should be default impl of this trait
    fn get_bucket_index(&self, value: i64) -> usize;

    //TODO should be default impl of this trait
    fn get_sub_bucket_index(&self, value: i64, bucket_index: usize) -> usize;

    fn increment_count_at_index(&mut self, index: usize) -> Result<(), String>;
    fn normalize_index(&self, index: usize, normalizing_index_offset: usize, array_length: usize) ->
        Result<usize, String>;

}

impl HistogramBase for SimpleHdrHistogram {

    fn normalize_index(&self, index: usize, normalizing_index_offset: usize, array_length: usize) ->
Result<usize, String> {

        match normalizing_index_offset {
            0 => Ok(index),
            _ =>
                if ((index > array_length) || (index < 0)) {
                    Err(String::from("index out of covered range"))
                } else {
                    let mut normalized_index: usize = index - normalizing_index_offset;
                    if (normalized_index < 0) {
                        normalized_index += array_length;
                    } else if (normalized_index >= array_length) {
                        normalized_index -=array_length;
                    }
                    Ok(normalized_index)
                }
        }

    }

    fn increment_count_at_index(&mut self, index: usize) -> Result<(), String> {
        let normalized_index =
            self.normalize_index(index, self.normalizing_index_offset, self.counts_array_length);
        match normalized_index {
            Ok(the_index) => {
                self.counts[the_index] += 1;
                Ok(())
            }
            Err(err) =>
                Err(err)
        }
    }

    fn get_sub_bucket_index(&self, value: i64, bucket_index: usize) -> usize {
        let sum = bucket_index + self.unit_magnitude;
        value.rotate_right(sum as u32) as usize
    }

    fn get_bucket_index(&self, value: i64) -> usize {
        let valueOrred = value | self.sub_bucket_mask;
        self.leading_zeros_count_base - (valueOrred.leading_zeros() as usize)
    }

    fn record_single_value(&mut self, value: i64) -> Result<(), String> {

        match self.counts_array_index(value) {
            Ok(counts_index) =>
                match self.increment_count_at_index(counts_index) {
                    Ok(_) =>
                        if true {
                            Ok(())
                        } else {
                            Err(String::from("Could not record single value"))
                        },
                    Err(err) => Err(String::from("Could not increment stuff"))
                },
            Err(err) => Err(String::from("Could not get index"))
        }

    }

    fn counts_array_index(&self, value: i64) -> Result<usize, String> {
        if value < 0 {
            Err(String::from("Histogram recorded values cannot be negative."))
        } else {
            let bucket_index = self.get_bucket_index(value);
            let sub_bucket_index = self.get_sub_bucket_index(value, bucket_index);
            let result = self.counts_array_index_sub(bucket_index, sub_bucket_index);
            Ok(result)
        }
    }

    fn counts_array_index_sub(&self, bucket_index: usize, sub_bucket_index: usize) -> usize {
        assert!(sub_bucket_index < self.sub_bucket_count);
        assert!(bucket_index == 0 || (sub_bucket_index >= self.sub_bucket_half_count));

        let bucket_base_index = (bucket_index + 1) << self.sub_bucket_half_count_magnitude;
        let offset_in_bucket = sub_bucket_index - self.sub_bucket_half_count;

        bucket_base_index + offset_in_bucket
    }
}

#[test]
fn can_record_single_value() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.record_single_value(99);

    match result {
        Ok(_) => (),
        Err(err) => panic!(format!("could not add single record to histogram because error: {}", err))
    }
}

#[test]
fn can_compute_counts_array_index() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.counts_array_index(99);

    match result {
        Ok(_) => (),
        Err(err) => panic!(format!("could not compute counts array index because error: {}", err))
    }
}

#[test]
fn can_get_bucket_index() {
    let the_hist = init_histo();
    let result = the_hist.get_bucket_index(99);
    assert_eq!(result, 0)
}

#[test]
fn can_get_sub_bucket_index() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.get_sub_bucket_index(99, 1);
    assert_eq!(result, 0)
}<commit_msg>make value be u64 to make nicer casts<commit_after>/*

  This struct essentially encapsulates the "instance variables"

*/
pub struct SimpleHdrHistogram {
    pub leading_zeros_count_base: usize,
    pub sub_bucket_mask: u64,
    pub unit_magnitude: u32,
    pub sub_bucket_count: usize,
    pub sub_bucket_half_count: usize,
    pub sub_bucket_half_count_magnitude: u32,
    pub counts: Vec<u64>,
    pub counts_array_length: usize,
    pub normalizing_index_offset: usize,
}

/*

  Implementing this trait (Default) for our struct gives us a nice way to
  initialize an instance using default args instead of having to provide all of them

 */
impl Default for SimpleHdrHistogram {
    fn default () -> SimpleHdrHistogram {
        SimpleHdrHistogram {
            leading_zeros_count_base: 0,
            sub_bucket_mask: 0,
            unit_magnitude: 0,
            sub_bucket_count: 0,
            sub_bucket_half_count: 0,
            sub_bucket_half_count_magnitude: 0,
            counts: Vec::new(),
            counts_array_length: 0,
            normalizing_index_offset: 0,
        }
    }
}

pub trait HistogramBase {

    //FIXME this stuff could be mostly unsigned

    //TODO should be default impl of this trait
    fn record_single_value(&mut self, value: u64) -> Result<(), String>;

    //TODO should be default impl of this trait
    fn counts_array_index(&self, value: u64) -> Result<usize, String>;

    //TODO should be default impl of this trait
    fn counts_array_index_sub(&self, bucket_index: usize, sub_bucket_index: usize) -> usize;

    //TODO should be default impl of this trait
    fn get_bucket_index(&self, value: u64) -> usize;

    //TODO should be default impl of this trait
    fn get_sub_bucket_index(&self, value: u64, bucket_index: usize) -> usize;

    fn increment_count_at_index(&mut self, index: usize) -> Result<(), String>;
    fn normalize_index(&self, index: usize, normalizing_index_offset: usize, array_length: usize) ->
        Result<usize, String>;

}

impl HistogramBase for SimpleHdrHistogram {

    fn normalize_index(&self, index: usize, normalizing_index_offset: usize, array_length: usize) ->
Result<usize, String> {

        match normalizing_index_offset {
            0 => Ok(index),
            _ =>
                if ((index > array_length) || (index < 0)) {
                    Err(String::from("index out of covered range"))
                } else {
                    let mut normalized_index: usize = index - normalizing_index_offset;
                    if (normalized_index < 0) {
                        normalized_index += array_length;
                    } else if (normalized_index >= array_length) {
                        normalized_index -=array_length;
                    }
                    Ok(normalized_index)
                }
        }

    }

    fn increment_count_at_index(&mut self, index: usize) -> Result<(), String> {
        let normalized_index =
            self.normalize_index(index, self.normalizing_index_offset, self.counts_array_length);
        match normalized_index {
            Ok(the_index) => {
                self.counts[the_index] += 1;
                Ok(())
            }
            Err(err) =>
                Err(err)
        }
    }

    fn get_sub_bucket_index(&self, value: u64, bucket_index: usize) -> usize {
        let sum = bucket_index + self.unit_magnitude as usize;
        value.rotate_right(sum as u32) as usize
    }

    fn get_bucket_index(&self, value: u64) -> usize {
        let valueOrred = value | self.sub_bucket_mask;
        self.leading_zeros_count_base - (valueOrred.leading_zeros() as usize)
    }

    fn record_single_value(&mut self, value: u64) -> Result<(), String> {

        match self.counts_array_index(value) {
            Ok(counts_index) =>
                match self.increment_count_at_index(counts_index) {
                    Ok(_) =>
                        if true {
                            Ok(())
                        } else {
                            Err(String::from("Could not record single value"))
                        },
                    Err(err) => Err(String::from("Could not increment stuff"))
                },
            Err(err) => Err(String::from("Could not get index"))
        }

    }

    fn counts_array_index(&self, value: u64) -> Result<usize, String> {
        if value < 0 {
            Err(String::from("Histogram recorded values cannot be negative."))
        } else {
            let bucket_index = self.get_bucket_index(value);
            let sub_bucket_index = self.get_sub_bucket_index(value, bucket_index);
            let result = self.counts_array_index_sub(bucket_index, sub_bucket_index);
            Ok(result)
        }
    }

    fn counts_array_index_sub(&self, bucket_index: usize, sub_bucket_index: usize) -> usize {
        assert!(sub_bucket_index < self.sub_bucket_count);
        assert!(bucket_index == 0 || (sub_bucket_index >= self.sub_bucket_half_count));

        let bucket_base_index = (bucket_index + 1) << self.sub_bucket_half_count_magnitude;
        let offset_in_bucket = sub_bucket_index - self.sub_bucket_half_count;

        bucket_base_index + offset_in_bucket
    }
}

#[test]
fn can_record_single_value() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.record_single_value(99);

    match result {
        Ok(_) => (),
        Err(err) => panic!(format!("could not add single record to histogram because error: {}", err))
    }
}

#[test]
fn can_compute_counts_array_index() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.counts_array_index(99);

    match result {
        Ok(_) => (),
        Err(err) => panic!(format!("could not compute counts array index because error: {}", err))
    }
}

#[test]
fn can_get_bucket_index() {
    let the_hist = init_histo();
    let result = the_hist.get_bucket_index(99);
    assert_eq!(result, 0)
}

#[test]
fn can_get_sub_bucket_index() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.get_sub_bucket_index(99, 1);
    assert_eq!(result, 0)
}<|endoftext|>


<commit_before>// * This file is part of the uutils coreutils package.
// *
// * (c) Dorota Kapturkiewicz <dokaptur@gmail.com>
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.

// spell-checker:ignore (ToDO) tempdir dyld dylib dragonflybsd optgrps libstdbuf

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
use std::convert::TryFrom;
use std::fs::File;
use std::io::{self, Write};
use std::os::unix::process::ExitStatusExt;
use std::path::PathBuf;
use std::process::Command;
use tempfile::tempdir;
use tempfile::TempDir;
use uucore::InvalidEncodingHandling;

static ABOUT: &str =
    "Run COMMAND, with modified buffering operations for its standard streams.\n\n\
     Mandatory arguments to long options are mandatory for short options too.";
static LONG_HELP: &str = "If MODE is 'L' the corresponding stream will be line buffered.\n\
     This option is invalid with standard input.\n\n\
     If MODE is '0' the corresponding stream will be unbuffered.\n\n\
     Otherwise MODE is a number which may be followed by one of the following:\n\n\
     KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
     In this case the corresponding stream will be fully buffered with the buffer size set to \
     MODE bytes.\n\n\
     NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for e.g.) then \
     that will override corresponding settings changed by 'stdbuf'.\n\
     Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O, \
     and are thus unaffected by 'stdbuf' settings.\n";

mod options {
    pub const INPUT: &str = "input";
    pub const INPUT_SHORT: &str = "i";
    pub const OUTPUT: &str = "output";
    pub const OUTPUT_SHORT: &str = "o";
    pub const ERROR: &str = "error";
    pub const ERROR_SHORT: &str = "e";
    pub const COMMAND: &str = "command";
}

fn get_usage() -> String {
    format!("{0} OPTION... COMMAND", executable!())
}

const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.so"));

enum BufferType {
    Default,
    Line,
    Size(u64),
}

struct ProgramOptions {
    stdin: BufferType,
    stdout: BufferType,
    stderr: BufferType,
}

impl<'a> TryFrom<&ArgMatches<'a>> for ProgramOptions {
    type Error = ProgramOptionsError;

    fn try_from(matches: &ArgMatches) -> Result<Self, Self::Error> {
        Ok(ProgramOptions {
            stdin: check_option(&matches, options::INPUT)?,
            stdout: check_option(&matches, options::OUTPUT)?,
            stderr: check_option(&matches, options::ERROR)?,
        })
    }
}

struct ProgramOptionsError(String);

#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "dragonflybsd"
))]
fn preload_strings() -> (&'static str, &'static str) {
    ("LD_PRELOAD", "so")
}

#[cfg(target_vendor = "apple")]
fn preload_strings() -> (&'static str, &'static str) {
    ("DYLD_LIBRARY_PATH", "dylib")
}

#[cfg(not(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "dragonflybsd",
    target_vendor = "apple"
)))]
fn preload_strings() -> (&'static str, &'static str) {
    crash!(1, "Command not supported for this operating system!")
}

fn parse_size(size: &str) -> Option<u64> {
    let ext = size.trim_start_matches(|c: char| c.is_digit(10));
    let num = size.trim_end_matches(char::is_alphabetic);
    let mut recovered = num.to_owned();
    recovered.push_str(ext);
    if recovered != size {
        return None;
    }
    let buf_size: u64 = match num.parse().ok() {
        Some(m) => m,
        None => return None,
    };
    let (power, base): (u32, u64) = match ext {
        "" => (0, 0),
        "KB" => (1, 1024),
        "K" => (1, 1000),
        "MB" => (2, 1024),
        "M" => (2, 1000),
        "GB" => (3, 1024),
        "G" => (3, 1000),
        "TB" => (4, 1024),
        "T" => (4, 1000),
        "PB" => (5, 1024),
        "P" => (5, 1000),
        "EB" => (6, 1024),
        "E" => (6, 1000),
        "ZB" => (7, 1024),
        "Z" => (7, 1000),
        "YB" => (8, 1024),
        "Y" => (8, 1000),
        _ => return None,
    };
    Some(buf_size * base.pow(power))
}

fn check_option(matches: &ArgMatches, name: &str) -> Result<BufferType, ProgramOptionsError> {
    match matches.value_of(name) {
        Some(value) => match value {
            "L" => {
                if name == options::INPUT {
                    Err(ProgramOptionsError(
                        "line buffering stdin is meaningless".to_string(),
                    ))
                } else {
                    Ok(BufferType::Line)
                }
            }
            x => {
                let size = match parse_size(x) {
                    Some(m) => m,
                    None => return Err(ProgramOptionsError(format!("invalid mode {}", x))),
                };
                Ok(BufferType::Size(size))
            }
        },
        None => Ok(BufferType::Default),
    }
}

fn set_command_env(command: &mut Command, buffer_name: &str, buffer_type: BufferType) {
    match buffer_type {
        BufferType::Size(m) => {
            command.env(buffer_name, m.to_string());
        }
        BufferType::Line => {
            command.env(buffer_name, "L");
        }
        BufferType::Default => {}
    }
}

fn get_preload_env(tmp_dir: &mut TempDir) -> io::Result<(String, PathBuf)> {
    let (preload, extension) = preload_strings();
    let inject_path = tmp_dir.path().join("libstdbuf").with_extension(extension);

    let mut file = File::create(&inject_path)?;
    file.write_all(STDBUF_INJECT)?;

    Ok((preload.to_owned(), inject_path))
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();
    let usage = get_usage();

    let matches = App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(LONG_HELP)
        .setting(AppSettings::TrailingVarArg)
        .arg(
            Arg::with_name(options::INPUT)
                .long(options::INPUT)
                .short(options::INPUT_SHORT)
                .help("adjust standard input stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::OUTPUT, options::ERROR]),
        )
        .arg(
            Arg::with_name(options::OUTPUT)
                .long(options::OUTPUT)
                .short(options::OUTPUT_SHORT)
                .help("adjust standard output stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::INPUT, options::ERROR]),
        )
        .arg(
            Arg::with_name(options::ERROR)
                .long(options::ERROR)
                .short(options::ERROR_SHORT)
                .help("adjust standard error stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::INPUT, options::OUTPUT]),
        )
        .arg(
            Arg::with_name(options::COMMAND)
                .multiple(true)
                .takes_value(true)
                .hidden(true)
                .required(true),
        )
        .get_matches_from(args);

    let options = ProgramOptions::try_from(&matches)
        .unwrap_or_else(|e| crash!(125, "{}\nTry 'stdbuf --help' for more information.", e.0));

    let mut command_values = matches.values_of::<&str>(options::COMMAND).unwrap();
    let mut command = Command::new(command_values.next().unwrap());
    let command_params: Vec<&str> = command_values.collect();

    let mut tmp_dir = tempdir().unwrap();
    let (preload_env, libstdbuf) = return_if_err!(1, get_preload_env(&mut tmp_dir));
    command.env(preload_env, libstdbuf);
    set_command_env(&mut command, "_STDBUF_I", options.stdin);
    set_command_env(&mut command, "_STDBUF_O", options.stdout);
    set_command_env(&mut command, "_STDBUF_E", options.stderr);
    command.args(command_params);

    let mut process = match command.spawn() {
        Ok(p) => p,
        Err(e) => crash!(1, "failed to execute process: {}", e),
    };
    match process.wait() {
        Ok(status) => match status.code() {
            Some(i) => i,
            None => crash!(1, "process killed by signal {}", status.signal().unwrap()),
        },
        Err(e) => crash!(1, "{}", e),
    }
}
<commit_msg>refactor/stdbuf ~ fix `cargo clippy` complaint (clippy::needless_borrow)<commit_after>// * This file is part of the uutils coreutils package.
// *
// * (c) Dorota Kapturkiewicz <dokaptur@gmail.com>
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.

// spell-checker:ignore (ToDO) tempdir dyld dylib dragonflybsd optgrps libstdbuf

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
use std::convert::TryFrom;
use std::fs::File;
use std::io::{self, Write};
use std::os::unix::process::ExitStatusExt;
use std::path::PathBuf;
use std::process::Command;
use tempfile::tempdir;
use tempfile::TempDir;
use uucore::InvalidEncodingHandling;

static ABOUT: &str =
    "Run COMMAND, with modified buffering operations for its standard streams.\n\n\
     Mandatory arguments to long options are mandatory for short options too.";
static LONG_HELP: &str = "If MODE is 'L' the corresponding stream will be line buffered.\n\
     This option is invalid with standard input.\n\n\
     If MODE is '0' the corresponding stream will be unbuffered.\n\n\
     Otherwise MODE is a number which may be followed by one of the following:\n\n\
     KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
     In this case the corresponding stream will be fully buffered with the buffer size set to \
     MODE bytes.\n\n\
     NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for e.g.) then \
     that will override corresponding settings changed by 'stdbuf'.\n\
     Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O, \
     and are thus unaffected by 'stdbuf' settings.\n";

mod options {
    pub const INPUT: &str = "input";
    pub const INPUT_SHORT: &str = "i";
    pub const OUTPUT: &str = "output";
    pub const OUTPUT_SHORT: &str = "o";
    pub const ERROR: &str = "error";
    pub const ERROR_SHORT: &str = "e";
    pub const COMMAND: &str = "command";
}

fn get_usage() -> String {
    format!("{0} OPTION... COMMAND", executable!())
}

const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.so"));

enum BufferType {
    Default,
    Line,
    Size(u64),
}

struct ProgramOptions {
    stdin: BufferType,
    stdout: BufferType,
    stderr: BufferType,
}

impl<'a> TryFrom<&ArgMatches<'a>> for ProgramOptions {
    type Error = ProgramOptionsError;

    fn try_from(matches: &ArgMatches) -> Result<Self, Self::Error> {
        Ok(ProgramOptions {
            stdin: check_option(matches, options::INPUT)?,
            stdout: check_option(matches, options::OUTPUT)?,
            stderr: check_option(matches, options::ERROR)?,
        })
    }
}

struct ProgramOptionsError(String);

#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "dragonflybsd"
))]
fn preload_strings() -> (&'static str, &'static str) {
    ("LD_PRELOAD", "so")
}

#[cfg(target_vendor = "apple")]
fn preload_strings() -> (&'static str, &'static str) {
    ("DYLD_LIBRARY_PATH", "dylib")
}

#[cfg(not(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "dragonflybsd",
    target_vendor = "apple"
)))]
fn preload_strings() -> (&'static str, &'static str) {
    crash!(1, "Command not supported for this operating system!")
}

fn parse_size(size: &str) -> Option<u64> {
    let ext = size.trim_start_matches(|c: char| c.is_digit(10));
    let num = size.trim_end_matches(char::is_alphabetic);
    let mut recovered = num.to_owned();
    recovered.push_str(ext);
    if recovered != size {
        return None;
    }
    let buf_size: u64 = match num.parse().ok() {
        Some(m) => m,
        None => return None,
    };
    let (power, base): (u32, u64) = match ext {
        "" => (0, 0),
        "KB" => (1, 1024),
        "K" => (1, 1000),
        "MB" => (2, 1024),
        "M" => (2, 1000),
        "GB" => (3, 1024),
        "G" => (3, 1000),
        "TB" => (4, 1024),
        "T" => (4, 1000),
        "PB" => (5, 1024),
        "P" => (5, 1000),
        "EB" => (6, 1024),
        "E" => (6, 1000),
        "ZB" => (7, 1024),
        "Z" => (7, 1000),
        "YB" => (8, 1024),
        "Y" => (8, 1000),
        _ => return None,
    };
    Some(buf_size * base.pow(power))
}

fn check_option(matches: &ArgMatches, name: &str) -> Result<BufferType, ProgramOptionsError> {
    match matches.value_of(name) {
        Some(value) => match value {
            "L" => {
                if name == options::INPUT {
                    Err(ProgramOptionsError(
                        "line buffering stdin is meaningless".to_string(),
                    ))
                } else {
                    Ok(BufferType::Line)
                }
            }
            x => {
                let size = match parse_size(x) {
                    Some(m) => m,
                    None => return Err(ProgramOptionsError(format!("invalid mode {}", x))),
                };
                Ok(BufferType::Size(size))
            }
        },
        None => Ok(BufferType::Default),
    }
}

fn set_command_env(command: &mut Command, buffer_name: &str, buffer_type: BufferType) {
    match buffer_type {
        BufferType::Size(m) => {
            command.env(buffer_name, m.to_string());
        }
        BufferType::Line => {
            command.env(buffer_name, "L");
        }
        BufferType::Default => {}
    }
}

fn get_preload_env(tmp_dir: &mut TempDir) -> io::Result<(String, PathBuf)> {
    let (preload, extension) = preload_strings();
    let inject_path = tmp_dir.path().join("libstdbuf").with_extension(extension);

    let mut file = File::create(&inject_path)?;
    file.write_all(STDBUF_INJECT)?;

    Ok((preload.to_owned(), inject_path))
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();
    let usage = get_usage();

    let matches = App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(LONG_HELP)
        .setting(AppSettings::TrailingVarArg)
        .arg(
            Arg::with_name(options::INPUT)
                .long(options::INPUT)
                .short(options::INPUT_SHORT)
                .help("adjust standard input stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::OUTPUT, options::ERROR]),
        )
        .arg(
            Arg::with_name(options::OUTPUT)
                .long(options::OUTPUT)
                .short(options::OUTPUT_SHORT)
                .help("adjust standard output stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::INPUT, options::ERROR]),
        )
        .arg(
            Arg::with_name(options::ERROR)
                .long(options::ERROR)
                .short(options::ERROR_SHORT)
                .help("adjust standard error stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::INPUT, options::OUTPUT]),
        )
        .arg(
            Arg::with_name(options::COMMAND)
                .multiple(true)
                .takes_value(true)
                .hidden(true)
                .required(true),
        )
        .get_matches_from(args);

    let options = ProgramOptions::try_from(&matches)
        .unwrap_or_else(|e| crash!(125, "{}\nTry 'stdbuf --help' for more information.", e.0));

    let mut command_values = matches.values_of::<&str>(options::COMMAND).unwrap();
    let mut command = Command::new(command_values.next().unwrap());
    let command_params: Vec<&str> = command_values.collect();

    let mut tmp_dir = tempdir().unwrap();
    let (preload_env, libstdbuf) = return_if_err!(1, get_preload_env(&mut tmp_dir));
    command.env(preload_env, libstdbuf);
    set_command_env(&mut command, "_STDBUF_I", options.stdin);
    set_command_env(&mut command, "_STDBUF_O", options.stdout);
    set_command_env(&mut command, "_STDBUF_E", options.stderr);
    command.args(command_params);

    let mut process = match command.spawn() {
        Ok(p) => p,
        Err(e) => crash!(1, "failed to execute process: {}", e),
    };
    match process.wait() {
        Ok(status) => match status.code() {
            Some(i) => i,
            None => crash!(1, "process killed by signal {}", status.signal().unwrap()),
        },
        Err(e) => crash!(1, "{}", e),
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Liberally derived from the [Firefox JS implementation]
//! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/inspector.js).

use devtools_traits::{DevtoolScriptControlMsg, NodeInfo};
use devtools_traits::DevtoolScriptControlMsg::{GetRootNode, GetDocumentElement, GetChildren};
use devtools_traits::DevtoolScriptControlMsg::{GetLayout, ModifyAttribute};

use actor::{Actor, ActorRegistry};
use protocol::JsonPacketStream;

use std::collections::BTreeMap;
use msg::constellation_msg::PipelineId;
use rustc_serialize::json::{self, Json, ToJson};
use std::cell::RefCell;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender};

pub struct InspectorActor {
    pub name: String,
    pub walker: RefCell<Option<String>>,
    pub pageStyle: RefCell<Option<String>>,
    pub highlighter: RefCell<Option<String>>,
    pub script_chan: Sender<DevtoolScriptControlMsg>,
    pub pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct GetHighlighterReply {
    highligter: HighlighterMsg, // sic.
    from: String,
}

#[derive(RustcEncodable)]
struct HighlighterMsg {
    actor: String,
}

struct HighlighterActor {
    name: String,
}

pub struct NodeActor {
    pub name: String,
    script_chan: Sender<DevtoolScriptControlMsg>,
    pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct ShowBoxModelReply {
    from: String,
}

#[derive(RustcEncodable)]
struct HideBoxModelReply {
    from: String,
}

impl Actor for HighlighterActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      _registry: &ActorRegistry,
                      msg_type: &str,
                      _msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "showBoxModel" => {
                let msg = ShowBoxModelReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "hideBoxModel" => {
                let msg = HideBoxModelReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}

#[derive(RustcEncodable)]
struct ModifyAttributeReply{
    from: String,
}

impl Actor for NodeActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "modifyAttributes" => {
                let target = msg.get(&"to".to_string()).unwrap().as_string().unwrap();
                let mods = msg.get(&"modifications".to_string()).unwrap().as_array().unwrap();
                let modifications = mods.iter().map(|json_mod| {
                    json::decode(&json_mod.to_string()).unwrap()
                }).collect();

                self.script_chan.send(ModifyAttribute(self.pipeline,
                                                      registry.actor_to_script(target.to_string()),
                                                      modifications))
                                .unwrap();
                let reply = ModifyAttributeReply{
                    from: self.name(),
                };
                stream.write_json_packet(&reply);
                true
            }

            _ => false,
        })
    }
}

#[derive(RustcEncodable)]
struct GetWalkerReply {
    from: String,
    walker: WalkerMsg,
}

#[derive(RustcEncodable)]
struct WalkerMsg {
    actor: String,
    root: NodeActorMsg,
}

#[derive(RustcEncodable)]
struct AttrMsg {
    namespace: String,
    name: String,
    value: String,
}

#[derive(RustcEncodable)]
struct NodeActorMsg {
    actor: String,
    baseURI: String,
    parent: String,
    nodeType: u16,
    namespaceURI: String,
    nodeName: String,
    numChildren: usize,

    name: String,
    publicId: String,
    systemId: String,

    attrs: Vec<AttrMsg>,

    pseudoClassLocks: Vec<String>,

    isDisplayed: bool,

    hasEventListeners: bool,

    isDocumentElement: bool,

    shortValue: String,
    incompleteValue: bool,
}

trait NodeInfoToProtocol {
    fn encode(self,
              actors: &ActorRegistry,
              display: bool,
              script_chan: Sender<DevtoolScriptControlMsg>,
              pipeline: PipelineId) -> NodeActorMsg;
}

impl NodeInfoToProtocol for NodeInfo {
    fn encode(self,
              actors: &ActorRegistry,
              display: bool,
              script_chan: Sender<DevtoolScriptControlMsg>,
              pipeline: PipelineId) -> NodeActorMsg {
        let actor_name = if !actors.script_actor_registered(self.uniqueId.clone()) {
            let name = actors.new_name("node");
            let node_actor = NodeActor {
                name: name.clone(),
                script_chan: script_chan,
                pipeline: pipeline.clone(),
            };
            actors.register_script_actor(self.uniqueId, name.clone());
            actors.register_later(box node_actor);
            name
        } else {
            actors.script_to_actor(self.uniqueId)
        };

        NodeActorMsg {
            actor: actor_name,
            baseURI: self.baseURI,
            parent: actors.script_to_actor(self.parent.clone()),
            nodeType: self.nodeType,
            namespaceURI: self.namespaceURI,
            nodeName: self.nodeName,
            numChildren: self.numChildren,

            name: self.name,
            publicId: self.publicId,
            systemId: self.systemId,

            attrs: self.attrs.into_iter().map(|attr| {
                AttrMsg {
                    namespace: attr.namespace,
                    name: attr.name,
                    value: attr.value,
                }
            }).collect(),

            pseudoClassLocks: vec!(), //TODO get this data from script

            isDisplayed: display,

            hasEventListeners: false, //TODO get this data from script

            isDocumentElement: self.isDocumentElement,

            shortValue: self.shortValue,
            incompleteValue: self.incompleteValue,
        }
    }
}

struct WalkerActor {
    name: String,
    script_chan: Sender<DevtoolScriptControlMsg>,
    pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct QuerySelectorReply {
    from: String,
}

#[derive(RustcEncodable)]
struct DocumentElementReply {
    from: String,
    node: NodeActorMsg,
}

#[derive(RustcEncodable)]
struct ClearPseudoclassesReply {
    from: String,
}

#[derive(RustcEncodable)]
struct ChildrenReply {
    hasFirst: bool,
    hasLast: bool,
    nodes: Vec<NodeActorMsg>,
    from: String,
}

impl Actor for WalkerActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "querySelector" => {
                let msg = QuerySelectorReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "documentElement" => {
                let (tx, rx) = channel();
                self.script_chan.send(GetDocumentElement(self.pipeline, tx)).unwrap();
                let doc_elem_info = rx.recv().unwrap();
                let node = doc_elem_info.encode(registry, true, self.script_chan.clone(), self.pipeline);

                let msg = DocumentElementReply {
                    from: self.name(),
                    node: node,
                };
                stream.write_json_packet(&msg);
                true
            }

            "clearPseudoClassLocks" => {
                let msg = ClearPseudoclassesReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "children" => {
                let target = msg.get(&"node".to_string()).unwrap().as_string().unwrap();
                let (tx, rx) = channel();
                self.script_chan.send(GetChildren(self.pipeline,
                                                  registry.actor_to_script(target.to_string()),
                                                  tx))
                                .unwrap();
                let children = rx.recv().unwrap();

                let msg = ChildrenReply {
                    hasFirst: true,
                    hasLast: true,
                    nodes: children.into_iter().map(|child| {
                        child.encode(registry, true, self.script_chan.clone(), self.pipeline)
                    }).collect(),
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}

#[derive(RustcEncodable)]
struct GetPageStyleReply {
    from: String,
    pageStyle: PageStyleMsg,
}

#[derive(RustcEncodable)]
struct PageStyleMsg {
    actor: String,
}

struct PageStyleActor {
    name: String,
    script_chan: Sender<DevtoolScriptControlMsg>,
    pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct GetAppliedReply {
    entries: Vec<AppliedEntry>,
    rules: Vec<AppliedRule>,
    sheets: Vec<AppliedSheet>,
    from: String,
}

#[derive(RustcEncodable)]
struct GetComputedReply {
    computed: Vec<u32>, //XXX all css props
    from: String,
}

#[derive(RustcEncodable)]
struct AppliedEntry {
    rule: String,
    pseudoElement: Json,
    isSystem: bool,
    matchedSelectors: Vec<String>,
}

#[derive(RustcEncodable)]
struct AppliedRule {
    actor: String,
    __type__: u32,
    href: String,
    cssText: String,
    line: u32,
    column: u32,
    parentStyleSheet: String,
}

#[derive(RustcEncodable)]
struct AppliedSheet {
    actor: String,
    href: String,
    nodeHref: String,
    disabled: bool,
    title: String,
    system: bool,
    styleSheetIndex: isize,
    ruleCount: usize,
}

#[derive(RustcEncodable)]
struct GetLayoutReply {
    width: i32,
    height: i32,
    autoMargins: Json,
    from: String,
}

#[derive(RustcEncodable)]
#[allow(dead_code)]
struct AutoMargins {
    top: String,
    bottom: String,
    left: String,
    right: String,
}

impl Actor for PageStyleActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "getApplied" => {
                //TODO: query script for relevant applied styles to node (msg.node)
                let msg = GetAppliedReply {
                    entries: vec!(),
                    rules: vec!(),
                    sheets: vec!(),
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "getComputed" => {
                //TODO: query script for relevant computed styles on node (msg.node)
                let msg = GetComputedReply {
                    computed: vec!(),
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            //TODO: query script for box layout properties of node (msg.node)
            "getLayout" => {
                let target = msg.get(&"node".to_string()).unwrap().as_string().unwrap();
                let (tx, rx) = channel();
                self.script_chan.send(GetLayout(self.pipeline,
                                      registry.actor_to_script(target.to_string()),
                                      tx))
                                .unwrap();
                let (width, height) = rx.recv().unwrap();

                let auto_margins = msg.get(&"autoMargins".to_string()).unwrap().as_boolean().unwrap();

                //TODO: the remaining layout properties (margin, border, padding, position)
                //      as specified in getLayout in
                //      http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
                let msg = GetLayoutReply {
                    width: width.round() as i32,
                    height: height.round() as i32,
                    autoMargins: if auto_margins {
                        //TODO: real values like processMargins in
                        //  http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
                        let mut m = BTreeMap::new();
                        m.insert("top".to_string(), "auto".to_string().to_json());
                        m.insert("bottom".to_string(), "auto".to_string().to_json());
                        m.insert("left".to_string(), "auto".to_string().to_json());
                        m.insert("right".to_string(), "auto".to_string().to_json());
                        Json::Object(m)
                    } else {
                        Json::Null
                    },
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}

impl Actor for InspectorActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      _msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "getWalker" => {
                if self.walker.borrow().is_none() {
                    let walker = WalkerActor {
                        name: registry.new_name("walker"),
                        script_chan: self.script_chan.clone(),
                        pipeline: self.pipeline,
                    };
                    let mut walker_name = self.walker.borrow_mut();
                    *walker_name = Some(walker.name());
                    registry.register_later(box walker);
                }

                let (tx, rx) = channel();
                self.script_chan.send(GetRootNode(self.pipeline, tx)).unwrap();
                let root_info = rx.recv().unwrap();

                let node = root_info.encode(registry, false, self.script_chan.clone(), self.pipeline);

                let msg = GetWalkerReply {
                    from: self.name(),
                    walker: WalkerMsg {
                        actor: self.walker.borrow().clone().unwrap(),
                        root: node,
                    }
                };
                stream.write_json_packet(&msg);
                true
            }

            "getPageStyle" => {
                if self.pageStyle.borrow().is_none() {
                    let style = PageStyleActor {
                        name: registry.new_name("pageStyle"),
                        script_chan: self.script_chan.clone(),
                        pipeline: self.pipeline,
                    };
                    let mut pageStyle = self.pageStyle.borrow_mut();
                    *pageStyle = Some(style.name());
                    registry.register_later(box style);
                }

                let msg = GetPageStyleReply {
                    from: self.name(),
                    pageStyle: PageStyleMsg {
                        actor: self.pageStyle.borrow().clone().unwrap(),
                    },
                };
                stream.write_json_packet(&msg);
                true
            }

            //TODO: this is an old message; try adding highlightable to the root traits instead
            //      and support getHighlighter instead
            //"highlight" => {}
            "getHighlighter" => {
                if self.highlighter.borrow().is_none() {
                    let highlighter_actor = HighlighterActor {
                        name: registry.new_name("highlighter"),
                    };
                    let mut highlighter = self.highlighter.borrow_mut();
                    *highlighter = Some(highlighter_actor.name());
                    registry.register_later(box highlighter_actor);
                }

                let msg = GetHighlighterReply {
                    from: self.name(),
                    highligter: HighlighterMsg {
                        actor: self.highlighter.borrow().clone().unwrap(),
                    },
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}
<commit_msg>Don't crash when Firefox doesn't send autoMargins.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Liberally derived from the [Firefox JS implementation]
//! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/inspector.js).

use devtools_traits::{DevtoolScriptControlMsg, NodeInfo};
use devtools_traits::DevtoolScriptControlMsg::{GetRootNode, GetDocumentElement, GetChildren};
use devtools_traits::DevtoolScriptControlMsg::{GetLayout, ModifyAttribute};

use actor::{Actor, ActorRegistry};
use protocol::JsonPacketStream;

use std::collections::BTreeMap;
use msg::constellation_msg::PipelineId;
use rustc_serialize::json::{self, Json, ToJson};
use std::cell::RefCell;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender};

pub struct InspectorActor {
    pub name: String,
    pub walker: RefCell<Option<String>>,
    pub pageStyle: RefCell<Option<String>>,
    pub highlighter: RefCell<Option<String>>,
    pub script_chan: Sender<DevtoolScriptControlMsg>,
    pub pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct GetHighlighterReply {
    highligter: HighlighterMsg, // sic.
    from: String,
}

#[derive(RustcEncodable)]
struct HighlighterMsg {
    actor: String,
}

struct HighlighterActor {
    name: String,
}

pub struct NodeActor {
    pub name: String,
    script_chan: Sender<DevtoolScriptControlMsg>,
    pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct ShowBoxModelReply {
    from: String,
}

#[derive(RustcEncodable)]
struct HideBoxModelReply {
    from: String,
}

impl Actor for HighlighterActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      _registry: &ActorRegistry,
                      msg_type: &str,
                      _msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "showBoxModel" => {
                let msg = ShowBoxModelReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "hideBoxModel" => {
                let msg = HideBoxModelReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}

#[derive(RustcEncodable)]
struct ModifyAttributeReply{
    from: String,
}

impl Actor for NodeActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "modifyAttributes" => {
                let target = msg.get(&"to".to_string()).unwrap().as_string().unwrap();
                let mods = msg.get(&"modifications".to_string()).unwrap().as_array().unwrap();
                let modifications = mods.iter().map(|json_mod| {
                    json::decode(&json_mod.to_string()).unwrap()
                }).collect();

                self.script_chan.send(ModifyAttribute(self.pipeline,
                                                      registry.actor_to_script(target.to_string()),
                                                      modifications))
                                .unwrap();
                let reply = ModifyAttributeReply{
                    from: self.name(),
                };
                stream.write_json_packet(&reply);
                true
            }

            _ => false,
        })
    }
}

#[derive(RustcEncodable)]
struct GetWalkerReply {
    from: String,
    walker: WalkerMsg,
}

#[derive(RustcEncodable)]
struct WalkerMsg {
    actor: String,
    root: NodeActorMsg,
}

#[derive(RustcEncodable)]
struct AttrMsg {
    namespace: String,
    name: String,
    value: String,
}

#[derive(RustcEncodable)]
struct NodeActorMsg {
    actor: String,
    baseURI: String,
    parent: String,
    nodeType: u16,
    namespaceURI: String,
    nodeName: String,
    numChildren: usize,

    name: String,
    publicId: String,
    systemId: String,

    attrs: Vec<AttrMsg>,

    pseudoClassLocks: Vec<String>,

    isDisplayed: bool,

    hasEventListeners: bool,

    isDocumentElement: bool,

    shortValue: String,
    incompleteValue: bool,
}

trait NodeInfoToProtocol {
    fn encode(self,
              actors: &ActorRegistry,
              display: bool,
              script_chan: Sender<DevtoolScriptControlMsg>,
              pipeline: PipelineId) -> NodeActorMsg;
}

impl NodeInfoToProtocol for NodeInfo {
    fn encode(self,
              actors: &ActorRegistry,
              display: bool,
              script_chan: Sender<DevtoolScriptControlMsg>,
              pipeline: PipelineId) -> NodeActorMsg {
        let actor_name = if !actors.script_actor_registered(self.uniqueId.clone()) {
            let name = actors.new_name("node");
            let node_actor = NodeActor {
                name: name.clone(),
                script_chan: script_chan,
                pipeline: pipeline.clone(),
            };
            actors.register_script_actor(self.uniqueId, name.clone());
            actors.register_later(box node_actor);
            name
        } else {
            actors.script_to_actor(self.uniqueId)
        };

        NodeActorMsg {
            actor: actor_name,
            baseURI: self.baseURI,
            parent: actors.script_to_actor(self.parent.clone()),
            nodeType: self.nodeType,
            namespaceURI: self.namespaceURI,
            nodeName: self.nodeName,
            numChildren: self.numChildren,

            name: self.name,
            publicId: self.publicId,
            systemId: self.systemId,

            attrs: self.attrs.into_iter().map(|attr| {
                AttrMsg {
                    namespace: attr.namespace,
                    name: attr.name,
                    value: attr.value,
                }
            }).collect(),

            pseudoClassLocks: vec!(), //TODO get this data from script

            isDisplayed: display,

            hasEventListeners: false, //TODO get this data from script

            isDocumentElement: self.isDocumentElement,

            shortValue: self.shortValue,
            incompleteValue: self.incompleteValue,
        }
    }
}

struct WalkerActor {
    name: String,
    script_chan: Sender<DevtoolScriptControlMsg>,
    pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct QuerySelectorReply {
    from: String,
}

#[derive(RustcEncodable)]
struct DocumentElementReply {
    from: String,
    node: NodeActorMsg,
}

#[derive(RustcEncodable)]
struct ClearPseudoclassesReply {
    from: String,
}

#[derive(RustcEncodable)]
struct ChildrenReply {
    hasFirst: bool,
    hasLast: bool,
    nodes: Vec<NodeActorMsg>,
    from: String,
}

impl Actor for WalkerActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "querySelector" => {
                let msg = QuerySelectorReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "documentElement" => {
                let (tx, rx) = channel();
                self.script_chan.send(GetDocumentElement(self.pipeline, tx)).unwrap();
                let doc_elem_info = rx.recv().unwrap();
                let node = doc_elem_info.encode(registry, true, self.script_chan.clone(), self.pipeline);

                let msg = DocumentElementReply {
                    from: self.name(),
                    node: node,
                };
                stream.write_json_packet(&msg);
                true
            }

            "clearPseudoClassLocks" => {
                let msg = ClearPseudoclassesReply {
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "children" => {
                let target = msg.get(&"node".to_string()).unwrap().as_string().unwrap();
                let (tx, rx) = channel();
                self.script_chan.send(GetChildren(self.pipeline,
                                                  registry.actor_to_script(target.to_string()),
                                                  tx))
                                .unwrap();
                let children = rx.recv().unwrap();

                let msg = ChildrenReply {
                    hasFirst: true,
                    hasLast: true,
                    nodes: children.into_iter().map(|child| {
                        child.encode(registry, true, self.script_chan.clone(), self.pipeline)
                    }).collect(),
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}

#[derive(RustcEncodable)]
struct GetPageStyleReply {
    from: String,
    pageStyle: PageStyleMsg,
}

#[derive(RustcEncodable)]
struct PageStyleMsg {
    actor: String,
}

struct PageStyleActor {
    name: String,
    script_chan: Sender<DevtoolScriptControlMsg>,
    pipeline: PipelineId,
}

#[derive(RustcEncodable)]
struct GetAppliedReply {
    entries: Vec<AppliedEntry>,
    rules: Vec<AppliedRule>,
    sheets: Vec<AppliedSheet>,
    from: String,
}

#[derive(RustcEncodable)]
struct GetComputedReply {
    computed: Vec<u32>, //XXX all css props
    from: String,
}

#[derive(RustcEncodable)]
struct AppliedEntry {
    rule: String,
    pseudoElement: Json,
    isSystem: bool,
    matchedSelectors: Vec<String>,
}

#[derive(RustcEncodable)]
struct AppliedRule {
    actor: String,
    __type__: u32,
    href: String,
    cssText: String,
    line: u32,
    column: u32,
    parentStyleSheet: String,
}

#[derive(RustcEncodable)]
struct AppliedSheet {
    actor: String,
    href: String,
    nodeHref: String,
    disabled: bool,
    title: String,
    system: bool,
    styleSheetIndex: isize,
    ruleCount: usize,
}

#[derive(RustcEncodable)]
struct GetLayoutReply {
    width: i32,
    height: i32,
    autoMargins: Json,
    from: String,
}

#[derive(RustcEncodable)]
#[allow(dead_code)]
struct AutoMargins {
    top: String,
    bottom: String,
    left: String,
    right: String,
}

impl Actor for PageStyleActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "getApplied" => {
                //TODO: query script for relevant applied styles to node (msg.node)
                let msg = GetAppliedReply {
                    entries: vec!(),
                    rules: vec!(),
                    sheets: vec!(),
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            "getComputed" => {
                //TODO: query script for relevant computed styles on node (msg.node)
                let msg = GetComputedReply {
                    computed: vec!(),
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            //TODO: query script for box layout properties of node (msg.node)
            "getLayout" => {
                let target = msg.get(&"node".to_string()).unwrap().as_string().unwrap();
                let (tx, rx) = channel();
                self.script_chan.send(GetLayout(self.pipeline,
                                      registry.actor_to_script(target.to_string()),
                                      tx))
                                .unwrap();
                let (width, height) = rx.recv().unwrap();

                let auto_margins = msg.get(&"autoMargins".to_string())
                    .and_then(&Json::as_boolean).unwrap_or(false);

                //TODO: the remaining layout properties (margin, border, padding, position)
                //      as specified in getLayout in
                //      http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
                let msg = GetLayoutReply {
                    width: width.round() as i32,
                    height: height.round() as i32,
                    autoMargins: if auto_margins {
                        //TODO: real values like processMargins in
                        //  http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
                        let mut m = BTreeMap::new();
                        m.insert("top".to_string(), "auto".to_string().to_json());
                        m.insert("bottom".to_string(), "auto".to_string().to_json());
                        m.insert("left".to_string(), "auto".to_string().to_json());
                        m.insert("right".to_string(), "auto".to_string().to_json());
                        Json::Object(m)
                    } else {
                        Json::Null
                    },
                    from: self.name(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}

impl Actor for InspectorActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &str,
                      _msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type {
            "getWalker" => {
                if self.walker.borrow().is_none() {
                    let walker = WalkerActor {
                        name: registry.new_name("walker"),
                        script_chan: self.script_chan.clone(),
                        pipeline: self.pipeline,
                    };
                    let mut walker_name = self.walker.borrow_mut();
                    *walker_name = Some(walker.name());
                    registry.register_later(box walker);
                }

                let (tx, rx) = channel();
                self.script_chan.send(GetRootNode(self.pipeline, tx)).unwrap();
                let root_info = rx.recv().unwrap();

                let node = root_info.encode(registry, false, self.script_chan.clone(), self.pipeline);

                let msg = GetWalkerReply {
                    from: self.name(),
                    walker: WalkerMsg {
                        actor: self.walker.borrow().clone().unwrap(),
                        root: node,
                    }
                };
                stream.write_json_packet(&msg);
                true
            }

            "getPageStyle" => {
                if self.pageStyle.borrow().is_none() {
                    let style = PageStyleActor {
                        name: registry.new_name("pageStyle"),
                        script_chan: self.script_chan.clone(),
                        pipeline: self.pipeline,
                    };
                    let mut pageStyle = self.pageStyle.borrow_mut();
                    *pageStyle = Some(style.name());
                    registry.register_later(box style);
                }

                let msg = GetPageStyleReply {
                    from: self.name(),
                    pageStyle: PageStyleMsg {
                        actor: self.pageStyle.borrow().clone().unwrap(),
                    },
                };
                stream.write_json_packet(&msg);
                true
            }

            //TODO: this is an old message; try adding highlightable to the root traits instead
            //      and support getHighlighter instead
            //"highlight" => {}
            "getHighlighter" => {
                if self.highlighter.borrow().is_none() {
                    let highlighter_actor = HighlighterActor {
                        name: registry.new_name("highlighter"),
                    };
                    let mut highlighter = self.highlighter.borrow_mut();
                    *highlighter = Some(highlighter_actor.name());
                    registry.register_later(box highlighter_actor);
                }

                let msg = GetHighlighterReply {
                    from: self.name(),
                    highligter: HighlighterMsg {
                        actor: self.highlighter.borrow().clone().unwrap(),
                    },
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false,
        })
    }
}
<|endoftext|>


<commit_before>use proc_macro2::{Span, TokenStream};
use std::borrow::Cow;
use std::iter;
use syn::{
    self, Data, DataEnum, DataStruct, DeriveInput, Fields, FieldsNamed, FieldsUnnamed,
    GenericParam, Generics, Ident, Lifetime, LifetimeDef, Type,
};

pub fn derive(input: TokenStream) -> TokenStream {
    let DeriveInput {
        ident,
        data,
        generics,
        ..
    } = syn::parse2(input).expect("Input is checked by rustc");

    let tokens = match data {
        Data::Struct(ast) => derive_struct(ast, ident, generics),
        Data::Enum(ast) => derive_enum(ast, ident, generics),
        Data::Union(_) => panic!("Unions are not supported"),
    };

    tokens.into()
}

fn derive_struct(ast: DataStruct, ident: Ident, generics: Generics) -> TokenStream {
    let (field_idents, field_types) = get_info_from_fields(&ast.fields);
    let field_idents2 = &field_idents;

    // destructure the struct so the the fields can be accessed by the push implementation
    let destructured = match &ast.fields {
        Fields::Named(_) => quote! { let #ident { #(#field_idents2),* } = self; },
        Fields::Unnamed(_) => quote! { let #ident ( #(#field_idents2),* ) = self; },
        Fields::Unit => quote!{},
    };

    let push_impl = gen_push_impl(0, &field_idents, &field_types);

    gen_impl(&ident, generics, quote! { #destructured #push_impl })
}

fn derive_enum(ast: DataEnum, ident: Ident, generics: Generics) -> TokenStream {
    // generate a correct implementation for each variant, destructuring the enum
    // to get access to the values
    let match_arms = ast.variants.iter().enumerate().map(|(tag, variant)| {
        let (field_idents, field_types) = get_info_from_fields(&variant.fields);
        let field_idents2 = &field_idents;
        let variant_ident = &variant.ident;

        let pattern = match &variant.fields {
            Fields::Named(_) => quote! { #ident::#variant_ident{ #(#field_idents2),* } },
            Fields::Unnamed(_) => quote! { #ident::#variant_ident( #(#field_idents2),* ) },
            Fields::Unit => quote! { #ident::#variant_ident },
        };

        let push_impl = gen_push_impl(tag, &field_idents, &field_types);

        quote! {
            #pattern => {
                #push_impl
            }
        }
    });

    // build the final match from the individual arms
    let push_impl = quote! {
        match self {
            #(#match_arms),*
        };
    };

    gen_impl(&ident, generics, push_impl)
}

fn gen_impl(ident: &Ident, generics: Generics, push_impl: TokenStream) -> TokenStream {
    // add trailing comma or add a where if there are no predicates
    let (_, ty_generics, where_clause) = generics.split_for_impl();
    let where_clause = where_clause
        .map(|clause| quote! { #clause, })
        .unwrap_or(quote! { where });

    // generate the generic params for the impl block
    // these need an additional lifetime used by Pushable
    let mut generics = generics.clone();
    let lifetime = GenericParam::from(LifetimeDef::new(Lifetime::new("'__vm", Span::call_site())));
    generics.params.insert(0, lifetime);
    let (impl_generics, ..) = generics.split_for_impl();

    let pushable_bounds = create_pushable_bounds(&generics);

    quote! {
        #[automatically_derived]
        #[allow(unused_attributes)]
        impl #impl_generics ::gluon::vm::api::Pushable<'__vm> for #ident #ty_generics
        #where_clause #(#pushable_bounds),*
        {
            fn push(self, vm: &'__vm ::gluon::vm::thread::Thread, ctx: &mut ::gluon::vm::thread::Context) -> ::gluon::vm::Result<()> {
                #push_impl
                Ok(())
            }
        }
    }
}

fn gen_push_impl(tag: usize, field_idents: &[Cow<Ident>], field_types: &[&Type]) -> TokenStream {
    debug_assert!(field_idents.len() == field_types.len());
    let len = field_idents.len();

    // push each field onto the stack
    // this has to be done in reverse order so the fields come out in the correct
    // order when popping the stack
    let stack_pushes = field_idents
        .iter()
        .zip(field_types)
        .map(|(ident, ty)| {
            quote! {
                <#ty as ::gluon::vm::api::Pushable<'__vm>>::push(#ident, vm, ctx)?;
            }
        })
        .rev();

    // since the number of fields is statically known, we can allocate all values in an array
    let array_init = iter::repeat(quote! { ::gluon::vm::internal::Value::int(0) }).take(len);
    let stack_init = quote! {
        let mut fields = [ #(#array_init),* ];

        for i in 0..#len {
            fields[i] = ctx.stack.pop();
        }

        let val = ctx.new_data(vm, #tag as ::gluon::vm::types::VmTag, &fields)?;
        ctx.stack.push(val);
    };

    quote! {
        #(#stack_pushes)*
        #stack_init
    }
}

fn create_pushable_bounds(generics: &Generics) -> Vec<TokenStream> {
    generics
        .params
        .iter()
        .filter_map(|param| match param {
            GenericParam::Type(param) => Some(&param.ident),
            _ => None,
        })
        .map(|ty| {
            quote! {
                #ty: ::gluon::vm::api::Pushable<'__vm>
            }
        })
        .collect()
}

fn get_info_from_fields(fields: &Fields) -> (Vec<Cow<Ident>>, Vec<&Type>) {
    // get all the fields if there are any
    let fields = match fields {
        Fields::Named(FieldsNamed { named, .. }) => named,
        Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => unnamed,
        Fields::Unit => return (Vec::new(), Vec::new()),
    };

    fields
        .iter()
        .enumerate()
        .map(|(idx, field)| {
            // if the fields belong to a struct we use the field name,
            // otherwise generate one from the index of the tuple element
            let ident = match &field.ident {
                Some(ident) => Cow::Borrowed(ident),
                None => Cow::Owned(Ident::new(&format!("_{}", idx), Span::call_site())),
            };

            (ident, &field.ty)
        })
        .unzip()
}
<commit_msg>Directly initialize the array with the right values by popping the stack<commit_after>use proc_macro2::{Span, TokenStream};
use std::borrow::Cow;
use std::iter;
use syn::{
    self, Data, DataEnum, DataStruct, DeriveInput, Fields, FieldsNamed, FieldsUnnamed,
    GenericParam, Generics, Ident, Lifetime, LifetimeDef, Type,
};

pub fn derive(input: TokenStream) -> TokenStream {
    let DeriveInput {
        ident,
        data,
        generics,
        ..
    } = syn::parse2(input).expect("Input is checked by rustc");

    let tokens = match data {
        Data::Struct(ast) => derive_struct(ast, ident, generics),
        Data::Enum(ast) => derive_enum(ast, ident, generics),
        Data::Union(_) => panic!("Unions are not supported"),
    };

    tokens.into()
}

fn derive_struct(ast: DataStruct, ident: Ident, generics: Generics) -> TokenStream {
    let (field_idents, field_types) = get_info_from_fields(&ast.fields);
    let field_idents2 = &field_idents;

    // destructure the struct so the the fields can be accessed by the push implementation
    let destructured = match &ast.fields {
        Fields::Named(_) => quote! { let #ident { #(#field_idents2),* } = self; },
        Fields::Unnamed(_) => quote! { let #ident ( #(#field_idents2),* ) = self; },
        Fields::Unit => quote!{},
    };

    let push_impl = gen_push_impl(0, &field_idents, &field_types);

    gen_impl(&ident, generics, quote! { #destructured #push_impl })
}

fn derive_enum(ast: DataEnum, ident: Ident, generics: Generics) -> TokenStream {
    // generate a correct implementation for each variant, destructuring the enum
    // to get access to the values
    let match_arms = ast.variants.iter().enumerate().map(|(tag, variant)| {
        let (field_idents, field_types) = get_info_from_fields(&variant.fields);
        let field_idents2 = &field_idents;
        let variant_ident = &variant.ident;

        let pattern = match &variant.fields {
            Fields::Named(_) => quote! { #ident::#variant_ident{ #(#field_idents2),* } },
            Fields::Unnamed(_) => quote! { #ident::#variant_ident( #(#field_idents2),* ) },
            Fields::Unit => quote! { #ident::#variant_ident },
        };

        let push_impl = gen_push_impl(tag, &field_idents, &field_types);

        quote! {
            #pattern => {
                #push_impl
            }
        }
    });

    // build the final match from the individual arms
    let push_impl = quote! {
        match self {
            #(#match_arms),*
        };
    };

    gen_impl(&ident, generics, push_impl)
}

fn gen_impl(ident: &Ident, generics: Generics, push_impl: TokenStream) -> TokenStream {
    // add trailing comma or add a where if there are no predicates
    let (_, ty_generics, where_clause) = generics.split_for_impl();
    let where_clause = where_clause
        .map(|clause| quote! { #clause, })
        .unwrap_or(quote! { where });

    // generate the generic params for the impl block
    // these need an additional lifetime used by Pushable
    let mut generics = generics.clone();
    let lifetime = GenericParam::from(LifetimeDef::new(Lifetime::new("'__vm", Span::call_site())));
    generics.params.insert(0, lifetime);
    let (impl_generics, ..) = generics.split_for_impl();

    let pushable_bounds = create_pushable_bounds(&generics);

    quote! {
        #[automatically_derived]
        #[allow(unused_attributes)]
        impl #impl_generics ::gluon::vm::api::Pushable<'__vm> for #ident #ty_generics
        #where_clause #(#pushable_bounds),*
        {
            fn push(self, vm: &'__vm ::gluon::vm::thread::Thread, ctx: &mut ::gluon::vm::thread::Context) -> ::gluon::vm::Result<()> {
                #push_impl
                Ok(())
            }
        }
    }
}

fn gen_push_impl(tag: usize, field_idents: &[Cow<Ident>], field_types: &[&Type]) -> TokenStream {
    debug_assert!(field_idents.len() == field_types.len());

    // push each field onto the stack
    // this has to be done in reverse order so the fields come out in the correct
    // order when popping the stack
    let stack_pushes = field_idents
        .iter()
        .zip(field_types)
        .map(|(ident, ty)| {
            quote! {
                <#ty as ::gluon::vm::api::Pushable<'__vm>>::push(#ident, vm, ctx)?;
            }
        })
        .rev();

    // since the number of fields is statically known, we can allocate an array
    // by popping the stack for each field
    let array_init = iter::repeat(quote! { ctx.stack.pop() }).take(field_idents.len());

    quote! {
        #(#stack_pushes)*
        let fields = [ #(#array_init),* ];
        let val = ctx.new_data(vm, #tag as ::gluon::vm::types::VmTag, &fields)?;
        ctx.stack.push(val);
    }
}

fn create_pushable_bounds(generics: &Generics) -> Vec<TokenStream> {
    generics
        .params
        .iter()
        .filter_map(|param| match param {
            GenericParam::Type(param) => Some(&param.ident),
            _ => None,
        })
        .map(|ty| {
            quote! {
                #ty: ::gluon::vm::api::Pushable<'__vm>
            }
        })
        .collect()
}

fn get_info_from_fields(fields: &Fields) -> (Vec<Cow<Ident>>, Vec<&Type>) {
    // get all the fields if there are any
    let fields = match fields {
        Fields::Named(FieldsNamed { named, .. }) => named,
        Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => unnamed,
        Fields::Unit => return (Vec::new(), Vec::new()),
    };

    fields
        .iter()
        .enumerate()
        .map(|(idx, field)| {
            // if the fields belong to a struct we use the field name,
            // otherwise generate one from the index of the tuple element
            let ident = match &field.ident {
                Some(ident) => Cow::Borrowed(ident),
                None => Cow::Owned(Ident::new(&format!("_{}", idx), Span::call_site())),
            };

            (ident, &field.ty)
        })
        .unzip()
}
<|endoftext|>