<commit_before>//! The dump module contains code related for outputing/dumping data.
use std::fmt::Display;
use super::errors::*;

/// Enum which provides all possible output value formats supported by the dump module.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum OutputFormat {
    Hex,
    Decimal,
    Octal,
    Binary,
}

pub struct OutputSettings {
    start_address: u32,
    show_address: bool,
    group_size: usize,
    columns: usize,
    show_interpretation: bool,
    output_fmt: OutputFormat,
}

impl OutputSettings {
    fn new() -> OutputSettings {
        OutputSettings {
            start_address: 0,
            show_address: true,
            group_size: 1,
            columns: 8,
            show_interpretation: true,
            output_fmt: OutputFormat::Hex,
        }
    }

    pub fn start_address(mut self, address: u32) -> Self {
        self.start_address = address;
        self
    }

    pub fn show_address(mut self, show: bool) -> Self {
        self.show_address = show;
        self
    }

    pub fn group_size(mut self, size: usize) -> Self {
        self.group_size = size;
        self
    }

    pub fn columns(mut self, columns: usize) -> Self {
        self.columns = columns;
        self
    }

    pub fn show_interpretation(mut self, show: bool) -> Self {
        self.show_interpretation = show;
        self
    }

    pub fn format(mut self, fmt: OutputFormat) -> Self {
        self.output_fmt = fmt;
        self
    }
}

/// The `OutputLine` struct contains all  information needed to dump/output a single line of data.
#[derive(Debug)]
pub struct OutputLine<'a> {
    start_address: u32,
    show_address: bool,
    data: &'a [u8], // TODO NiCo: Add member for data format (output format of data)
    group_size: usize,
    columns: usize,
    show_interpretation: bool,
    output_fmt: OutputFormat,
}

impl<'a> OutputLine<'a> {
    fn new(data: &[u8]) -> OutputLine {
        OutputLine {
            start_address: 0,
            show_address: true,
            data: data,
            group_size: 1,
            columns: 8,
            show_interpretation: true,
            output_fmt: OutputFormat::Hex,
        }
    }

    pub fn format(self, fmt: OutputFormat) -> Self {
        OutputLine {
            start_address: self.start_address,
            show_address: self.show_interpretation,
            data: self.data,
            group_size: self.group_size,
            columns: self.columns,
            show_interpretation: self.show_interpretation,
            output_fmt: fmt,
        }
    }

    fn write_address(&self, f: &mut ::fmt::Formatter) -> Result<()> {
        write!(f, "{:08.X}: ", self.start_address).map_err(|e| e.into())
    }

    fn write_bytes(&self, f: &mut ::fmt::Formatter) -> Result<()> {
        let mut byte_count = 0;
        for b in self.data.iter() {
            byte_count += 1;
            let is_seperator_necessary = byte_count % self.group_size == 0;
            if is_seperator_necessary {
                self.write_formated_byte(f, b)?;
                write!(f, " ")?
            } else {
                self.write_formated_byte(f, b)?
            }
        }
        Ok(())
    }

    fn write_formated_byte(&self, f: &mut ::fmt::Formatter, byte: &u8) -> Result<()> {
        match self.output_fmt {
            OutputFormat::Hex => write!(f, "{:02.X}", byte).map_err(|e| e.into()),
            OutputFormat::Octal => write!(f, "{:02.o}", byte).map_err(|e| e.into()),
            OutputFormat::Decimal => write!(f, "{:02}", byte).map_err(|e| e.into()),
            OutputFormat::Binary => write!(f, "{:02b}", byte).map_err(|e| e.into()),
        }
    }

    fn write_interpretation(&self, f: &mut ::fmt::Formatter) -> Result<()> {
        write!(f, "   ");
        for b in self.data.iter() {
            match *b {
                character @ 20u8...126u8 => write!(f, "{}", character as char)?,
                _ => write!(f, "{}", ".")?,
            }
        }
        Ok(())
    }
}

impl<'a> ::fmt::Display for OutputLine<'a> {
    fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
        if self.show_address {
            self.write_address(f);
        }
        self.write_bytes(f);
        if self.show_interpretation {
            self.write_interpretation(f);
        }
        Ok(())
    }
}

mod test {
    use super::*;
    use std::fmt::Write;

    #[test]
    fn output_settings_can_be_constructed() {
        let output_settings = OutputSettings::new();
        assert!(true);
    }

    #[test]
    fn output_settings_builder() {
        let format = OutputFormat::Binary;
        let start_address = 0xFF00AABB;
        let group_size = 2;
        let show_address = false;
        let show_interpretation = false;

        let mut output_settings = OutputSettings::new()
            .format(format)
            .start_address(start_address)
            .group_size(group_size)
            .show_address(show_address)
            .show_interpretation(show_interpretation);

        assert_eq!(output_settings.output_fmt, format);
        assert_eq!(output_settings.start_address, start_address);
        assert_eq!(output_settings.group_size, group_size);
        assert_eq!(output_settings.show_address, show_address);
        assert_eq!(output_settings.show_interpretation, show_interpretation);
    }

    #[test]
    fn output_settings_start_address() {}

    #[test]
    fn outputline_can_be_constructed() {
        let data = [1, 2, 3];
        let output_line = OutputLine::new(&data);
        assert!(true);
    }

    #[test]
    fn default_output_format_for_a_single_line() {
        let data = [1, 2, 3, 4, 5, 6, 7, 8];
        let expected_output = "00000000: 01 02 03 04 05 06 07 08    ........";
        let output_line = OutputLine::new(&data);
        let mut buffer = String::new();
        let result = write!(&mut buffer, "{}", output_line);
        assert_eq!(Ok(()), result);
        assert_eq!(expected_output, buffer);
    }

    #[test]
    fn octal_output_format_on_a_single_line() {
        let data = [8, 9, 10, 11, 12, 13, 14, 15];
        let expected_output = "00000000: 10 11 12 13 14 15 16 17    ........";
        let output_line = OutputLine::new(&data).format(OutputFormat::Octal);
        let mut buffer = String::new();
        let result = write!(&mut buffer, "{}", output_line);
        assert_eq!(Ok(()), result);
        assert_eq!(expected_output, buffer);
    }

    #[test]
    fn binary_output_format_on_a_single_line() {
        let data = [65, 66, 67, 68, 126, 124, 60, 46];
        let expected_output = "00000000: 1000001 1000010 1000011 1000100 1111110 1111100 111100 101110    ABCD~|<.";
        let output_line = OutputLine::new(&data).format(OutputFormat::Binary);
        let mut buffer = String::new();
        let result = write!(&mut buffer, "{}", output_line);
        assert_eq!(Ok(()), result);
        assert_eq!(expected_output, buffer);
    }

    #[test]
    fn interpretation_for_default_settings() {
        let data = [65, 66, 67, 68, 126, 124, 60, 46];
        let expected_output = "00000000: 41 42 43 44 7E 7C 3C 2E    ABCD~|<.";
        let output_line = OutputLine::new(&data);
        let mut buffer = String::new();
        let result = write!(&mut buffer, "{}", output_line);
        assert_eq!(Ok(()), result);
        assert_eq!(expected_output, buffer);
    }
}
<commit_msg>Refactor OutputLine to use OutputSettings<commit_after>//! The dump module contains code related for outputing/dumping data.
use std::fmt::Display;
use super::errors::*;

/// Enum which provides all possible output value formats supported by the dump module.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum OutputFormat {
    Hex,
    Decimal,
    Octal,
    Binary,
}

#[derive(Debug)]
pub struct OutputSettings {
    start_address: u32,
    show_address: bool,
    group_size: usize,
    columns: usize,
    show_interpretation: bool,
    output_fmt: OutputFormat,
}

impl OutputSettings {
    fn new() -> OutputSettings {
        OutputSettings {
            start_address: 0,
            show_address: true,
            group_size: 1,
            columns: 8,
            show_interpretation: true,
            output_fmt: OutputFormat::Hex,
        }
    }

    pub fn start_address(mut self, address: u32) -> Self {
        self.start_address = address;
        self
    }

    pub fn show_address(mut self, show: bool) -> Self {
        self.show_address = show;
        self
    }

    pub fn group_size(mut self, size: usize) -> Self {
        self.group_size = size;
        self
    }

    pub fn columns(mut self, columns: usize) -> Self {
        self.columns = columns;
        self
    }

    pub fn show_interpretation(mut self, show: bool) -> Self {
        self.show_interpretation = show;
        self
    }

    pub fn format(mut self, fmt: OutputFormat) -> Self {
        self.output_fmt = fmt;
        self
    }
}

/// The `OutputLine` struct contains all  information needed to dump/output a single line of data.
#[derive(Debug)]
pub struct OutputLine<'a> {
    output_settings: OutputSettings,
    data: &'a [u8], // TODO NiCo: Add member for data format (output format of data)
}

impl<'a> OutputLine<'a> {
    fn new(data: &[u8]) -> OutputLine {
        OutputLine {
            output_settings: OutputSettings::new(),
            data: data,
        }
    }

    pub fn format(self, settings: OutputSettings) -> Self {
        OutputLine {
            output_settings: settings,
            data: self.data,
        }
    }

    fn write_address(&self, f: &mut ::fmt::Formatter) -> Result<()> {
        write!(f, "{:08.X}: ", self.output_settings.start_address).map_err(|e| e.into())
    }

    fn write_bytes(&self, f: &mut ::fmt::Formatter) -> Result<()> {
        let mut byte_count = 0;
        for b in self.data.iter() {
            byte_count += 1;
            let is_seperator_necessary = byte_count % self.output_settings.group_size == 0;
            if is_seperator_necessary {
                self.write_formated_byte(f, b)?;
                write!(f, " ")?
            } else {
                self.write_formated_byte(f, b)?
            }
        }
        Ok(())
    }

    fn write_formated_byte(&self, f: &mut ::fmt::Formatter, byte: &u8) -> Result<()> {
        match self.output_settings.output_fmt {
            OutputFormat::Hex => write!(f, "{:02.X}", byte).map_err(|e| e.into()),
            OutputFormat::Octal => write!(f, "{:02.o}", byte).map_err(|e| e.into()),
            OutputFormat::Decimal => write!(f, "{:02}", byte).map_err(|e| e.into()),
            OutputFormat::Binary => write!(f, "{:02b}", byte).map_err(|e| e.into()),
        }
    }

    fn write_interpretation(&self, f: &mut ::fmt::Formatter) -> Result<()> {
        write!(f, "   ");
        for b in self.data.iter() {
            match *b {
                character @ 20u8...126u8 => write!(f, "{}", character as char)?,
                _ => write!(f, "{}", ".")?,
            }
        }
        Ok(())
    }
}

impl<'a> ::fmt::Display for OutputLine<'a> {
    fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
        if self.output_settings.show_address {
            self.write_address(f);
        }
        self.write_bytes(f);
        if self.output_settings.show_interpretation {
            self.write_interpretation(f);
        }
        Ok(())
    }
}

mod test {
    use super::*;
    use std::fmt::Write;

    #[test]
    fn output_settings_can_be_constructed() {
        let output_settings = OutputSettings::new();
        assert!(true);
    }

    #[test]
    fn output_settings_builder() {
        let format = OutputFormat::Binary;
        let start_address = 0xFF00AABB;
        let group_size = 2;
        let show_address = false;
        let show_interpretation = false;

        let mut output_settings = OutputSettings::new()
            .format(format)
            .start_address(start_address)
            .group_size(group_size)
            .show_address(show_address)
            .show_interpretation(show_interpretation);

        assert_eq!(output_settings.output_fmt, format);
        assert_eq!(output_settings.start_address, start_address);
        assert_eq!(output_settings.group_size, group_size);
        assert_eq!(output_settings.show_address, show_address);
        assert_eq!(output_settings.show_interpretation, show_interpretation);
    }

    #[test]
    fn output_settings_start_address() {}

    #[test]
    fn outputline_can_be_constructed() {
        let data = [1, 2, 3];
        let output_line = OutputLine::new(&data);
        assert!(true);
    }

    #[test]
    fn default_output_format_for_a_single_line() {
        let data = [1, 2, 3, 4, 5, 6, 7, 8];
        let expected_output = "00000000: 01 02 03 04 05 06 07 08    ........";
        let output_line = OutputLine::new(&data);
        let mut buffer = String::new();
        let result = write!(&mut buffer, "{}", output_line);
        assert_eq!(Ok(()), result);
        assert_eq!(expected_output, buffer);
    }

    #[test]
    fn octal_output_format_on_a_single_line() {
        let data = [8, 9, 10, 11, 12, 13, 14, 15];
        let expected_output = "00000000: 10 11 12 13 14 15 16 17    ........";
        let output_settings = OutputSettings::new().format(OutputFormat::Octal);
        let output_line = OutputLine::new(&data).format(output_settings);
        let mut buffer = String::new();
        let result = write!(&mut buffer, "{}", output_line);
        assert_eq!(Ok(()), result);
        assert_eq!(expected_output, buffer);
    }

    #[test]
    fn binary_output_format_on_a_single_line() {
        let data = [65, 66, 67, 68, 126, 124, 60, 46];
        let expected_output = "00000000: 1000001 1000010 1000011 1000100 1111110 1111100 111100 101110    ABCD~|<.";
        let output_settings = OutputSettings::new().format(OutputFormat::Binary);
        let output_line = OutputLine::new(&data).format(output_settings);
        let mut buffer = String::new();
        let result = write!(&mut buffer, "{}", output_line);
        assert_eq!(Ok(()), result);
        assert_eq!(expected_output, buffer);
    }

    #[test]
    fn interpretation_for_default_settings() {
        let data = [65, 66, 67, 68, 126, 124, 60, 46];
        let expected_output = "00000000: 41 42 43 44 7E 7C 3C 2E    ABCD~|<.";
        let output_settings = OutputSettings::new();
        let output_line = OutputLine::new(&data).format(output_settings);
        let mut buffer = String::new();
        let result = write!(&mut buffer, "{}", output_line);
        assert_eq!(Ok(()), result);
        assert_eq!(expected_output, buffer);
    }
}
<|endoftext|>


<commit_before>extern crate liquid;
extern crate serde_yaml;

#[macro_use]
extern crate difference;

use std::f32;

#[test]
pub fn serialize_num() {
    let actual = liquid::Value::scalar(1f32);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n1", "", 0);

    let actual = liquid::Value::scalar(-100f32);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n-100", "", 0);

    let actual = liquid::Value::scalar(3.14e_10f32);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n31399999488", "", 0);

    let actual = liquid::Value::scalar(f32::NAN);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\nNaN", "", 0);

    let actual = liquid::Value::scalar(f32::INFINITY);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\ninf", "", 0);
}

#[test]
pub fn deserialize_num() {
    let actual: liquid::Value = serde_yaml::from_str("---\n1").unwrap();
    assert_eq!(actual, liquid::Value::scalar(1f32));

    let actual: liquid::Value = serde_yaml::from_str("---\n-100").unwrap();
    assert_eq!(actual, liquid::Value::scalar(-100f32));

    let actual: liquid::Value = serde_yaml::from_str("---\n31399999488").unwrap();
    assert_eq!(actual, liquid::Value::scalar(3.14e_10f32));

    // Skipping NaN since equality fails

    let actual: liquid::Value = serde_yaml::from_str("---\ninf").unwrap();
    assert_eq!(actual, liquid::Value::scalar(f32::INFINITY));
}

#[test]
pub fn serialize_bool() {
    let actual = liquid::Value::scalar(true);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\ntrue", "", 0);

    let actual = liquid::Value::scalar(false);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\nfalse", "", 0);
}

#[test]
pub fn deserialize_bool() {
    let actual: liquid::Value = serde_yaml::from_str("---\ntrue").unwrap();
    assert_eq!(actual, liquid::Value::scalar(true));

    let actual: liquid::Value = serde_yaml::from_str("---\nfalse").unwrap();
    assert_eq!(actual, liquid::Value::scalar(false));
}

#[test]
pub fn serialize_nil() {
    let actual = liquid::Value::Nil;
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n~", "", 0);
}

#[test]
pub fn deserialize_nil() {
    let actual: liquid::Value = serde_yaml::from_str("---\n~").unwrap();
    assert_eq!(actual, liquid::Value::Nil);

    let actual: liquid::Value = serde_yaml::from_str("---\n- ").unwrap();
    assert_eq!(actual, liquid::Value::Array(vec![liquid::Value::Nil]));
}

#[test]
pub fn serialize_str() {
    let actual = liquid::Value::scalar("Hello");
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\nHello", "", 0);

    let actual = liquid::Value::scalar("10");
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n\"10\"", "", 0);

    let actual = liquid::Value::scalar("false");
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n\"false\"", "", 0);
}

#[test]
pub fn deserialize_str() {
    let actual: liquid::Value = serde_yaml::from_str("---\nHello").unwrap();
    assert_eq!(actual, liquid::Value::scalar("Hello"));

    let actual: liquid::Value = serde_yaml::from_str("\"10\"\n").unwrap();
    assert_eq!(actual, liquid::Value::scalar("10"));

    let actual: liquid::Value = serde_yaml::from_str("---\n\"false\"").unwrap();
    assert_eq!(actual, liquid::Value::scalar("false"));
}

#[test]
pub fn serialize_array() {
    let actual = vec![
        liquid::Value::scalar(1f32),
        liquid::Value::scalar(true),
        liquid::Value::scalar("true"),
    ];
    let actual = liquid::Value::Array(actual);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n- 1\n- true\n- \"true\"", "", 0);
}

#[test]
pub fn deserialize_array() {
    let actual: liquid::Value = serde_yaml::from_str("---\n- 1\n- true\n- \"true\"").unwrap();
    let expected = vec![
        liquid::Value::scalar(1f32),
        liquid::Value::scalar(true),
        liquid::Value::scalar("true"),
    ];
    let expected = liquid::Value::Array(expected);
    assert_eq!(actual, expected);
}

#[test]
pub fn serialize_object() {
    // Skipping due to HashMap ordering issues
}

#[test]
pub fn deserialize_object() {
    let actual: liquid::Value =
        serde_yaml::from_str("---\nNum: 1\nBool: true\nStr: \"true\"").unwrap();
    let expected: liquid::Object = [
        ("Num".to_owned(), liquid::Value::scalar(1f32)),
        ("Bool".to_owned(), liquid::Value::scalar(true)),
        ("Str".to_owned(), liquid::Value::scalar("true")),
    ].iter()
        .cloned()
        .collect();
    let expected = liquid::Value::Object(expected);
    assert_eq!(actual, expected);
}
<commit_msg>chore: Update tests for new deps<commit_after>extern crate liquid;
extern crate serde_yaml;

#[macro_use]
extern crate difference;

use std::f32;

#[test]
pub fn serialize_num() {
    let actual = liquid::Value::scalar(1f32);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n1.0", "", 0);

    let actual = liquid::Value::scalar(-100f32);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n-100.0", "", 0);

    let actual = liquid::Value::scalar(3.14e_10f32);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n31399999488.0", "", 0);

    let actual = liquid::Value::scalar(f32::NAN);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n.nan", "", 0);

    let actual = liquid::Value::scalar(f32::INFINITY);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n.inf", "", 0);
}

#[test]
pub fn deserialize_num() {
    let actual: liquid::Value = serde_yaml::from_str("---\n1").unwrap();
    assert_eq!(actual, liquid::Value::scalar(1f32));

    let actual: liquid::Value = serde_yaml::from_str("---\n-100").unwrap();
    assert_eq!(actual, liquid::Value::scalar(-100f32));

    let actual: liquid::Value = serde_yaml::from_str("---\n31399999488").unwrap();
    assert_eq!(actual, liquid::Value::scalar(3.14e_10f32));

    // Skipping NaN since equality fails

    let actual: liquid::Value = serde_yaml::from_str("---\ninf").unwrap();
    assert_eq!(actual, liquid::Value::scalar(f32::INFINITY));
}

#[test]
pub fn serialize_bool() {
    let actual = liquid::Value::scalar(true);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\ntrue", "", 0);

    let actual = liquid::Value::scalar(false);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\nfalse", "", 0);
}

#[test]
pub fn deserialize_bool() {
    let actual: liquid::Value = serde_yaml::from_str("---\ntrue").unwrap();
    assert_eq!(actual, liquid::Value::scalar(true));

    let actual: liquid::Value = serde_yaml::from_str("---\nfalse").unwrap();
    assert_eq!(actual, liquid::Value::scalar(false));
}

#[test]
pub fn serialize_nil() {
    let actual = liquid::Value::Nil;
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n~", "", 0);
}

#[test]
pub fn deserialize_nil() {
    let actual: liquid::Value = serde_yaml::from_str("---\n~").unwrap();
    assert_eq!(actual, liquid::Value::Nil);

    let actual: liquid::Value = serde_yaml::from_str("---\n- ").unwrap();
    assert_eq!(actual, liquid::Value::Array(vec![liquid::Value::Nil]));
}

#[test]
pub fn serialize_str() {
    let actual = liquid::Value::scalar("Hello");
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\nHello", "", 0);

    let actual = liquid::Value::scalar("10");
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n\"10\"", "", 0);

    let actual = liquid::Value::scalar("false");
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n\"false\"", "", 0);
}

#[test]
pub fn deserialize_str() {
    let actual: liquid::Value = serde_yaml::from_str("---\nHello").unwrap();
    assert_eq!(actual, liquid::Value::scalar("Hello"));

    let actual: liquid::Value = serde_yaml::from_str("\"10\"\n").unwrap();
    assert_eq!(actual, liquid::Value::scalar("10"));

    let actual: liquid::Value = serde_yaml::from_str("---\n\"false\"").unwrap();
    assert_eq!(actual, liquid::Value::scalar("false"));
}

#[test]
pub fn serialize_array() {
    let actual = vec![
        liquid::Value::scalar(1f32),
        liquid::Value::scalar(true),
        liquid::Value::scalar("true"),
    ];
    let actual = liquid::Value::Array(actual);
    let actual = serde_yaml::to_string(&actual).unwrap();
    assert_diff!(&actual, "---\n- 1.0\n- true\n- \"true\"", "", 0);
}

#[test]
pub fn deserialize_array() {
    let actual: liquid::Value = serde_yaml::from_str("---\n- 1\n- true\n- \"true\"").unwrap();
    let expected = vec![
        liquid::Value::scalar(1f32),
        liquid::Value::scalar(true),
        liquid::Value::scalar("true"),
    ];
    let expected = liquid::Value::Array(expected);
    assert_eq!(actual, expected);
}

#[test]
pub fn serialize_object() {
    // Skipping due to HashMap ordering issues
}

#[test]
pub fn deserialize_object() {
    let actual: liquid::Value =
        serde_yaml::from_str("---\nNum: 1\nBool: true\nStr: \"true\"").unwrap();
    let expected: liquid::Object = [
        ("Num".to_owned(), liquid::Value::scalar(1f32)),
        ("Bool".to_owned(), liquid::Value::scalar(true)),
        ("Str".to_owned(), liquid::Value::scalar("true")),
    ].iter()
        .cloned()
        .collect();
    let expected = liquid::Value::Object(expected);
    assert_eq!(actual, expected);
}
<|endoftext|>


<commit_before>use std::{collections::BTreeMap, fmt};

use ruma_common::OwnedEventId;
use serde::{
    de::{Deserializer, MapAccess, Visitor},
    ser::{SerializeMap, Serializer},
    Deserialize, Serialize,
};

#[derive(Deserialize, Serialize)]
struct WrappedError {
    #[serde(skip_serializing_if = "Option::is_none")]
    error: Option<String>,
}

pub fn serialize<S>(
    response: &BTreeMap<OwnedEventId, Result<(), String>>,
    serializer: S,
) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let mut map = serializer.serialize_map(Some(response.len()))?;
    for (key, value) in response {
        let wrapped_error = WrappedError {
            error: match value {
                Ok(_) => None,
                Err(error) => Some(error.clone()),
            },
        };
        map.serialize_entry(&key, &wrapped_error)?;
    }
    map.end()
}

#[allow(clippy::type_complexity)]
pub fn deserialize<'de, D>(
    deserializer: D,
) -> Result<BTreeMap<OwnedEventId, Result<(), String>>, D::Error>
where
    D: Deserializer<'de>,
{
    struct PduProcessResponseVisitor;

    impl<'de> Visitor<'de> for PduProcessResponseVisitor {
        type Value = BTreeMap<OwnedEventId, Result<(), String>>;

        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
            formatter.write_str("A map of EventIds to a map of optional errors")
        }

        fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
        where
            M: MapAccess<'de>,
        {
            let mut map = BTreeMap::new();

            while let Some((key, value)) = access.next_entry::<OwnedEventId, WrappedError>()? {
                let v = match value.error {
                    None => Ok(()),
                    Some(error) => Err(error),
                };
                map.insert(key, v);
            }
            Ok(map)
        }
    }

    deserializer.deserialize_map(PduProcessResponseVisitor)
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;

    use ruma_common::{event_id, OwnedEventId};
    use serde_json::{json, value::Serializer as JsonSerializer};

    use super::{deserialize, serialize};

    #[test]
    fn serialize_error() {
        let mut response: BTreeMap<OwnedEventId, Result<(), String>> = BTreeMap::new();
        response.insert(
            event_id!("$someevent:matrix.org").to_owned(),
            Err("Some processing error.".into()),
        );

        let serialized = serialize(&response, JsonSerializer).unwrap();
        let json = json!({
            "$someevent:matrix.org": { "error": "Some processing error." }
        });
        assert_eq!(serialized, json);
    }

    #[test]
    fn serialize_ok() {
        let mut response: BTreeMap<OwnedEventId, Result<(), String>> = BTreeMap::new();
        response.insert(event_id!("$someevent:matrix.org").to_owned(), Ok(()));

        let serialized = serialize(&response, serde_json::value::Serializer).unwrap();
        let json = json!({
            "$someevent:matrix.org": {}
        });
        assert_eq!(serialized, json);
    }

    #[test]
    fn deserialize_error() {
        let json = json!({
            "$someevent:matrix.org": { "error": "Some processing error." }
        });

        let response = deserialize(json).unwrap();
        let event_id = event_id!("$someevent:matrix.org");

        let event_response = response.get(event_id).unwrap().clone().unwrap_err();
        assert_eq!(event_response, "Some processing error.");
    }

    #[test]
    fn deserialize_null_error_is_ok() {
        let json = json!({
            "$someevent:matrix.org": { "error": null }
        });

        let response = deserialize(json).unwrap();
        let event_id = event_id!("$someevent:matrix.org");

        assert!(response.get(event_id).unwrap().is_ok());
    }

    #[test]
    fn desieralize_empty_error_is_err() {
        let json = json!({
            "$someevent:matrix.org": { "error": "" }
        });

        let response = deserialize(json).unwrap();
        let event_id = event_id!("$someevent:matrix.org");

        let event_response = response.get(event_id).unwrap().clone().unwrap_err();
        assert_eq!(event_response, "");
    }

    #[test]
    fn deserialize_ok() {
        let json = json!({
            "$someevent:matrix.org": {}
        });
        let response = deserialize(json).unwrap();
        assert!(response.get(event_id!("$someevent:matrix.org")).unwrap().is_ok());
    }
}
<commit_msg>federation-api: Make some test assertions more helpful<commit_after>use std::{collections::BTreeMap, fmt};

use ruma_common::OwnedEventId;
use serde::{
    de::{Deserializer, MapAccess, Visitor},
    ser::{SerializeMap, Serializer},
    Deserialize, Serialize,
};

#[derive(Deserialize, Serialize)]
struct WrappedError {
    #[serde(skip_serializing_if = "Option::is_none")]
    error: Option<String>,
}

pub fn serialize<S>(
    response: &BTreeMap<OwnedEventId, Result<(), String>>,
    serializer: S,
) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let mut map = serializer.serialize_map(Some(response.len()))?;
    for (key, value) in response {
        let wrapped_error = WrappedError {
            error: match value {
                Ok(_) => None,
                Err(error) => Some(error.clone()),
            },
        };
        map.serialize_entry(&key, &wrapped_error)?;
    }
    map.end()
}

#[allow(clippy::type_complexity)]
pub fn deserialize<'de, D>(
    deserializer: D,
) -> Result<BTreeMap<OwnedEventId, Result<(), String>>, D::Error>
where
    D: Deserializer<'de>,
{
    struct PduProcessResponseVisitor;

    impl<'de> Visitor<'de> for PduProcessResponseVisitor {
        type Value = BTreeMap<OwnedEventId, Result<(), String>>;

        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
            formatter.write_str("A map of EventIds to a map of optional errors")
        }

        fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
        where
            M: MapAccess<'de>,
        {
            let mut map = BTreeMap::new();

            while let Some((key, value)) = access.next_entry::<OwnedEventId, WrappedError>()? {
                let v = match value.error {
                    None => Ok(()),
                    Some(error) => Err(error),
                };
                map.insert(key, v);
            }
            Ok(map)
        }
    }

    deserializer.deserialize_map(PduProcessResponseVisitor)
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;

    use ruma_common::{event_id, OwnedEventId};
    use serde_json::{json, value::Serializer as JsonSerializer};

    use super::{deserialize, serialize};

    #[test]
    fn serialize_error() {
        let mut response: BTreeMap<OwnedEventId, Result<(), String>> = BTreeMap::new();
        response.insert(
            event_id!("$someevent:matrix.org").to_owned(),
            Err("Some processing error.".into()),
        );

        let serialized = serialize(&response, JsonSerializer).unwrap();
        let json = json!({
            "$someevent:matrix.org": { "error": "Some processing error." }
        });
        assert_eq!(serialized, json);
    }

    #[test]
    fn serialize_ok() {
        let mut response: BTreeMap<OwnedEventId, Result<(), String>> = BTreeMap::new();
        response.insert(event_id!("$someevent:matrix.org").to_owned(), Ok(()));

        let serialized = serialize(&response, serde_json::value::Serializer).unwrap();
        let json = json!({
            "$someevent:matrix.org": {}
        });
        assert_eq!(serialized, json);
    }

    #[test]
    fn deserialize_error() {
        let json = json!({
            "$someevent:matrix.org": { "error": "Some processing error." }
        });

        let response = deserialize(json).unwrap();
        let event_id = event_id!("$someevent:matrix.org");

        let event_response = response.get(event_id).unwrap().clone().unwrap_err();
        assert_eq!(event_response, "Some processing error.");
    }

    #[test]
    fn deserialize_null_error_is_ok() {
        let json = json!({
            "$someevent:matrix.org": { "error": null }
        });

        let response = deserialize(json).unwrap();
        let event_id = event_id!("$someevent:matrix.org");

        response.get(event_id).unwrap().as_ref().unwrap();
    }

    #[test]
    fn deserialize_empty_error_is_err() {
        let json = json!({
            "$someevent:matrix.org": { "error": "" }
        });

        let response = deserialize(json).unwrap();
        let event_id = event_id!("$someevent:matrix.org");

        let event_response = response.get(event_id).unwrap().clone().unwrap_err();
        assert_eq!(event_response, "");
    }

    #[test]
    fn deserialize_ok() {
        let json = json!({
            "$someevent:matrix.org": {}
        });
        let response = deserialize(json).unwrap();
        response.get(event_id!("$someevent:matrix.org")).unwrap().as_ref().unwrap();
    }
}
<|endoftext|>


<commit_before>use rustc_errors::DiagnosticBuilder;
use rustc_middle::lint::LintDiagnosticBuilder;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::CONST_ITEM_MUTATION;
use rustc_span::def_id::DefId;

use crate::transform::{MirPass, MirSource};

pub struct CheckConstItemMutation;

impl<'tcx> MirPass<'tcx> for CheckConstItemMutation {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let mut checker = ConstMutationChecker { body, tcx, target_local: None };
        checker.visit_body(&body);
    }
}

struct ConstMutationChecker<'a, 'tcx> {
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    target_local: Option<Local>,
}

impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> {
    fn is_const_item(&self, local: Local) -> Option<DefId> {
        if let Some(box LocalInfo::ConstRef { def_id }) = self.body.local_decls[local].local_info {
            Some(def_id)
        } else {
            None
        }
    }

    fn is_const_item_without_destructor(&self, local: Local) -> Option<DefId> {
        let def_id = self.is_const_item(local)?;

        // We avoid linting mutation of a const item if the const's type has a
        // Drop impl. The Drop logic observes the mutation which was performed.
        //
        //     struct Log { msg: &'static str }
        //     const LOG: Log = Log { msg: "" };
        //     impl Drop for Log {
        //         fn drop(&mut self) { println!("{}", self.msg); }
        //     }
        //
        //     LOG.msg = "wow";  // prints "wow"
        //
        match self.tcx.adt_def(def_id).destructor(self.tcx) {
            Some(_) => None,
            None => Some(def_id),
        }
    }

    fn lint_const_item_usage(
        &self,
        const_item: DefId,
        location: Location,
        decorate: impl for<'b> FnOnce(LintDiagnosticBuilder<'b>) -> DiagnosticBuilder<'b>,
    ) {
        let source_info = self.body.source_info(location);
        let lint_root = self.body.source_scopes[source_info.scope]
            .local_data
            .as_ref()
            .assert_crate_local()
            .lint_root;

        self.tcx.struct_span_lint_hir(CONST_ITEM_MUTATION, lint_root, source_info.span, |lint| {
            decorate(lint)
                .span_note(self.tcx.def_span(const_item), "`const` item defined here")
                .emit()
        });
    }
}

impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> {
    fn visit_statement(&mut self, stmt: &Statement<'tcx>, loc: Location) {
        if let StatementKind::Assign(box (lhs, _)) = &stmt.kind {
            // Check for assignment to fields of a constant
            // Assigning directly to a constant (e.g. `FOO = true;`) is a hard error,
            // so emitting a lint would be redundant.
            if !lhs.projection.is_empty() {
                if let Some(def_id) = self.is_const_item_without_destructor(lhs.local) {
                    // Don't lint on writes through a pointer
                    // (e.g. `unsafe { *FOO = 0; *BAR.field = 1; }`)
                    if !matches!(lhs.projection.last(), Some(PlaceElem::Deref)) {
                        self.lint_const_item_usage(def_id, loc, |lint| {
                            let mut lint = lint.build("attempting to modify a `const` item");
                            lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified");
                            lint
                        })
                    }
                }
            }
            // We are looking for MIR of the form:
            //
            // ```
            // _1 = const FOO;
            // _2 = &mut _1;
            // method_call(_2, ..)
            // ```
            //
            // Record our current LHS, so that we can detect this
            // pattern in `visit_rvalue`
            self.target_local = lhs.as_local();
        }
        self.super_statement(stmt, loc);
        self.target_local = None;
    }
    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, loc: Location) {
        if let Rvalue::Ref(_, BorrowKind::Mut { .. }, place) = rvalue {
            let local = place.local;
            if let Some(def_id) = self.is_const_item_without_destructor(local) {
                // If this Rvalue is being used as the right-hand side of a
                // `StatementKind::Assign`, see if it ends up getting used as
                // the `self` parameter of a method call (as the terminator of our current
                // BasicBlock). If so, we emit a more specific lint.
                let method_did = self.target_local.and_then(|target_local| {
                    crate::util::find_self_call(self.tcx, &self.body, target_local, loc.block)
                });
                let lint_loc =
                    if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc };
                self.lint_const_item_usage(def_id, lint_loc, |lint| {
                    let mut lint = lint.build("taking a mutable reference to a `const` item");
                    lint
                        .note("each usage of a `const` item creates a new temporary")
                        .note("the mutable reference will refer to this temporary, not the original `const` item");

                    if let Some((method_did, _substs)) = method_did {
                        lint.span_note(self.tcx.def_span(method_did), "mutable reference created due to call to this method");
                    }

                    lint
                });
            }
        }
        self.super_rvalue(rvalue, loc);
    }
}
<commit_msg>Simplify defid destructor check<commit_after>use rustc_errors::DiagnosticBuilder;
use rustc_middle::lint::LintDiagnosticBuilder;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::CONST_ITEM_MUTATION;
use rustc_span::def_id::DefId;

use crate::transform::{MirPass, MirSource};

pub struct CheckConstItemMutation;

impl<'tcx> MirPass<'tcx> for CheckConstItemMutation {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let mut checker = ConstMutationChecker { body, tcx, target_local: None };
        checker.visit_body(&body);
    }
}

struct ConstMutationChecker<'a, 'tcx> {
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    target_local: Option<Local>,
}

impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> {
    fn is_const_item(&self, local: Local) -> Option<DefId> {
        if let Some(box LocalInfo::ConstRef { def_id }) = self.body.local_decls[local].local_info {
            Some(def_id)
        } else {
            None
        }
    }

    fn is_const_item_without_destructor(&self, local: Local) -> Option<DefId> {
        let def_id = self.is_const_item(local)?;

        // We avoid linting mutation of a const item if the const's type has a
        // Drop impl. The Drop logic observes the mutation which was performed.
        //
        //     struct Log { msg: &'static str }
        //     const LOG: Log = Log { msg: "" };
        //     impl Drop for Log {
        //         fn drop(&mut self) { println!("{}", self.msg); }
        //     }
        //
        //     LOG.msg = "wow";  // prints "wow"
        //
        match self.tcx.adt_destructor(def_id) {
            Some(_) => None,
            None => Some(def_id),
        }
    }

    fn lint_const_item_usage(
        &self,
        const_item: DefId,
        location: Location,
        decorate: impl for<'b> FnOnce(LintDiagnosticBuilder<'b>) -> DiagnosticBuilder<'b>,
    ) {
        let source_info = self.body.source_info(location);
        let lint_root = self.body.source_scopes[source_info.scope]
            .local_data
            .as_ref()
            .assert_crate_local()
            .lint_root;

        self.tcx.struct_span_lint_hir(CONST_ITEM_MUTATION, lint_root, source_info.span, |lint| {
            decorate(lint)
                .span_note(self.tcx.def_span(const_item), "`const` item defined here")
                .emit()
        });
    }
}

impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> {
    fn visit_statement(&mut self, stmt: &Statement<'tcx>, loc: Location) {
        if let StatementKind::Assign(box (lhs, _)) = &stmt.kind {
            // Check for assignment to fields of a constant
            // Assigning directly to a constant (e.g. `FOO = true;`) is a hard error,
            // so emitting a lint would be redundant.
            if !lhs.projection.is_empty() {
                if let Some(def_id) = self.is_const_item_without_destructor(lhs.local) {
                    // Don't lint on writes through a pointer
                    // (e.g. `unsafe { *FOO = 0; *BAR.field = 1; }`)
                    if !matches!(lhs.projection.last(), Some(PlaceElem::Deref)) {
                        self.lint_const_item_usage(def_id, loc, |lint| {
                            let mut lint = lint.build("attempting to modify a `const` item");
                            lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified");
                            lint
                        })
                    }
                }
            }
            // We are looking for MIR of the form:
            //
            // ```
            // _1 = const FOO;
            // _2 = &mut _1;
            // method_call(_2, ..)
            // ```
            //
            // Record our current LHS, so that we can detect this
            // pattern in `visit_rvalue`
            self.target_local = lhs.as_local();
        }
        self.super_statement(stmt, loc);
        self.target_local = None;
    }
    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, loc: Location) {
        if let Rvalue::Ref(_, BorrowKind::Mut { .. }, place) = rvalue {
            let local = place.local;
            if let Some(def_id) = self.is_const_item_without_destructor(local) {
                // If this Rvalue is being used as the right-hand side of a
                // `StatementKind::Assign`, see if it ends up getting used as
                // the `self` parameter of a method call (as the terminator of our current
                // BasicBlock). If so, we emit a more specific lint.
                let method_did = self.target_local.and_then(|target_local| {
                    crate::util::find_self_call(self.tcx, &self.body, target_local, loc.block)
                });
                let lint_loc =
                    if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc };
                self.lint_const_item_usage(def_id, lint_loc, |lint| {
                    let mut lint = lint.build("taking a mutable reference to a `const` item");
                    lint
                        .note("each usage of a `const` item creates a new temporary")
                        .note("the mutable reference will refer to this temporary, not the original `const` item");

                    if let Some((method_did, _substs)) = method_did {
                        lint.span_note(self.tcx.def_span(method_did), "mutable reference created due to call to this method");
                    }

                    lint
                });
            }
        }
        self.super_rvalue(rvalue, loc);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate neon;
#[macro_use]
extern crate lazy_static;
extern crate fall_tree;
extern crate fall_gen;
extern crate lang_fall;

extern crate neon_serde;
#[macro_use]
extern crate serde_derive;
extern crate serde;

use serde::{Serialize, Deserialize};

use std::sync::{Arc, Mutex};

use neon::vm::{Call, VmResult, JsResult};
use neon::scope::{Scope, RootScope};
use neon::mem::Handle;
use neon::js::{JsString, JsInteger, JsNull, JsValue, JsFunction};
use neon::task::Task;

use lang_fall::{editor_api, FileWithAnalysis, Analysis};
use fall_tree::{TextRange, File};
use fall_gen::TestRenderer;

lazy_static! {
    static ref FILE: Mutex<Option<Arc<FileWithAnalysis>>> = Mutex::new(None);
}

fn file_create(call: Call) -> JsResult<JsNull> {
    let scope = call.scope;
    let text = call.arguments.require(scope, 0)?.check::<JsString>()?;
    let text = text.value();
    *FILE.lock().unwrap() = Some(Arc::new(editor_api::analyse(text)));
    Ok(JsNull::new())
}

#[derive(Serialize)]
struct PerformanceCounters {
    lexing_time: u32,
    parsing_time: u32,
    reparsed_region: TextRange,
}

fn performance_counters(file: &File) -> PerformanceCounters {
    let stats = file.stats();
    PerformanceCounters {
        lexing_time: stats.lexing_time.subsec_nanos(),
        parsing_time: stats.parsing_time.subsec_nanos(),
        reparsed_region: stats.reparsed_region,
    }
}

struct RenderTask(Arc<FileWithAnalysis>, usize);
impl Task for RenderTask {
    type Output = String;
    type Error = ();
    type JsEvent = JsString;

    fn perform(&self) -> Result<String, ()> {
        lazy_static! {
            static ref TEST_RENDERER: Mutex<TestRenderer> = Mutex::new(TestRenderer);
        }

        let mut renderer = TEST_RENDERER.lock().unwrap();
        let tree = renderer.render_one(&self.0.file(), self.1);
        Ok(tree)
    }

    fn complete<'a, T: Scope<'a>>(self, scope: &'a mut T, result: Result<String, ()>) -> JsResult<JsString> {
        Ok(JsString::new(scope, &result.unwrap()).unwrap())
    }
}

fn parse_test(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let test = call.arguments.require(scope, 0)?.check::<JsInteger>()?.value() as usize;
    let callback = call.arguments.require(scope, 1)?.check::<JsFunction>()?;
    let file = FILE.lock().unwrap();

    if let Some(ref file) = *file {
        RenderTask(file.clone(), test).schedule(callback);
    }
    Ok(JsNull::new().upcast())
}

fn a_fn<'a, S, F>(call: &mut Call<'a>, f: F) -> JsResult<'a, JsValue>
    where S: Serialize,
          F: Fn(&mut Call<'a>, &Analysis) -> VmResult<S> {
    let file = FILE.lock().unwrap();
    let file: &FileWithAnalysis = match *file {
        None => return Ok(JsNull::new().upcast()),
        Some(ref file) => &*file,
    };
    file.analyse(|a| {
        let result = f(call, a)?;
        Ok(to_value(&result, call.scope)?)
    })
}

fn a_fn0<S: Serialize, F: Fn(&Analysis) -> S>(mut call: Call, f: F) -> JsResult<JsValue> {
    a_fn(&mut call, |_, file| Ok(f(file)))
}

fn file_fn<'a, S, F>(call: &mut Call<'a>, f: F) -> JsResult<'a, JsValue>
    where S: Serialize,
          F: Fn(&mut Call<'a>, &File) -> VmResult<S> {
    let file = FILE.lock().unwrap();
    let file = match *file {
        None => return Ok(JsNull::new().upcast()),
        Some(ref file) => file.file(),
    };
    let result = f(call, file)?;
    Ok(to_value(&result, call.scope)?)
}

fn file_fn0<S: Serialize, F: Fn(&File) -> S>(mut call: Call, f: F) -> JsResult<JsValue> {
    file_fn(&mut call, |_, file| Ok(f(file)))
}

fn file_fn1<'c, S: Serialize, D: Deserialize<'c>>(
    mut call: Call<'c>,
    f: fn(&File, D) -> S
) -> JsResult<'c, JsValue> {
    file_fn(&mut call, |call, file| {
        let arg: D = from_handle(call.arguments.require(call.scope, 0)?, call.scope)?;
        Ok(f(file, arg))
    })
}

fn file_fn2<'c, S: Serialize, D1: Deserialize<'c>, D2: Deserialize<'c>>(
    mut call: Call<'c>,
    f: fn(&File, D1, D2) -> S
) -> JsResult<'c, JsValue> {
    file_fn(&mut call, |call, file| {
        let arg1: D1 = from_handle(call.arguments.require(call.scope, 0)?, call.scope)?;
        let arg2: D2 = from_handle(call.arguments.require(call.scope, 1)?, call.scope)?;
        Ok(f(file, arg1, arg2))
    })
}

register_module!(m, {
    m.export("tree_as_text", |call| file_fn0(call, editor_api::tree_as_text))?;
    m.export("performance_counters", |call| file_fn0(call, performance_counters))?;
    m.export("highlight", |call| file_fn0(call, editor_api::highlight))?;
    m.export("structure", |call| file_fn0(call, editor_api::structure))?;
    m.export("extend_selection", |call| file_fn1(call, editor_api::extend_selection))?;
    m.export("context_actions", |call| file_fn1(call, editor_api::context_actions))?;
    m.export("apply_context_action", |call| file_fn2(call, |file, range: TextRange, aid: String| {
        editor_api::apply_context_action(file, range, &aid)
    }))?;
    m.export("resolve_reference", |call| file_fn1(call, editor_api::resolve_reference))?;
    m.export("find_usages", |call| file_fn1(call, editor_api::find_usages))?;
    m.export("diagnostics", |call| a_fn0(call, editor_api::diagnostics))?;
    m.export("reformat", |call| file_fn0(call, editor_api::reformat))?;
    m.export("test_at_offset", |call| file_fn1(call, editor_api::test_at_offset))?;
    m.export("parse_test", parse_test)?;
    m.export("file_create", file_create)?;
    Ok(())
});

fn from_handle<'a, T: serde::Deserialize<'a> + ? Sized>(
    input: Handle<'a, JsValue>,
    scope: &mut RootScope<'a>,
) -> neon_serde::errors::Result<T> {
    let scope: &'a mut RootScope<'a> = unsafe { ::std::mem::transmute(scope) };
    neon_serde::from_handle(input, scope)
}

fn to_value<'value, 'scope, V: Serialize + ?Sized>(
    value: &'value V,
    scope: &mut RootScope<'scope>,
) -> neon_serde::errors::Result<Handle<'scope, JsValue>> {
    let scope: &'scope mut RootScope<'scope> = unsafe { ::std::mem::transmute(scope) };
    neon_serde::to_value(value, scope)
}

<commit_msg>Fix code<commit_after>#[macro_use]
extern crate neon;
#[macro_use]
extern crate lazy_static;
extern crate fall_tree;
extern crate fall_gen;
extern crate lang_fall;

extern crate neon_serde;
#[macro_use]
extern crate serde_derive;
extern crate serde;

use serde::{Serialize, Deserialize};

use std::sync::{Arc, Mutex};

use neon::vm::{Call, VmResult, JsResult};
use neon::scope::{Scope, RootScope};
use neon::mem::Handle;
use neon::js::{JsString, JsInteger, JsNull, JsValue, JsFunction};
use neon::task::Task;

use lang_fall::{editor_api, FileWithAnalysis, Analysis};
use fall_tree::{TextRange, File};
use fall_gen::TestRenderer;

lazy_static! {
    static ref FILE: Mutex<Option<Arc<FileWithAnalysis>>> = Mutex::new(None);
}

fn file_create(call: Call) -> JsResult<JsNull> {
    let scope = call.scope;
    let text = call.arguments.require(scope, 0)?.check::<JsString>()?;
    let text = text.value();
    *FILE.lock().unwrap() = Some(Arc::new(editor_api::analyse(text)));
    Ok(JsNull::new())
}

#[derive(Serialize)]
struct PerformanceCounters {
    lexing_time: u32,
    parsing_time: u32,
    reparsed_region: TextRange,
}

fn performance_counters(file: &File) -> PerformanceCounters {
    let stats = file.stats();
    PerformanceCounters {
        lexing_time: stats.lexing_time.subsec_nanos(),
        parsing_time: stats.parsing_time.subsec_nanos(),
        reparsed_region: stats.reparsed_region,
    }
}

struct RenderTask(Arc<FileWithAnalysis>, usize);
impl Task for RenderTask {
    type Output = String;
    type Error = ();
    type JsEvent = JsString;

    fn perform(&self) -> Result<String, ()> {
        lazy_static! {
            static ref TEST_RENDERER: Mutex<TestRenderer> = Mutex::new(TestRenderer);
        }

        let mut renderer = TEST_RENDERER.lock().unwrap();
        let tree = renderer.render_one(&self.0.file(), self.1);
        Ok(tree)
    }

    fn complete<'a, T: Scope<'a>>(self, scope: &'a mut T, result: Result<String, ()>) -> JsResult<JsString> {
        Ok(JsString::new(scope, &result.unwrap()).unwrap())
    }
}

fn parse_test(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let test = call.arguments.require(scope, 0)?.check::<JsInteger>()?.value() as usize;
    let callback = call.arguments.require(scope, 1)?.check::<JsFunction>()?;
    let file = FILE.lock().unwrap();

    if let Some(ref file) = *file {
        RenderTask(file.clone(), test).schedule(callback);
    }
    Ok(JsNull::new().upcast())
}

fn a_fn<'a, S, F>(call: &mut Call<'a>, f: F) -> JsResult<'a, JsValue>
    where S: Serialize,
          F: Fn(&mut Call<'a>, &Analysis) -> VmResult<S> {
    let file = FILE.lock().unwrap();
    let file: &FileWithAnalysis = match *file {
        None => return Ok(JsNull::new().upcast()),
        Some(ref file) => &*file,
    };
    file.analyse(|a| {
        let result = f(call, a)?;
        Ok(to_value(&result, call.scope)?)
    })
}

fn a_fn0<S: Serialize, F: Fn(&Analysis) -> S>(mut call: Call, f: F) -> JsResult<JsValue> {
    a_fn(&mut call, |_, file| Ok(f(file)))
}

fn a_fn1<'c, S: Serialize, D: Deserialize<'c>>(
    mut call: Call<'c>,
    f: fn(&Analysis, D) -> S
) -> JsResult<'c, JsValue> {
    a_fn(&mut call, |call, file| {
        let arg: D = from_handle(call.arguments.require(call.scope, 0)?, call.scope)?;
        Ok(f(file, arg))
    })
}

fn file_fn<'a, S, F>(call: &mut Call<'a>, f: F) -> JsResult<'a, JsValue>
    where S: Serialize,
          F: Fn(&mut Call<'a>, &File) -> VmResult<S> {
    let file = FILE.lock().unwrap();
    let file = match *file {
        None => return Ok(JsNull::new().upcast()),
        Some(ref file) => file.file(),
    };
    let result = f(call, file)?;
    Ok(to_value(&result, call.scope)?)
}

fn file_fn0<S: Serialize, F: Fn(&File) -> S>(mut call: Call, f: F) -> JsResult<JsValue> {
    file_fn(&mut call, |_, file| Ok(f(file)))
}

fn file_fn1<'c, S: Serialize, D: Deserialize<'c>>(
    mut call: Call<'c>,
    f: fn(&File, D) -> S
) -> JsResult<'c, JsValue> {
    file_fn(&mut call, |call, file| {
        let arg: D = from_handle(call.arguments.require(call.scope, 0)?, call.scope)?;
        Ok(f(file, arg))
    })
}

fn file_fn2<'c, S: Serialize, D1: Deserialize<'c>, D2: Deserialize<'c>>(
    mut call: Call<'c>,
    f: fn(&File, D1, D2) -> S
) -> JsResult<'c, JsValue> {
    file_fn(&mut call, |call, file| {
        let arg1: D1 = from_handle(call.arguments.require(call.scope, 0)?, call.scope)?;
        let arg2: D2 = from_handle(call.arguments.require(call.scope, 1)?, call.scope)?;
        Ok(f(file, arg1, arg2))
    })
}

register_module!(m, {
    m.export("tree_as_text", |call| file_fn0(call, editor_api::tree_as_text))?;
    m.export("performance_counters", |call| file_fn0(call, performance_counters))?;
    m.export("highlight", |call| a_fn0(call, editor_api::highlight))?;
    m.export("structure", |call| file_fn0(call, editor_api::structure))?;
    m.export("extend_selection", |call| file_fn1(call, editor_api::extend_selection))?;
    m.export("context_actions", |call| file_fn1(call, editor_api::context_actions))?;
    m.export("apply_context_action", |call| file_fn2(call, |file, range: TextRange, aid: String| {
        editor_api::apply_context_action(file, range, &aid)
    }))?;
    m.export("resolve_reference", |call| a_fn1(call, editor_api::resolve_reference))?;
    m.export("find_usages", |call| a_fn1(call, editor_api::find_usages))?;
    m.export("diagnostics", |call| a_fn0(call, editor_api::diagnostics))?;
    m.export("reformat", |call| file_fn0(call, editor_api::reformat))?;
    m.export("test_at_offset", |call| file_fn1(call, editor_api::test_at_offset))?;
    m.export("parse_test", parse_test)?;
    m.export("file_create", file_create)?;
    Ok(())
});

fn from_handle<'a, T: serde::Deserialize<'a> + ? Sized>(
    input: Handle<'a, JsValue>,
    scope: &mut RootScope<'a>,
) -> neon_serde::errors::Result<T> {
    let scope: &'a mut RootScope<'a> = unsafe { ::std::mem::transmute(scope) };
    neon_serde::from_handle(input, scope)
}

fn to_value<'value, 'scope, V: Serialize + ?Sized>(
    value: &'value V,
    scope: &mut RootScope<'scope>,
) -> neon_serde::errors::Result<Handle<'scope, JsValue>> {
    let scope: &'scope mut RootScope<'scope> = unsafe { ::std::mem::transmute(scope) };
    neon_serde::to_value(value, scope)
}

<|endoftext|>


<commit_before>//! Utilities for creating zero-cost wakers that don't do anything.
use futures_core::task::{RawWaker, RawWakerVTable, Waker};
use core::ptr::null;
#[cfg(feature = "std")]
use core::cell::UnsafeCell;

unsafe fn noop_clone(_data: *const ()) -> RawWaker {
    noop_raw_waker()
}

unsafe fn noop(_data: *const ()) {}

const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);

fn noop_raw_waker() -> RawWaker {
    RawWaker::new(null(), &NOOP_WAKER_VTABLE)
}

/// Create a new [`Waker`](futures_core::task::Waker) which does
/// nothing when `wake()` is called on it. The [`Waker`] can be converted
/// into a [`Waker`] which will behave the same way.
///
/// # Examples
///
/// ```
/// use futures::task::noop_waker;
/// let waker = noop_waker();
/// waker.wake();
/// ```
#[inline]
pub fn noop_waker() -> Waker {
    unsafe {
        Waker::from_raw(noop_raw_waker())
    }
}

/// Get a thread local reference to a
/// [`Waker`](futures_core::task::Waker) referencing a singleton
/// instance of a [`Waker`] which panics when woken.
///
/// # Examples
///
/// ```
/// use futures::task::noop_waker_ref;
/// let waker = noop_waker_ref();
/// waker.wake_by_ref();
/// ```
#[inline]
#[cfg(feature = "std")]
pub fn noop_waker_ref() -> &'static Waker {
    thread_local! {
        static NOOP_WAKER_INSTANCE: UnsafeCell<Waker> =
            UnsafeCell::new(noop_waker());
    }
    NOOP_WAKER_INSTANCE.with(|l| unsafe { &*l.get() })
}
<commit_msg>This is problematic, cross crate references don't work and there is no clear solution...<commit_after>//! Utilities for creating zero-cost wakers that don't do anything.
use futures_core::task::{RawWaker, RawWakerVTable, Waker};
use core::ptr::null;
#[cfg(feature = "std")]
use core::cell::UnsafeCell;

unsafe fn noop_clone(_data: *const ()) -> RawWaker {
    noop_raw_waker()
}

unsafe fn noop(_data: *const ()) {}

const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);

fn noop_raw_waker() -> RawWaker {
    RawWaker::new(null(), &NOOP_WAKER_VTABLE)
}

/// Create a new [`Waker`](futures_core::task::Waker) which does
/// nothing when `wake()` is called on it. The [`Waker`](futures_core::task::Waker) can be converted
/// into a [`Waker`](futures_core::task::Waker) which will behave the same way.
///
/// # Examples
///
/// ```
/// use futures::task::noop_waker;
/// let waker = noop_waker();
/// waker.wake();
/// ```
#[inline]
pub fn noop_waker() -> Waker {
    unsafe {
        Waker::from_raw(noop_raw_waker())
    }
}

/// Get a thread local reference to a
/// [`Waker`](futures_core::task::Waker) referencing a singleton
/// instance of a [`Waker`](futures_core::task::Waker) which panics when woken.
///
/// # Examples
///
/// ```
/// use futures::task::noop_waker_ref;
/// let waker = noop_waker_ref();
/// waker.wake_by_ref();
/// ```
#[inline]
#[cfg(feature = "std")]
pub fn noop_waker_ref() -> &'static Waker {
    thread_local! {
        static NOOP_WAKER_INSTANCE: UnsafeCell<Waker> =
            UnsafeCell::new(noop_waker());
    }
    NOOP_WAKER_INSTANCE.with(|l| unsafe { &*l.get() })
}
<|endoftext|>


<commit_before>// Copyright 2018 Developers of the Rand project.
// Copyright 2013-2015 The Rust Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Interface to the random number generator of the operating system.
// Note: keep this code in sync with the rand::rngs::os module!

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
       html_root_url = "https://rust-random.github.io/rand/")]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![doc(test(attr(allow(unused_variables), deny(warnings))))]

#![no_std]  // but see getrandom crate

#![deprecated(since="0.2.2", note="OsRng is now provided by rand_core and rand")]

pub use rand_core;  // re-export

use getrandom::getrandom;
use rand_core::{CryptoRng, RngCore, Error, impls};

/// A random number generator that retrieves randomness from from the
/// operating system.
///
/// This is a zero-sized struct. It can be freely constructed with `OsRng`.
///
/// The implementation is provided by the [getrandom] crate. Refer to
/// [getrandom] documentation for details.
///
/// # Blocking and error handling
///
/// It is possible that when used during early boot the first call to `OsRng`
/// will block until the system's RNG is initialised. It is also possible
/// (though highly unlikely) for `OsRng` to fail on some platforms, most
/// likely due to system mis-configuration.
///
/// After the first successful call, it is highly unlikely that failures or
/// significant delays will occur (although performance should be expected to
/// be much slower than a user-space PRNG).
///
/// # Usage example
/// ```
/// use rand_os::rand_core::RngCore;
/// use rand_os::OsRng;
///
/// let mut key = [0u8; 16];
/// OsRng.fill_bytes(&mut key);
/// let random_u64 = OsRng.next_u64();
/// ```
///
/// [getrandom]: https://crates.io/crates/getrandom
#[derive(Clone, Copy, Debug, Default)]
pub struct OsRng;

impl OsRng {
    /// Create a new `OsRng`.
    #[deprecated(since="0.2.0", note="replace OsRng::new().unwrap() with just OsRng")]
    pub fn new() -> Result<OsRng, Error> {
        Ok(OsRng)
    }
}

impl CryptoRng for OsRng {}

impl RngCore for OsRng {
    fn next_u32(&mut self) -> u32 {
        impls::next_u32_via_fill(self)
    }

    fn next_u64(&mut self) -> u64 {
        impls::next_u64_via_fill(self)
    }

    fn fill_bytes(&mut self, dest: &mut [u8]) {
        if let Err(e) = self.try_fill_bytes(dest) {
            panic!("Error: {}", e);
        }
    }

    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
        getrandom(dest)?;
        Ok(())
    }
}

#[test]
fn test_os_rng() {
    let x = OsRng.next_u64();
    let y = OsRng.next_u64();
    assert!(x != 0);
    assert!(x != y);
}

#[test]
fn test_construction() {
    let mut rng = OsRng::default();
    assert!(rng.next_u64() != 0);
}
<commit_msg>rand_os: fix test<commit_after>// Copyright 2018 Developers of the Rand project.
// Copyright 2013-2015 The Rust Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Interface to the random number generator of the operating system.
// Note: keep this code in sync with the rand::rngs::os module!

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
       html_root_url = "https://rust-random.github.io/rand/")]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![doc(test(attr(allow(unused_variables), deny(warnings))))]

#![no_std]  // but see getrandom crate

#![deprecated(since="0.2.2", note="OsRng is now provided by rand_core and rand")]

pub use rand_core;  // re-export

use getrandom::getrandom;
use rand_core::{CryptoRng, RngCore, Error, impls};

/// A random number generator that retrieves randomness from from the
/// operating system.
///
/// This is a zero-sized struct. It can be freely constructed with `OsRng`.
///
/// The implementation is provided by the [getrandom] crate. Refer to
/// [getrandom] documentation for details.
///
/// # Blocking and error handling
///
/// It is possible that when used during early boot the first call to `OsRng`
/// will block until the system's RNG is initialised. It is also possible
/// (though highly unlikely) for `OsRng` to fail on some platforms, most
/// likely due to system mis-configuration.
///
/// After the first successful call, it is highly unlikely that failures or
/// significant delays will occur (although performance should be expected to
/// be much slower than a user-space PRNG).
///
/// # Usage example
/// ```
/// #![allow(deprecated)]
/// use rand_os::rand_core::RngCore;
/// use rand_os::OsRng;
///
/// let mut key = [0u8; 16];
/// OsRng.fill_bytes(&mut key);
/// let random_u64 = OsRng.next_u64();
/// ```
///
/// [getrandom]: https://crates.io/crates/getrandom
#[derive(Clone, Copy, Debug, Default)]
pub struct OsRng;

impl OsRng {
    /// Create a new `OsRng`.
    #[deprecated(since="0.2.0", note="replace OsRng::new().unwrap() with just OsRng")]
    pub fn new() -> Result<OsRng, Error> {
        Ok(OsRng)
    }
}

impl CryptoRng for OsRng {}

impl RngCore for OsRng {
    fn next_u32(&mut self) -> u32 {
        impls::next_u32_via_fill(self)
    }

    fn next_u64(&mut self) -> u64 {
        impls::next_u64_via_fill(self)
    }

    fn fill_bytes(&mut self, dest: &mut [u8]) {
        if let Err(e) = self.try_fill_bytes(dest) {
            panic!("Error: {}", e);
        }
    }

    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
        getrandom(dest)?;
        Ok(())
    }
}

#[test]
fn test_os_rng() {
    let x = OsRng.next_u64();
    let y = OsRng.next_u64();
    assert!(x != 0);
    assert!(x != y);
}

#[test]
fn test_construction() {
    let mut rng = OsRng::default();
    assert!(rng.next_u64() != 0);
}
<|endoftext|>


<commit_before>extern crate strutil;

#[test]
fn test_is_anagram() {
    assert_eq!(false, strutil::is_anagram("", ""));
    assert_eq!(false, strutil::is_anagram("abc", "abcd"));
    assert_eq!(true, strutil::is_anagram("Race car", "Car race"));
    assert_eq!(true, strutil::is_anagram("Angel", "glean"));
    assert_eq!(true, strutil::is_anagram("Madam Curie", "Radium came"));
    assert_eq!(true,
               strutil::is_anagram("Eleven plus two", "Twelve plus one"));
    assert_eq!(true, strutil::is_anagram("A gentleman", "Elegant man"));
    assert_eq!(true,
               strutil::is_anagram("♥☺♥☺♥☺♥", "♥♥♥♥☺☺☺"));
}<commit_msg>Add additional test<commit_after>extern crate strutil;

#[test]
fn test_is_anagram() {
    assert_eq!(false, strutil::is_anagram("", ""));
    assert_eq!(false, strutil::is_anagram("abc", "abcd"));
    assert_eq!(true, strutil::is_anagram("Race car", "Car race"));
    assert_eq!(true, strutil::is_anagram("Angel", "glean"));
    assert_eq!(true, strutil::is_anagram("Madam Curie", "Radium came"));
    assert_eq!(true,
               strutil::is_anagram("Eleven plus two", "Twelve plus one"));
    assert_eq!(true, strutil::is_anagram("A gentleman", "Elegant man"));
    assert_eq!(true,
               strutil::is_anagram("♥☺♥☺♥☺♥", "♥♥♥♥☺☺☺"));
    assert_eq!(false, strutil::is_anagram("racecar", "racebar"));
}<|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 https://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Keyword %>
<% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>

${helpers.predefined_type(
    "color",
    "ColorPropertyValue",
    "::cssparser::RGBA::new(0, 0, 0, 255)",
    engines="gecko servo-2013 servo-2020",
    animation_value_type="AnimatedRGBA",
    ignored_when_colors_disabled="True",
    spec="https://drafts.csswg.org/css-color/#color",
)}

${helpers.predefined_type(
    "line-height",
    "LineHeight",
    "computed::LineHeight::normal()",
    engines="gecko servo-2013 servo-2020",
    animation_value_type="LineHeight",
    spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height",
    servo_restyle_damage="reflow"
)}

// CSS Text Module Level 3

${helpers.predefined_type(
    "text-transform",
    "TextTransform",
    "computed::TextTransform::none()",
    engines="gecko servo-2013",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-text-transform",
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.single_keyword(
    "hyphens",
    "manual none auto",
    engines="gecko",
    gecko_enum_prefix="StyleHyphens",
    animation_value_type="discrete",
    extra_prefixes="moz",
    spec="https://drafts.csswg.org/css-text/#propdef-hyphens",
)}

// TODO: Support <percentage>
${helpers.single_keyword(
    "-moz-text-size-adjust",
    "auto none",
    engines="gecko",
    gecko_enum_prefix="StyleTextSizeAdjust",
    gecko_ffi_name="mTextSizeAdjust",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-size-adjust/#adjustment-control",
    alias="-webkit-text-size-adjust",
)}

${helpers.predefined_type(
    "text-indent",
    "LengthPercentage",
    "computed::LengthPercentage::zero()",
    engines="gecko servo-2013 servo-2020",
    servo_2020_pref="layout.2020.unimplemented",
    animation_value_type="ComputedValue",
    spec="https://drafts.csswg.org/css-text/#propdef-text-indent",
    allow_quirks="Yes",
    servo_restyle_damage = "reflow",
)}

// Also known as "word-wrap" (which is more popular because of IE), but this is
// the preferred name per CSS-TEXT 6.2.
${helpers.predefined_type(
    "overflow-wrap",
    "OverflowWrap",
    "computed::OverflowWrap::Normal",
    engines="gecko servo-2013 servo-2020",
    servo_2020_pref="layout.2020.unimplemented",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap",
    alias="word-wrap",
    needs_context=False,
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.predefined_type(
    "word-break",
    "WordBreak",
    "computed::WordBreak::Normal",
    engines="gecko servo-2013 servo-2020",
    servo_2020_pref="layout.2020.unimplemented",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-word-break",
    needs_context=False,
    servo_restyle_damage="rebuild_and_reflow",
)}

// TODO(pcwalton): Support `text-justify: distribute`.
<%helpers:single_keyword
    name="text-justify"
    values="auto none inter-word"
    engines="gecko servo-2013 servo-2020",
    servo_2020_pref="layout.2020.unimplemented",
    extra_gecko_values="inter-character"
    extra_specified="${'distribute' if engine == 'gecko' else ''}"
    gecko_enum_prefix="StyleTextJustify"
    animation_value_type="discrete"
    gecko_pref="layout.css.text-justify.enabled"
    has_effect_on_gecko_scrollbars="False"
    spec="https://drafts.csswg.org/css-text/#propdef-text-justify"
    servo_restyle_damage="rebuild_and_reflow"
>
    % if engine == 'gecko':
    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, _: &Context) -> computed_value::T {
            match *self {
                % for value in "Auto None InterCharacter InterWord".split():
                SpecifiedValue::${value} => computed_value::T::${value},
                % endfor
                // https://drafts.csswg.org/css-text-3/#valdef-text-justify-distribute
                SpecifiedValue::Distribute => computed_value::T::InterCharacter,
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> SpecifiedValue {
            match *computed {
                % for value in "Auto None InterCharacter InterWord".split():
                computed_value::T::${value} => SpecifiedValue::${value},
                % endfor
            }
        }
    }
    % endif
</%helpers:single_keyword>

${helpers.predefined_type(
    "text-align-last",
    "TextAlignLast",
    "computed::text::TextAlignLast::Auto",
    needs_context=False,
    engines="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-text-align-last",
)}

// TODO make this a shorthand and implement text-align-last/text-align-all
${helpers.predefined_type(
    "text-align",
    "TextAlign",
    "computed::TextAlign::Start",
    engines="gecko servo-2013 servo-2020",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-text-align",
    servo_restyle_damage = "reflow",
)}

${helpers.predefined_type(
    "letter-spacing",
    "LetterSpacing",
    "computed::LetterSpacing::normal()",
    engines="gecko servo-2013 servo-2020",
    animation_value_type="ComputedValue",
    spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing",
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.predefined_type(
    "word-spacing",
    "WordSpacing",
    "computed::WordSpacing::zero()",
    engines="gecko servo-2013 servo-2020",
    animation_value_type="ComputedValue",
    spec="https://drafts.csswg.org/css-text/#propdef-word-spacing",
    servo_restyle_damage="rebuild_and_reflow",
)}

<%helpers:single_keyword
    name="white-space"
    values="normal pre nowrap pre-wrap pre-line"
    engines="gecko servo-2013 servo-2020",
    extra_gecko_values="break-spaces -moz-pre-space"
    gecko_enum_prefix="StyleWhiteSpace"
    needs_conversion="True"
    animation_value_type="discrete"
    spec="https://drafts.csswg.org/css-text/#propdef-white-space"
    servo_restyle_damage="rebuild_and_reflow"
>
    % if engine in ["servo-2013", "servo-2020"]:
    impl SpecifiedValue {
        pub fn allow_wrap(&self) -> bool {
            match *self {
                SpecifiedValue::Nowrap |
                SpecifiedValue::Pre => false,
                SpecifiedValue::Normal |
                SpecifiedValue::PreWrap |
                SpecifiedValue::PreLine => true,
            }
        }

        pub fn preserve_newlines(&self) -> bool {
            match *self {
                SpecifiedValue::Normal |
                SpecifiedValue::Nowrap => false,
                SpecifiedValue::Pre |
                SpecifiedValue::PreWrap |
                SpecifiedValue::PreLine => true,
            }
        }

        pub fn preserve_spaces(&self) -> bool {
            match *self {
                SpecifiedValue::Normal |
                SpecifiedValue::Nowrap |
                SpecifiedValue::PreLine => false,
                SpecifiedValue::Pre |
                SpecifiedValue::PreWrap => true,
            }
        }
    }
    % endif
</%helpers:single_keyword>

${helpers.predefined_type(
    "text-shadow",
    "SimpleShadow",
    None,
    engines="gecko servo-2013",
    vector=True,
    vector_animation_type="with_zero",
    animation_value_type="AnimatedTextShadowList",
    ignored_when_colors_disabled=True,
    simple_vector_bindings=True,
    spec="https://drafts.csswg.org/css-text-decor-3/#text-shadow-property",
)}

${helpers.predefined_type(
    "text-emphasis-style",
    "TextEmphasisStyle",
    "computed::TextEmphasisStyle::None",
    engines="gecko",
    initial_specified_value="SpecifiedValue::None",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style",
)}

${helpers.predefined_type(
    "text-emphasis-position",
    "TextEmphasisPosition",
    "computed::TextEmphasisPosition::over_right()",
    engines="gecko",
    initial_specified_value="specified::TextEmphasisPosition::over_right()",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position",
)}

${helpers.predefined_type(
    "text-emphasis-color",
    "Color",
    "computed_value::T::currentcolor()",
    engines="gecko",
    initial_specified_value="specified::Color::currentcolor()",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color",
)}

${helpers.predefined_type(
    "-moz-tab-size",
    "NonNegativeLengthOrNumber",
    "generics::length::LengthOrNumber::Number(From::from(8.0))",
    engines="gecko",
    animation_value_type="LengthOrNumber",
    spec="https://drafts.csswg.org/css-text-3/#tab-size-property",
)}

${helpers.predefined_type(
    "line-break",
    "LineBreak",
    "computed::LineBreak::Auto",
    engines="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text-3/#line-break-property",
    needs_context=False,
)}

// CSS Compatibility
// https://compat.spec.whatwg.org
${helpers.predefined_type(
    "-webkit-text-fill-color",
    "Color",
    "computed_value::T::currentcolor()",
    engines="gecko",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color",
)}

${helpers.predefined_type(
    "-webkit-text-stroke-color",
    "Color",
    "computed_value::T::currentcolor()",
    initial_specified_value="specified::Color::currentcolor()",
    engines="gecko",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color",
)}

${helpers.predefined_type(
    "-webkit-text-stroke-width",
    "BorderSideWidth",
    "crate::values::computed::NonNegativeLength::new(0.)",
    engines="gecko",
    initial_specified_value="specified::BorderSideWidth::zero()",
    computed_type="crate::values::computed::NonNegativeLength",
    spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-width",
    animation_value_type="discrete",
)}

// CSS Ruby Layout Module Level 1
// https://drafts.csswg.org/css-ruby/
${helpers.single_keyword(
    "ruby-align",
    "space-around start center space-between",
    engines="gecko",
    animation_value_type="discrete",
    gecko_enum_prefix="StyleRubyAlign",
    spec="https://drafts.csswg.org/css-ruby/#ruby-align-property",
)}

${helpers.single_keyword(
    "ruby-position",
    "over under",
    engines="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-ruby/#ruby-position-property",
    gecko_enum_prefix="StyleRubyPosition",
)}

// CSS Writing Modes Module Level 3
// https://drafts.csswg.org/css-writing-modes-3/

${helpers.single_keyword(
    "text-combine-upright",
    "none all",
    engines="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-writing-modes-3/#text-combine-upright",
)}

// SVG 1.1: Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword(
    "text-rendering",
    "auto optimizespeed optimizelegibility geometricprecision",
    engines="gecko servo-2013 servo-2020",
    gecko_enum_prefix="StyleTextRendering",
    animation_value_type="discrete",
    spec="https://www.w3.org/TR/SVG11/painting.html#TextRenderingProperty",
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.single_keyword(
    "-moz-control-character-visibility",
    "hidden visible",
    engines="gecko",
    gecko_enum_prefix="StyleControlCharacterVisibility",
    gecko_pref_controlled_initial_value="layout.css.control-characters.visible=visible",
    animation_value_type="none",
    gecko_ffi_name="mControlCharacterVisibility",
    spec="Nonstandard",
)}

// text underline offset
${helpers.predefined_type(
    "text-underline-offset",
    "LengthPercentageOrAuto",
    "computed::LengthPercentageOrAuto::auto()",
    engines="gecko",
    animation_value_type="ComputedValue",
    gecko_pref="layout.css.text-underline-offset.enabled",
    has_effect_on_gecko_scrollbars=False,
    spec="https://drafts.csswg.org/css-text-decor-4/#underline-offset",
)}

// text underline position
${helpers.predefined_type(
    "text-underline-position",
    "TextUnderlinePosition",
    "computed::TextUnderlinePosition::AUTO",
    engines="gecko",
    animation_value_type="discrete",
    gecko_pref="layout.css.text-underline-position.enabled",
    has_effect_on_gecko_scrollbars=False,
    spec="https://drafts.csswg.org/css-text-decor-3/#text-underline-position-property",
)}

// text decoration skip ink
${helpers.predefined_type(
    "text-decoration-skip-ink",
    "TextDecorationSkipInk",
    "computed::TextDecorationSkipInk::Auto",
    engines="gecko",
    needs_context=False,
    animation_value_type="discrete",
    gecko_pref="layout.css.text-decoration-skip-ink.enabled",
    has_effect_on_gecko_scrollbars=False,
    spec="https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property",
)}
<commit_msg>style: Make text-combine-upright not animatable.<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 https://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Keyword %>
<% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>

${helpers.predefined_type(
    "color",
    "ColorPropertyValue",
    "::cssparser::RGBA::new(0, 0, 0, 255)",
    engines="gecko servo-2013 servo-2020",
    animation_value_type="AnimatedRGBA",
    ignored_when_colors_disabled="True",
    spec="https://drafts.csswg.org/css-color/#color",
)}

${helpers.predefined_type(
    "line-height",
    "LineHeight",
    "computed::LineHeight::normal()",
    engines="gecko servo-2013 servo-2020",
    animation_value_type="LineHeight",
    spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height",
    servo_restyle_damage="reflow"
)}

// CSS Text Module Level 3

${helpers.predefined_type(
    "text-transform",
    "TextTransform",
    "computed::TextTransform::none()",
    engines="gecko servo-2013",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-text-transform",
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.single_keyword(
    "hyphens",
    "manual none auto",
    engines="gecko",
    gecko_enum_prefix="StyleHyphens",
    animation_value_type="discrete",
    extra_prefixes="moz",
    spec="https://drafts.csswg.org/css-text/#propdef-hyphens",
)}

// TODO: Support <percentage>
${helpers.single_keyword(
    "-moz-text-size-adjust",
    "auto none",
    engines="gecko",
    gecko_enum_prefix="StyleTextSizeAdjust",
    gecko_ffi_name="mTextSizeAdjust",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-size-adjust/#adjustment-control",
    alias="-webkit-text-size-adjust",
)}

${helpers.predefined_type(
    "text-indent",
    "LengthPercentage",
    "computed::LengthPercentage::zero()",
    engines="gecko servo-2013 servo-2020",
    servo_2020_pref="layout.2020.unimplemented",
    animation_value_type="ComputedValue",
    spec="https://drafts.csswg.org/css-text/#propdef-text-indent",
    allow_quirks="Yes",
    servo_restyle_damage = "reflow",
)}

// Also known as "word-wrap" (which is more popular because of IE), but this is
// the preferred name per CSS-TEXT 6.2.
${helpers.predefined_type(
    "overflow-wrap",
    "OverflowWrap",
    "computed::OverflowWrap::Normal",
    engines="gecko servo-2013 servo-2020",
    servo_2020_pref="layout.2020.unimplemented",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap",
    alias="word-wrap",
    needs_context=False,
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.predefined_type(
    "word-break",
    "WordBreak",
    "computed::WordBreak::Normal",
    engines="gecko servo-2013 servo-2020",
    servo_2020_pref="layout.2020.unimplemented",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-word-break",
    needs_context=False,
    servo_restyle_damage="rebuild_and_reflow",
)}

// TODO(pcwalton): Support `text-justify: distribute`.
<%helpers:single_keyword
    name="text-justify"
    values="auto none inter-word"
    engines="gecko servo-2013 servo-2020",
    servo_2020_pref="layout.2020.unimplemented",
    extra_gecko_values="inter-character"
    extra_specified="${'distribute' if engine == 'gecko' else ''}"
    gecko_enum_prefix="StyleTextJustify"
    animation_value_type="discrete"
    gecko_pref="layout.css.text-justify.enabled"
    has_effect_on_gecko_scrollbars="False"
    spec="https://drafts.csswg.org/css-text/#propdef-text-justify"
    servo_restyle_damage="rebuild_and_reflow"
>
    % if engine == 'gecko':
    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, _: &Context) -> computed_value::T {
            match *self {
                % for value in "Auto None InterCharacter InterWord".split():
                SpecifiedValue::${value} => computed_value::T::${value},
                % endfor
                // https://drafts.csswg.org/css-text-3/#valdef-text-justify-distribute
                SpecifiedValue::Distribute => computed_value::T::InterCharacter,
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> SpecifiedValue {
            match *computed {
                % for value in "Auto None InterCharacter InterWord".split():
                computed_value::T::${value} => SpecifiedValue::${value},
                % endfor
            }
        }
    }
    % endif
</%helpers:single_keyword>

${helpers.predefined_type(
    "text-align-last",
    "TextAlignLast",
    "computed::text::TextAlignLast::Auto",
    needs_context=False,
    engines="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-text-align-last",
)}

// TODO make this a shorthand and implement text-align-last/text-align-all
${helpers.predefined_type(
    "text-align",
    "TextAlign",
    "computed::TextAlign::Start",
    engines="gecko servo-2013 servo-2020",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text/#propdef-text-align",
    servo_restyle_damage = "reflow",
)}

${helpers.predefined_type(
    "letter-spacing",
    "LetterSpacing",
    "computed::LetterSpacing::normal()",
    engines="gecko servo-2013 servo-2020",
    animation_value_type="ComputedValue",
    spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing",
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.predefined_type(
    "word-spacing",
    "WordSpacing",
    "computed::WordSpacing::zero()",
    engines="gecko servo-2013 servo-2020",
    animation_value_type="ComputedValue",
    spec="https://drafts.csswg.org/css-text/#propdef-word-spacing",
    servo_restyle_damage="rebuild_and_reflow",
)}

<%helpers:single_keyword
    name="white-space"
    values="normal pre nowrap pre-wrap pre-line"
    engines="gecko servo-2013 servo-2020",
    extra_gecko_values="break-spaces -moz-pre-space"
    gecko_enum_prefix="StyleWhiteSpace"
    needs_conversion="True"
    animation_value_type="discrete"
    spec="https://drafts.csswg.org/css-text/#propdef-white-space"
    servo_restyle_damage="rebuild_and_reflow"
>
    % if engine in ["servo-2013", "servo-2020"]:
    impl SpecifiedValue {
        pub fn allow_wrap(&self) -> bool {
            match *self {
                SpecifiedValue::Nowrap |
                SpecifiedValue::Pre => false,
                SpecifiedValue::Normal |
                SpecifiedValue::PreWrap |
                SpecifiedValue::PreLine => true,
            }
        }

        pub fn preserve_newlines(&self) -> bool {
            match *self {
                SpecifiedValue::Normal |
                SpecifiedValue::Nowrap => false,
                SpecifiedValue::Pre |
                SpecifiedValue::PreWrap |
                SpecifiedValue::PreLine => true,
            }
        }

        pub fn preserve_spaces(&self) -> bool {
            match *self {
                SpecifiedValue::Normal |
                SpecifiedValue::Nowrap |
                SpecifiedValue::PreLine => false,
                SpecifiedValue::Pre |
                SpecifiedValue::PreWrap => true,
            }
        }
    }
    % endif
</%helpers:single_keyword>

${helpers.predefined_type(
    "text-shadow",
    "SimpleShadow",
    None,
    engines="gecko servo-2013",
    vector=True,
    vector_animation_type="with_zero",
    animation_value_type="AnimatedTextShadowList",
    ignored_when_colors_disabled=True,
    simple_vector_bindings=True,
    spec="https://drafts.csswg.org/css-text-decor-3/#text-shadow-property",
)}

${helpers.predefined_type(
    "text-emphasis-style",
    "TextEmphasisStyle",
    "computed::TextEmphasisStyle::None",
    engines="gecko",
    initial_specified_value="SpecifiedValue::None",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style",
)}

${helpers.predefined_type(
    "text-emphasis-position",
    "TextEmphasisPosition",
    "computed::TextEmphasisPosition::over_right()",
    engines="gecko",
    initial_specified_value="specified::TextEmphasisPosition::over_right()",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position",
)}

${helpers.predefined_type(
    "text-emphasis-color",
    "Color",
    "computed_value::T::currentcolor()",
    engines="gecko",
    initial_specified_value="specified::Color::currentcolor()",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color",
)}

${helpers.predefined_type(
    "-moz-tab-size",
    "NonNegativeLengthOrNumber",
    "generics::length::LengthOrNumber::Number(From::from(8.0))",
    engines="gecko",
    animation_value_type="LengthOrNumber",
    spec="https://drafts.csswg.org/css-text-3/#tab-size-property",
)}

${helpers.predefined_type(
    "line-break",
    "LineBreak",
    "computed::LineBreak::Auto",
    engines="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-text-3/#line-break-property",
    needs_context=False,
)}

// CSS Compatibility
// https://compat.spec.whatwg.org
${helpers.predefined_type(
    "-webkit-text-fill-color",
    "Color",
    "computed_value::T::currentcolor()",
    engines="gecko",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color",
)}

${helpers.predefined_type(
    "-webkit-text-stroke-color",
    "Color",
    "computed_value::T::currentcolor()",
    initial_specified_value="specified::Color::currentcolor()",
    engines="gecko",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color",
)}

${helpers.predefined_type(
    "-webkit-text-stroke-width",
    "BorderSideWidth",
    "crate::values::computed::NonNegativeLength::new(0.)",
    engines="gecko",
    initial_specified_value="specified::BorderSideWidth::zero()",
    computed_type="crate::values::computed::NonNegativeLength",
    spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-width",
    animation_value_type="discrete",
)}

// CSS Ruby Layout Module Level 1
// https://drafts.csswg.org/css-ruby/
${helpers.single_keyword(
    "ruby-align",
    "space-around start center space-between",
    engines="gecko",
    animation_value_type="discrete",
    gecko_enum_prefix="StyleRubyAlign",
    spec="https://drafts.csswg.org/css-ruby/#ruby-align-property",
)}

${helpers.single_keyword(
    "ruby-position",
    "over under",
    engines="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-ruby/#ruby-position-property",
    gecko_enum_prefix="StyleRubyPosition",
)}

// CSS Writing Modes Module Level 3
// https://drafts.csswg.org/css-writing-modes-3/

${helpers.single_keyword(
    "text-combine-upright",
    "none all",
    engines="gecko",
    animation_value_type="none",
    spec="https://drafts.csswg.org/css-writing-modes-3/#text-combine-upright",
)}

// SVG 1.1: Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword(
    "text-rendering",
    "auto optimizespeed optimizelegibility geometricprecision",
    engines="gecko servo-2013 servo-2020",
    gecko_enum_prefix="StyleTextRendering",
    animation_value_type="discrete",
    spec="https://www.w3.org/TR/SVG11/painting.html#TextRenderingProperty",
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.single_keyword(
    "-moz-control-character-visibility",
    "hidden visible",
    engines="gecko",
    gecko_enum_prefix="StyleControlCharacterVisibility",
    gecko_pref_controlled_initial_value="layout.css.control-characters.visible=visible",
    animation_value_type="none",
    gecko_ffi_name="mControlCharacterVisibility",
    spec="Nonstandard",
)}

// text underline offset
${helpers.predefined_type(
    "text-underline-offset",
    "LengthPercentageOrAuto",
    "computed::LengthPercentageOrAuto::auto()",
    engines="gecko",
    animation_value_type="ComputedValue",
    gecko_pref="layout.css.text-underline-offset.enabled",
    has_effect_on_gecko_scrollbars=False,
    spec="https://drafts.csswg.org/css-text-decor-4/#underline-offset",
)}

// text underline position
${helpers.predefined_type(
    "text-underline-position",
    "TextUnderlinePosition",
    "computed::TextUnderlinePosition::AUTO",
    engines="gecko",
    animation_value_type="discrete",
    gecko_pref="layout.css.text-underline-position.enabled",
    has_effect_on_gecko_scrollbars=False,
    spec="https://drafts.csswg.org/css-text-decor-3/#text-underline-position-property",
)}

// text decoration skip ink
${helpers.predefined_type(
    "text-decoration-skip-ink",
    "TextDecorationSkipInk",
    "computed::TextDecorationSkipInk::Auto",
    engines="gecko",
    needs_context=False,
    animation_value_type="discrete",
    gecko_pref="layout.css.text-decoration-skip-ink.enabled",
    has_effect_on_gecko_scrollbars=False,
    spec="https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property",
)}
<|endoftext|>


<commit_before>//! Entry point for the dummy project.
//! This comment block tests `rust doc`;
//! you should be able to see it from the documentation
//! folder after running the project's rustDoc task
//! in Gradle.

/// Prints a message to test documentation of
/// functions.
///
/// # Parameters
/// * `message`: The message to print.
///
/// # Examples
/// This prints "Hi!":
///
/// ```
/// sayHello("Hi!");
/// ```
pub fn sayHello(message: &str) {
	println!("{}", message);
}

/// Entry point for the program.
fn main() {
	sayHello("Rust works!");
}<commit_msg>Have dummy project use block doc comments.<commit_after>/*! Entry point for the dummy project.
This comment block tests `rust doc`;
you should be able to see it from the documentation
folder after running the project's rustDoc task
in Gradle.
*/

/** Prints a message to test documentation of
functions.

# Parameters
* `message`: The message to print.

# Examples
This prints "Hi!":

```
sayHello("Hi!");
```
*/
pub fn sayHello(message: &str) {
	println!("{}", message);
}

/// Entry point for the program.
fn main() {
	sayHello("Rust works!");
}<|endoftext|>