<commit_before>//! Contains node property related stuff.

use std::fmt;
use std::str;
use std::io::Read;
use byteorder::{LittleEndian, ReadBytesExt};
use flate2::read::ZlibDecoder;

macro_rules! try_opt {
    ($opt:expr) => (if let Some(val) = $opt {
        val
    } else {
        return None;
    });
}

#[derive(Clone)]
pub struct DelayedProperties {
    pub buffer: Vec<u8>,
    pub num_properties: usize,
}

impl DelayedProperties {
    pub fn from_vec_u8(vec: Vec<u8>, _version: i32, num_properties: usize) -> Self {
        DelayedProperties {
            buffer: vec,
            num_properties: num_properties,
        }
    }

    pub fn iter(&self) -> Iter {
        Iter {
            buffer: &self.buffer[..],
            rest_properties: self.num_properties,
        }
    }

    pub fn num_properties(&self) -> usize {
        self.num_properties
    }
}

impl fmt::Debug for DelayedProperties {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("DelayedProperties")
            .field("buffer_size", &self.buffer.len())
            .field("num_properties", &self.num_properties)
            .finish()
    }
}

pub struct Iter<'a> {
    buffer: &'a [u8],
    rest_properties: usize,
}

impl<'a> Iter<'a> {
    fn read_u8(&mut self) -> Option<u8> {
        const SIZE: usize = 1;
        if self.buffer.len() < SIZE {
            error!("Property data is too short");
            self.rest_properties = 0;
            return None;
        }
        let val = self.buffer.read_u8().unwrap();
        Some(val)
    }

    fn read_u32(&mut self) -> Option<u32> {
        const SIZE: usize = 4;
        if self.buffer.len() < SIZE {
            error!("Property data is too short");
            self.rest_properties = 0;
            return None;
        }
        let val = self.buffer.read_u32::<LittleEndian>().unwrap();
        Some(val)
    }

    fn read_i16(&mut self) -> Option<i16> {
        const SIZE: usize = 2;
        if self.buffer.len() < SIZE {
            error!("Property data is too short");
            self.rest_properties = 0;
            return None;
        }
        let val = self.buffer.read_i16::<LittleEndian>().unwrap();
        Some(val)
    }

    fn read_i32(&mut self) -> Option<i32> {
        const SIZE: usize = 4;
        if self.buffer.len() < SIZE {
            error!("Property data is too short");
            self.rest_properties = 0;
            return None;
        }
        let val = self.buffer.read_i32::<LittleEndian>().unwrap();
        Some(val)
    }

    fn read_i64(&mut self) -> Option<i64> {
        const SIZE: usize = 8;
        if self.buffer.len() < SIZE {
            error!("Property data is too short");
            self.rest_properties = 0;
            return None;
        }
        let val = self.buffer.read_i64::<LittleEndian>().unwrap();
        Some(val)
    }

    fn read_f32(&mut self) -> Option<f32> {
        const SIZE: usize = 4;
        if self.buffer.len() < SIZE {
            error!("Property data is too short");
            self.rest_properties = 0;
            return None;
        }
        let val = self.buffer.read_f32::<LittleEndian>().unwrap();
        Some(val)
    }

    fn read_f64(&mut self) -> Option<f64> {
        const SIZE: usize = 8;
        if self.buffer.len() < SIZE {
            error!("Property data is too short");
            self.rest_properties = 0;
            return None;
        }
        let val = self.buffer.read_f64::<LittleEndian>().unwrap();
        Some(val)
    }
}

impl<'a> Iterator for Iter<'a> {
    type Item = Property<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.rest_properties == 0 {
            return None;
        }
        let type_code = try_opt!(self.read_u8());
        match type_code {
            // Boolean.
            b'C' => {
                let val = try_opt!(self.read_u8());
                if (val != b'T') && (val != b'Y') {
                    warn!("Expected 0x54 ('T') or 0x59 ('Y') as boolean property value, but got {:#x}", val);
                }
                self.rest_properties -= 1;
                Some(Property::Bool(val & 1 == 1))
            },
            // 2-byte signed integer.
            b'Y' => {
                let val = try_opt!(self.read_i16());
                self.rest_properties -= 1;
                Some(Property::I16(val))
            },
            // 4-byte signed integer.
            b'I' => {
                let val = try_opt!(self.read_i32());
                self.rest_properties -= 1;
                Some(Property::I32(val))
            },
            // 8-byte signed integer.
            b'L' => {
                let val = try_opt!(self.read_i64());
                self.rest_properties -= 1;
                Some(Property::I64(val))
            },
            // 4-byte single-precision IEEE 754 floating-point number.
            b'F' => {
                let val = try_opt!(self.read_f32());
                self.rest_properties -= 1;
                Some(Property::F32(val))
            },
            // 8-byte single-precision IEEE 754 floating-point number.
            b'D' => {
                let val = try_opt!(self.read_f64());
                self.rest_properties -= 1;
                Some(Property::F64(val))
            },
            // String.
            b'S' => {
                let length = try_opt!(self.read_u32()) as usize;
                if self.buffer.len() < length {
                    error!("Property data is too short");
                    self.rest_properties = 0;
                    return None;
                }
                let buf = &self.buffer[0..length];
                self.buffer = &self.buffer[length..];
                self.rest_properties -= 1;
                let strbuf = try_opt!(str::from_utf8(buf).map_err(|err| {
                    error!("Failed to decode a property of string type: {}", err);
                    self.rest_properties = 0;
                }).ok());
                Some(Property::String(strbuf))
            },
            // Raw binary.
            b'R' => {
                let length = try_opt!(self.read_u32()) as usize;
                if self.buffer.len() < length {
                    error!("Property data is too short");
                    self.rest_properties = 0;
                    return None;
                }
                let buf = &self.buffer[0..length];
                self.buffer = &self.buffer[length..];
                self.rest_properties -= 1;
                Some(Property::Binary(buf))
            },
            b'b' | b'i' | b'l' | b'f' | b'd' => {
                let array_header = if let Some((header, length)) = ArrayHeader::from_binary(self.buffer) {
                    self.buffer = &self.buffer[length..];
                    header
                } else {
                    error!("Property data is too short");
                    self.rest_properties = 0;
                    return None;
                };
                let buffer = if self.buffer.len() < array_header.compressed_length {
                    error!("Property data is too short");
                    self.rest_properties = 0;
                    return None;
                } else {
                    let bufs = self.buffer.split_at(array_header.compressed_length);
                    self.buffer = bufs.1;
                    bufs.0
                };
                if let Some(val) = read_property_array(buffer, &array_header, type_code) {
                    self.rest_properties -= 1;
                    Some(val)
                } else {
                    self.rest_properties = 0;
                    None
                }
            },
            _ => {
                error!("Unknown type code: {:#x}", type_code);
                self.rest_properties = 0;
                None
            },
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (0, Some(self.rest_properties))
    }
}

/// Header of array type property value.
struct ArrayHeader {
    /// Number of values in the array, *NOT byte size*.
    num_elements: usize,
    /// Denotes whether data in stream is plain, or what algorithm it is compressed by.
    encoding: u32,
    /// Byte size of the compressed array value in the stream.
    compressed_length: usize,
}

impl ArrayHeader {
    /// Constructs `ArrayValueHeader` from the given binary.
    pub fn from_binary(source: &[u8]) -> Option<(Self, usize)> {
        const LENGTH: usize = 4 * 3;
        let mut buffer = source;
        if buffer.len() < LENGTH {
            return None;
        }
        // `buffer` has enough length of data. `read_u32()`s must success.
        let num_elements = buffer.read_u32::<LittleEndian>().unwrap() as usize;
        let encoding = buffer.read_u32::<LittleEndian>().unwrap();
        let compressed_length = buffer.read_u32::<LittleEndian>().unwrap() as usize;
        Some((ArrayHeader {
            num_elements: num_elements,
            encoding: encoding,
            compressed_length: compressed_length,
        }, LENGTH))
    }
}

#[derive(Debug)]
pub enum Property<'a> {
    /// Boolean.
    Bool(bool),
    /// 2-byte signed integer.
    I16(i16),
    /// 4-byte signed integer.
    I32(i32),
    /// 8-byte signed integer.
    I64(i64),
    /// 4-byte single-precision IEEE 754 floating-point number.
    F32(f32),
    /// 8-byte single-precision IEEE 754 floating-point number.
    F64(f64),
    /// String.
    String(&'a str),
    /// Raw binary.
    Binary(&'a [u8]),
    /// Array of boolean.
    VecBool(Vec<bool>),
    /// Array of 4-byte signed integer.
    VecI32(Vec<i32>),
    /// Array of 8-byte signed integer.
    VecI64(Vec<i64>),
    /// Array of 4-byte single-precision IEEE 754 number.
    VecF32(Vec<f32>),
    /// Array of 8-byte double-precision IEEE 754 number.
    VecF64(Vec<f64>),
}

fn read_property_array<'a>(mut buffer: &'a [u8], header: &ArrayHeader, type_code: u8) -> Option<Property<'static>> {
    match header.encoding {
        // 0: raw.
        0 => read_property_array_from_plain_stream(&mut buffer, header, type_code),
        // 1: zlib compressed.
        1 => read_property_array_from_plain_stream(&mut ZlibDecoder::new(buffer), header, type_code),
        // Unknown.
        e => {
            error!("Unknown property array encoding: encoding={}", e);
            None
        },
    }
}

fn read_property_array_from_plain_stream<R: Read>(reader: &mut R, header: &ArrayHeader, type_code: u8) -> Option<Property<'static>> {
    macro_rules! read_into_vec {
        ($t:ty, $read_fun:ident, $variant:ident) => ({
            let mut data = Vec::<$t>::with_capacity(header.num_elements);
            for _ in 0..header.num_elements {
                data.push(try_opt!(reader.$read_fun::<LittleEndian>().ok()));
            }
            Property::$variant(data)
        });
    }
    Some(match type_code {
        // Array of 4-byte signed integer.
        b'b' => {
            let mut data = Vec::<bool>::with_capacity(header.num_elements);
            // Don't check whether the values are 'T's and 'Y's.
            for _ in 0..header.num_elements {
                data.push(try_opt!(reader.read_u8().ok()) & 1 == 1);
            }
            Property::VecBool(data)
        },
        // Array of 4-byte signed integer.
        b'i' => read_into_vec!(i32, read_i32, VecI32),
        // Array of 8-byte signed integer.
        b'l' => read_into_vec!(i64, read_i64, VecI64),
        // Array of 4-byte single-precision IEEE 754 floating-point number.
        b'f' => read_into_vec!(f32, read_f32, VecF32),
        // Array of 8-byte single-precision IEEE 754 floating-point number.
        b'd' => read_into_vec!(f64, read_f64, VecF64),
        _ => unreachable!(),
    })
}
<commit_msg>Simpify codes (by using macros)<commit_after>//! Contains node property related stuff.

use std::fmt;
use std::str;
use std::io::Read;
use byteorder::{LittleEndian, ReadBytesExt};
use flate2::read::ZlibDecoder;

macro_rules! try_opt {
    ($opt:expr) => (if let Some(val) = $opt {
        val
    } else {
        return None;
    });
}

#[derive(Clone)]
pub struct DelayedProperties {
    pub buffer: Vec<u8>,
    pub num_properties: usize,
}

impl DelayedProperties {
    pub fn from_vec_u8(vec: Vec<u8>, _version: i32, num_properties: usize) -> Self {
        DelayedProperties {
            buffer: vec,
            num_properties: num_properties,
        }
    }

    pub fn iter(&self) -> Iter {
        Iter {
            buffer: &self.buffer[..],
            rest_properties: self.num_properties,
        }
    }

    pub fn num_properties(&self) -> usize {
        self.num_properties
    }
}

impl fmt::Debug for DelayedProperties {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("DelayedProperties")
            .field("buffer_size", &self.buffer.len())
            .field("num_properties", &self.num_properties)
            .finish()
    }
}

pub struct Iter<'a> {
    buffer: &'a [u8],
    rest_properties: usize,
}

macro_rules! implement_iter_read {
    ($t:ty, $read_fun:ident, $size:expr) => (
        impl<'a> Iter<'a> {
            fn $read_fun(&mut self) -> Option<$t> {
                // TODO: Get size from `$t` at compile time.
                //const SIZE: usize = ::std::mem::size_of::<$t>(); // size_of() is not `const fn`.
                const SIZE: usize = $size;
                if self.buffer.len() < SIZE {
                    error!("Property data is too short");
                    self.rest_properties = 0;
                    return None;
                }
                let val = self.buffer.$read_fun::<LittleEndian>().unwrap();
                Some(val)
            }
        }
    )
}

impl<'a> Iter<'a> {
    fn read_u8(&mut self) -> Option<u8> {
        const SIZE: usize = 1;
        if self.buffer.len() < SIZE {
            error!("Property data is too short");
            self.rest_properties = 0;
            return None;
        }
        let val = self.buffer.read_u8().unwrap();
        Some(val)
    }
}
implement_iter_read!(u32, read_u32, 4);
implement_iter_read!(i16, read_i16, 2);
implement_iter_read!(i32, read_i32, 4);
implement_iter_read!(i64, read_i64, 8);
implement_iter_read!(f32, read_f32, 4);
implement_iter_read!(f64, read_f64, 8);

impl<'a> Iterator for Iter<'a> {
    type Item = Property<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        macro_rules! read_primitive_prop {
            ($read_fun:ident, $variant:ident) => ({
                let val = try_opt!(self.$read_fun());
                self.rest_properties -= 1;
                Some(Property::$variant(val))
            })
        }
        if self.rest_properties == 0 {
            return None;
        }
        let type_code = try_opt!(self.read_u8());
        match type_code {
            // Boolean.
            b'C' => {
                let val = try_opt!(self.read_u8());
                if (val != b'T') && (val != b'Y') {
                    warn!("Expected 0x54 ('T') or 0x59 ('Y') as boolean property value, but got {:#x}", val);
                }
                self.rest_properties -= 1;
                Some(Property::Bool(val & 1 == 1))
            },
            // 2-byte signed integer.
            b'Y' => read_primitive_prop!(read_i16, I16),
            // 4-byte signed integer.
            b'I' => read_primitive_prop!(read_i32, I32),
            // 8-byte signed integer.
            b'L' => read_primitive_prop!(read_i64, I64),
            // 4-byte single-precision IEEE 754 floating-point number.
            b'F' => read_primitive_prop!(read_f32, F32),
            // 8-byte single-precision IEEE 754 floating-point number.
            b'D' => read_primitive_prop!(read_f64, F64),
            // String.
            b'S' => {
                let length = try_opt!(self.read_u32()) as usize;
                if self.buffer.len() < length {
                    error!("Property data is too short");
                    self.rest_properties = 0;
                    return None;
                }
                let buf = &self.buffer[0..length];
                self.buffer = &self.buffer[length..];
                self.rest_properties -= 1;
                let strbuf = try_opt!(str::from_utf8(buf).map_err(|err| {
                    error!("Failed to decode a property of string type: {}", err);
                    self.rest_properties = 0;
                }).ok());
                Some(Property::String(strbuf))
            },
            // Raw binary.
            b'R' => {
                let length = try_opt!(self.read_u32()) as usize;
                if self.buffer.len() < length {
                    error!("Property data is too short");
                    self.rest_properties = 0;
                    return None;
                }
                let buf = &self.buffer[0..length];
                self.buffer = &self.buffer[length..];
                self.rest_properties -= 1;
                Some(Property::Binary(buf))
            },
            b'b' | b'i' | b'l' | b'f' | b'd' => {
                let array_header = if let Some((header, length)) = ArrayHeader::from_binary(self.buffer) {
                    self.buffer = &self.buffer[length..];
                    header
                } else {
                    error!("Property data is too short");
                    self.rest_properties = 0;
                    return None;
                };
                if self.buffer.len() < array_header.compressed_length {
                    error!("Property data is too short");
                    self.rest_properties = 0;
                    return None;
                }
                let buf = &self.buffer[0..array_header.compressed_length];
                self.buffer = &self.buffer[array_header.compressed_length..];
                if let Some(val) = read_property_array(buf, &array_header, type_code) {
                    self.rest_properties -= 1;
                    Some(val)
                } else {
                    self.rest_properties = 0;
                    None
                }
            },
            _ => {
                error!("Unknown type code: {:#x}", type_code);
                self.rest_properties = 0;
                None
            },
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (0, Some(self.rest_properties))
    }
}

/// Header of array type property value.
struct ArrayHeader {
    /// Number of values in the array, *NOT byte size*.
    num_elements: usize,
    /// Denotes whether data in stream is plain, or what algorithm it is compressed by.
    encoding: u32,
    /// Byte size of the compressed array value in the stream.
    compressed_length: usize,
}

impl ArrayHeader {
    /// Constructs `ArrayValueHeader` from the given binary.
    pub fn from_binary(source: &[u8]) -> Option<(Self, usize)> {
        const LENGTH: usize = 4 * 3;
        let mut buffer = source;
        if buffer.len() < LENGTH {
            return None;
        }
        // `buffer` has enough length of data. `read_u32()`s must success.
        let num_elements = buffer.read_u32::<LittleEndian>().unwrap() as usize;
        let encoding = buffer.read_u32::<LittleEndian>().unwrap();
        let compressed_length = buffer.read_u32::<LittleEndian>().unwrap() as usize;
        Some((ArrayHeader {
            num_elements: num_elements,
            encoding: encoding,
            compressed_length: compressed_length,
        }, LENGTH))
    }
}

#[derive(Debug)]
pub enum Property<'a> {
    /// Boolean.
    Bool(bool),
    /// 2-byte signed integer.
    I16(i16),
    /// 4-byte signed integer.
    I32(i32),
    /// 8-byte signed integer.
    I64(i64),
    /// 4-byte single-precision IEEE 754 floating-point number.
    F32(f32),
    /// 8-byte single-precision IEEE 754 floating-point number.
    F64(f64),
    /// String.
    String(&'a str),
    /// Raw binary.
    Binary(&'a [u8]),
    /// Array of boolean.
    VecBool(Vec<bool>),
    /// Array of 4-byte signed integer.
    VecI32(Vec<i32>),
    /// Array of 8-byte signed integer.
    VecI64(Vec<i64>),
    /// Array of 4-byte single-precision IEEE 754 number.
    VecF32(Vec<f32>),
    /// Array of 8-byte double-precision IEEE 754 number.
    VecF64(Vec<f64>),
}

fn read_property_array<'a>(mut buffer: &'a [u8], header: &ArrayHeader, type_code: u8) -> Option<Property<'static>> {
    match header.encoding {
        // 0: raw.
        0 => read_property_array_from_plain_stream(&mut buffer, header, type_code),
        // 1: zlib compressed.
        1 => read_property_array_from_plain_stream(&mut ZlibDecoder::new(buffer), header, type_code),
        // Unknown.
        e => {
            error!("Unknown property array encoding: encoding={}", e);
            None
        },
    }
}

fn read_property_array_from_plain_stream<R: Read>(reader: &mut R, header: &ArrayHeader, type_code: u8) -> Option<Property<'static>> {
    macro_rules! read_into_vec {
        ($t:ty, $read_fun:ident, $variant:ident) => ({
            let mut data = Vec::<$t>::with_capacity(header.num_elements);
            for _ in 0..header.num_elements {
                data.push(try_opt!(reader.$read_fun::<LittleEndian>().ok()));
            }
            Property::$variant(data)
        });
    }
    Some(match type_code {
        // Array of 4-byte signed integer.
        b'b' => {
            let mut data = Vec::<bool>::with_capacity(header.num_elements);
            // Don't check whether the values are 'T's and 'Y's.
            for _ in 0..header.num_elements {
                data.push(try_opt!(reader.read_u8().ok()) & 1 == 1);
            }
            Property::VecBool(data)
        },
        // Array of 4-byte signed integer.
        b'i' => read_into_vec!(i32, read_i32, VecI32),
        // Array of 8-byte signed integer.
        b'l' => read_into_vec!(i64, read_i64, VecI64),
        // Array of 4-byte single-precision IEEE 754 floating-point number.
        b'f' => read_into_vec!(f32, read_f32, VecF32),
        // Array of 8-byte single-precision IEEE 754 floating-point number.
        b'd' => read_into_vec!(f64, read_f64, VecF64),
        _ => unreachable!(),
    })
}
<|endoftext|>


<commit_before>//! Tests for additional link arguments.

// NOTE: Many of these tests use `without_status()` when passing bogus flags
// because MSVC link.exe just gives a warning on unknown flags (how helpful!),
// and other linkers will return an error.

use cargo_test_support::registry::Package;
use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, basic_manifest, project};

#[cargo_test]
fn build_script_extra_link_arg_bin() {
    let p = project()
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/main.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg-bins=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("build -v")
        .masquerade_as_nightly_cargo()
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg_bin_single() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]

                name = "foobar"
                version = "0.5.0"
                authors = ["wycats@example.com"]

                [[bin]]
                name = "foo"
                [[bin]]
                name = "bar"
            "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg-bins=--bogus-flag-all");
                    println!("cargo:rustc-link-arg-bin=foo=--bogus-flag-foo");
                    println!("cargo:rustc-link-arg-bin=bar=--bogus-flag-bar");
                }
            "#,
        )
        .build();

    p.cargo("build -v")
        .masquerade_as_nightly_cargo()
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name foo [..]-C link-arg=--bogus-flag-all -C link-arg=--bogus-flag-foo[..]",
        )
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name bar [..]-C link-arg=--bogus-flag-all -C link-arg=--bogus-flag-bar[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg() {
    let p = project()
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/main.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("build -v")
        .masquerade_as_nightly_cargo()
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn link_arg_missing_target() {
    // Errors when a given target doesn't exist.
    let p = project()
        .file("src/lib.rs", "")
        .file(
            "build.rs",
            r#"fn main() { println!("cargo:rustc-link-arg-cdylib=--bogus"); }"#,
        )
        .build();

    // TODO: Uncomment this if cdylib restriction is re-added (see
    // cdylib_link_arg_transitive below).
    //     p.cargo("check")
    //         .with_status(101)
    //         .with_stderr("\
    // [COMPILING] foo [..]
    // error: invalid instruction `cargo:rustc-link-arg-cdylib` from build script of `foo v0.0.1 ([ROOT]/foo)`
    // The package foo v0.0.1 ([ROOT]/foo) does not have a cdylib target.
    // ")
    //         .run();

    p.change_file(
        "build.rs",
        r#"fn main() { println!("cargo:rustc-link-arg-bins=--bogus"); }"#,
    );

    p.cargo("check")
        .masquerade_as_nightly_cargo()
        .with_status(101)
        .with_stderr("\
[COMPILING] foo [..]
error: invalid instruction `cargo:rustc-link-arg-bins` from build script of `foo v0.0.1 ([ROOT]/foo)`
The package foo v0.0.1 ([ROOT]/foo) does not have a bin target.
")
        .run();

    p.change_file(
        "build.rs",
        r#"fn main() { println!("cargo:rustc-link-arg-bin=abc=--bogus"); }"#,
    );

    p.cargo("check")
        .masquerade_as_nightly_cargo()
        .with_status(101)
        .with_stderr(
            "\
[COMPILING] foo [..]
error: invalid instruction `cargo:rustc-link-arg-bin` from build script of `foo v0.0.1 ([ROOT]/foo)`
The package foo v0.0.1 ([ROOT]/foo) does not have a bin target with the name `abc`.
",
        )
        .run();

    p.change_file(
        "build.rs",
        r#"fn main() { println!("cargo:rustc-link-arg-bin=abc"); }"#,
    );

    p.cargo("check")
        .masquerade_as_nightly_cargo()
        .with_status(101)
        .with_stderr(
            "\
[COMPILING] foo [..]
error: invalid instruction `cargo:rustc-link-arg-bin=abc` from build script of `foo v0.0.1 ([ROOT]/foo)`
The instruction should have the form cargo:rustc-link-arg-bin=BIN=ARG
",
        )
        .run();
}

#[cargo_test]
fn cdylib_link_arg_transitive() {
    // There was an unintended regression in 1.50 where rustc-link-arg-cdylib
    // arguments from dependencies were being applied in the parent package.
    // Previously it was silently ignored.
    // See https://github.com/rust-lang/cargo/issues/9562
    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"

                [lib]
                crate-type = ["cdylib"]

                [dependencies]
                bar = {path="bar"}
            "#,
        )
        .file("src/lib.rs", "")
        .file("bar/Cargo.toml", &basic_manifest("bar", "1.0.0"))
        .file("bar/src/lib.rs", "")
        .file(
            "bar/build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg-cdylib=--bogus");
                }
            "#,
        )
        .build();
    p.cargo("build -v")
        .without_status()
        .with_stderr_contains(
            "\
[COMPILING] bar v1.0.0 [..]
[RUNNING] `rustc --crate-name build_script_build bar/build.rs [..]
[RUNNING] `[..]build-script-build[..]
warning: cargo:rustc-link-arg-cdylib was specified in the build script of bar v1.0.0 \
([ROOT]/foo/bar), but that package does not contain a cdylib target

Allowing this was an unintended change in the 1.50 release, and may become an error in \
the future. For more information, see <https://github.com/rust-lang/cargo/issues/9562>.
[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]
[COMPILING] foo v0.1.0 [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C link-arg=--bogus[..]`
",
        )
        .run();
}

#[cargo_test]
fn link_arg_transitive_not_allowed() {
    // Verify that transitive dependencies don't pass link args.
    //
    // Note that rustc-link-arg doesn't have any errors or warnings when it is
    // unused. Perhaps that could be more aggressive, but it is difficult
    // since it could be used for test binaries.
    Package::new("bar", "1.0.0")
        .file("src/lib.rs", "")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg=--bogus");
                }
            "#,
        )
        .publish();

    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"

                [lib]
                crate-type = ["cdylib"]

                [dependencies]
                bar = "1.0"
            "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build -v")
        .masquerade_as_nightly_cargo()
        .with_stderr(
            "\
[UPDATING] [..]
[DOWNLOADING] [..]
[DOWNLOADED] [..]
[COMPILING] bar v1.0.0
[RUNNING] `rustc --crate-name build_script_build [..]
[RUNNING] `[..]/build-script-build[..]
[RUNNING] `rustc --crate-name bar [..]
[COMPILING] foo v0.1.0 [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..]
[FINISHED] dev [..]
",
        )
        .with_stderr_does_not_contain("--bogus")
        .run();
}

#[cargo_test]
fn link_arg_with_doctest() {
    let p = project()
        .file(
            "src/lib.rs",
            r#"
                //! ```
                //! let x = 5;
                //! assert_eq!(x, 5);
                //! ```
            "#,
        )
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("test --doc -v")
        .masquerade_as_nightly_cargo()
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg_tests() {
    let p = project()
        .file("Cargo.toml", &basic_lib_manifest("foo"))
        .file("src/lib.rs", "")
        .file("tests/test_foo.rs", "")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg-tests=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("test -v")
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name test_foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg_benches() {
    let p = project()
        .file("Cargo.toml", &basic_lib_manifest("foo"))
        .file("src/lib.rs", "")
        .file("benches/bench_foo.rs", "")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg-benches=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("bench -v")
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name bench_foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg_examples() {
    let p = project()
        .file("Cargo.toml", &basic_lib_manifest("foo"))
        .file("src/lib.rs", "")
        .file("examples/example_foo.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg-examples=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("build -v --examples")
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name example_foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}
<commit_msg>Auto merge of #10866 - Muscraft:update-extra-link-tests, r=ehuss<commit_after>//! Tests for additional link arguments.

// NOTE: Many of these tests use `without_status()` when passing bogus flags
// because MSVC link.exe just gives a warning on unknown flags (how helpful!),
// and other linkers will return an error.

use cargo_test_support::registry::Package;
use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, basic_manifest, project};

#[cargo_test]
fn build_script_extra_link_arg_bin() {
    let p = project()
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/main.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg-bins=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("build -v")
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg_bin_single() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]

                name = "foobar"
                version = "0.5.0"
                authors = ["wycats@example.com"]

                [[bin]]
                name = "foo"
                [[bin]]
                name = "bar"
            "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg-bins=--bogus-flag-all");
                    println!("cargo:rustc-link-arg-bin=foo=--bogus-flag-foo");
                    println!("cargo:rustc-link-arg-bin=bar=--bogus-flag-bar");
                }
            "#,
        )
        .build();

    p.cargo("build -v")
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name foo [..]-C link-arg=--bogus-flag-all -C link-arg=--bogus-flag-foo[..]",
        )
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name bar [..]-C link-arg=--bogus-flag-all -C link-arg=--bogus-flag-bar[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg() {
    let p = project()
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/main.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("build -v")
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn link_arg_missing_target() {
    // Errors when a given target doesn't exist.
    let p = project()
        .file("src/lib.rs", "")
        .file(
            "build.rs",
            r#"fn main() { println!("cargo:rustc-link-arg-cdylib=--bogus"); }"#,
        )
        .build();

    // TODO: Uncomment this if cdylib restriction is re-added (see
    // cdylib_link_arg_transitive below).
    //     p.cargo("check")
    //         .with_status(101)
    //         .with_stderr("\
    // [COMPILING] foo [..]
    // error: invalid instruction `cargo:rustc-link-arg-cdylib` from build script of `foo v0.0.1 ([ROOT]/foo)`
    // The package foo v0.0.1 ([ROOT]/foo) does not have a cdylib target.
    // ")
    //         .run();

    p.change_file(
        "build.rs",
        r#"fn main() { println!("cargo:rustc-link-arg-bins=--bogus"); }"#,
    );

    p.cargo("check")
        .with_status(101)
        .with_stderr("\
[COMPILING] foo [..]
error: invalid instruction `cargo:rustc-link-arg-bins` from build script of `foo v0.0.1 ([ROOT]/foo)`
The package foo v0.0.1 ([ROOT]/foo) does not have a bin target.
")
        .run();

    p.change_file(
        "build.rs",
        r#"fn main() { println!("cargo:rustc-link-arg-bin=abc=--bogus"); }"#,
    );

    p.cargo("check")
        .with_status(101)
        .with_stderr(
            "\
[COMPILING] foo [..]
error: invalid instruction `cargo:rustc-link-arg-bin` from build script of `foo v0.0.1 ([ROOT]/foo)`
The package foo v0.0.1 ([ROOT]/foo) does not have a bin target with the name `abc`.
",
        )
        .run();

    p.change_file(
        "build.rs",
        r#"fn main() { println!("cargo:rustc-link-arg-bin=abc"); }"#,
    );

    p.cargo("check")
        .with_status(101)
        .with_stderr(
            "\
[COMPILING] foo [..]
error: invalid instruction `cargo:rustc-link-arg-bin=abc` from build script of `foo v0.0.1 ([ROOT]/foo)`
The instruction should have the form cargo:rustc-link-arg-bin=BIN=ARG
",
        )
        .run();
}

#[cargo_test]
fn cdylib_link_arg_transitive() {
    // There was an unintended regression in 1.50 where rustc-link-arg-cdylib
    // arguments from dependencies were being applied in the parent package.
    // Previously it was silently ignored.
    // See https://github.com/rust-lang/cargo/issues/9562
    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"

                [lib]
                crate-type = ["cdylib"]

                [dependencies]
                bar = {path="bar"}
            "#,
        )
        .file("src/lib.rs", "")
        .file("bar/Cargo.toml", &basic_manifest("bar", "1.0.0"))
        .file("bar/src/lib.rs", "")
        .file(
            "bar/build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg-cdylib=--bogus");
                }
            "#,
        )
        .build();
    p.cargo("build -v")
        .without_status()
        .with_stderr_contains(
            "\
[COMPILING] bar v1.0.0 [..]
[RUNNING] `rustc --crate-name build_script_build bar/build.rs [..]
[RUNNING] `[..]build-script-build[..]
warning: cargo:rustc-link-arg-cdylib was specified in the build script of bar v1.0.0 \
([ROOT]/foo/bar), but that package does not contain a cdylib target

Allowing this was an unintended change in the 1.50 release, and may become an error in \
the future. For more information, see <https://github.com/rust-lang/cargo/issues/9562>.
[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]
[COMPILING] foo v0.1.0 [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C link-arg=--bogus[..]`
",
        )
        .run();
}

#[cargo_test]
fn link_arg_transitive_not_allowed() {
    // Verify that transitive dependencies don't pass link args.
    //
    // Note that rustc-link-arg doesn't have any errors or warnings when it is
    // unused. Perhaps that could be more aggressive, but it is difficult
    // since it could be used for test binaries.
    Package::new("bar", "1.0.0")
        .file("src/lib.rs", "")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg=--bogus");
                }
            "#,
        )
        .publish();

    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"

                [lib]
                crate-type = ["cdylib"]

                [dependencies]
                bar = "1.0"
            "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build -v")
        .with_stderr(
            "\
[UPDATING] [..]
[DOWNLOADING] [..]
[DOWNLOADED] [..]
[COMPILING] bar v1.0.0
[RUNNING] `rustc --crate-name build_script_build [..]
[RUNNING] `[..]/build-script-build[..]
[RUNNING] `rustc --crate-name bar [..]
[COMPILING] foo v0.1.0 [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..]
[FINISHED] dev [..]
",
        )
        .with_stderr_does_not_contain("--bogus")
        .run();
}

#[cargo_test]
fn link_arg_with_doctest() {
    let p = project()
        .file(
            "src/lib.rs",
            r#"
                //! ```
                //! let x = 5;
                //! assert_eq!(x, 5);
                //! ```
            "#,
        )
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("test --doc -v")
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustdoc [..]--crate-name foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg_tests() {
    let p = project()
        .file("Cargo.toml", &basic_lib_manifest("foo"))
        .file("src/lib.rs", "")
        .file("tests/test_foo.rs", "")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg-tests=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("test -v")
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name test_foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg_benches() {
    let p = project()
        .file("Cargo.toml", &basic_lib_manifest("foo"))
        .file("src/lib.rs", "")
        .file("benches/bench_foo.rs", "")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg-benches=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("bench -v")
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name bench_foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}

#[cargo_test]
fn build_script_extra_link_arg_examples() {
    let p = project()
        .file("Cargo.toml", &basic_lib_manifest("foo"))
        .file("src/lib.rs", "")
        .file("examples/example_foo.rs", "fn main() {}")
        .file(
            "build.rs",
            r#"
                fn main() {
                    println!("cargo:rustc-link-arg-examples=--this-is-a-bogus-flag");
                }
            "#,
        )
        .build();

    p.cargo("build -v --examples")
        .without_status()
        .with_stderr_contains(
            "[RUNNING] `rustc --crate-name example_foo [..]-C link-arg=--this-is-a-bogus-flag[..]",
        )
        .run();
}
<|endoftext|>


<commit_before>//! Semantic analyzer module.
//!
//! Usually a semantic analyzer checks if everything is in order (variables
//! are in scope, type inference...) before tying to compile the program.
//!
//! The aim with this one, for simplicity, is to walk dependencies (including
//! cyclic ones) to determine in what order they must be written.

use std::collections::hash_map::HashMap;
use std::collections::hash_set::HashSet;

use super::lexer::Token;
use super::parser::{Ast, Parser};

/// Value returned in [`Semantic::analyze`][0] if any errors are found.
///
/// In this analyzer, we only return error if we find any unexpected tokens.
/// Here we store all of them.
///
/// [0]: struct.Semantic.html#method.analyze
pub type UnexpectedTokens = Vec<Token>;

/// The semantic analyzer in our language.
///
/// Given an AST (from a [`Parser`][0]) determines the order in which all
/// definitions must go.
///
/// [0]: ../parser/struct.Parser.html
pub struct Semantic {
    /// All the type definitions. Since we consumed the AST, we have to store
    /// them somewhere.
    pub definitions: HashMap<String, Vec<(String, String)>>,
    /// The order in which to write the definitions.
    pub order: Vec<String>,
    /// If any cyclic dependency is found, all the types involved are stored
    /// here, so they can be handled accordingly.
    pub cycles: HashSet<String>,
}

impl Semantic {
    /// Builds the semantic analyzer and analyzes the AST.
    ///
    /// # Examples
    ///
    /// ```
    /// use simcom::lexer::Lexer;
    /// use simcom::parser::Parser;
    /// use simcom::semantic::Semantic;
    ///
    /// let content = "tipo A(x: X);";
    /// let ast = Parser::new(Lexer::new(content));
    ///
    /// if let Ok(s) = Semantic::analyze(ast) {
    ///     assert_eq!(s.order[0], String::from("X"));
    ///     assert_eq!(s.order[1], String::from("A"));
    /// } else {
    ///     panic!("Wrong if/else branch!");
    /// }
    /// ```
    ///
    /// ```
    /// use simcom::lexer::{Lexer, Token};
    /// use simcom::parser::Parser;
    /// use simcom::semantic::Semantic;
    ///
    /// // Note the two semicolons:
    /// let content = "tipo A(x: X);;";
    /// let s = Semantic::analyze(Parser::new(Lexer::new(content)));
    ///
    /// if let Err(ve) = s {
    ///     assert_eq!(ve[0], Token::Semicolon);
    /// } else {
    ///     panic!("Wrong if/else branch!");
    /// }
    /// ```
    pub fn analyze(ast: Parser) -> Result<Semantic, UnexpectedTokens> {
        let mut definitions = HashMap::new();
        let mut errors = Vec::new();

        for definition in ast {
            match definition {
                Ast::TypeDefinition(name, parameters) => {
                    definitions.insert(name, build_parameters(parameters));
                },
                Ast::Unexpected(token) => errors.push(token),
                _ => unreachable!(),
            }
        }

        match errors.is_empty() {
            true => SemanticBuilder::build(definitions),
            false => Err(errors),
        }
    }
}

struct SemanticBuilder {
    definitions: HashMap<String, Vec<(String, String)>>,

    order: Vec<String>,
    visited: HashSet<String>,
    cycles: HashSet<String>,
}

impl SemanticBuilder {
    fn build(definitions: HashMap<String, Vec<(String, String)>>) -> Result<Semantic, UnexpectedTokens> {
        let mut sb = SemanticBuilder {
            definitions,
            order: Vec::new(),
            visited: HashSet::new(),
            cycles: HashSet::new(),
        };

        for node in sb.definitions.clone().keys() {
            sb.visit(node);
        }

        Ok(Semantic {
            definitions: sb.definitions,
            order: sb.order,
            cycles: sb.cycles,
        })
    }

    fn visit(&mut self, node: &String) {
        if self.order.contains(node) {
            return;
        } else if self.visited.contains(node) {
            self.cycles = self.visited.clone();
            return;
        }

        self.visited.insert(node.clone());

        if let Some(d) = self.definitions.clone().get(node) {
            for &(_, ref v) in d {
                self.visit(v);
            }
        }

        self.order.push(node.clone());
        self.visited.remove(node);
    }
}

fn ast_to_parameter(ast: Ast) -> Option<(String, String)> {
    match ast {
        Ast::Parameter(name, typename) => Some((name, typename)),
        _ => None,
    }
}

fn build_parameters(ast: Vec<Ast>) -> Vec<(String, String)> {
    ast.into_iter()
        .filter_map(ast_to_parameter)
        .collect()
}

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

    fn get_semantic(content: &str) -> Result<Semantic, UnexpectedTokens> {
        Semantic::analyze(Parser::new(Lexer::new(content)))
    }

    #[test]
    fn order_ok() {
        let content = "tipo A(x: long);\
        tipo B(a: A);";

        let a = get_semantic(content).unwrap();

        assert_eq!(a.order[0], String::from("long"));
        assert_eq!(a.order[1], String::from("A"));
        assert_eq!(a.order[2], String::from("B"));
    }

    #[test]
    fn cycle() {
        let content = "tipo A(x: B);\
        tipo B (x: A);";

        let s = get_semantic(content).unwrap();

        assert!(s.cycles.contains("A"));
        assert!(s.cycles.contains("B"));
    }

    #[test]
    fn cycle_order_1() {
        let content = "tipo A(x: B);\
        tipo B(x: A);\
        tipo C(a: A, b: B);";

        let s = get_semantic(content).unwrap();

        assert_eq!(s.order[2], String::from("C"));
        assert!(s.cycles.contains("A"));
        assert!(s.cycles.contains("B"));
    }

    #[test]
    fn cycle_order_2() {
        let content = "tipo A(x: B, c: C);\
        tipo B(x: A);";

        let s = get_semantic(content).unwrap();

        let t_b = String::from("B");
        let t_c = String::from("C");

        let assert = s.order[0] == t_c || (s.order[0] == t_b && s.order[1] == t_c);

        assert!(assert);
        assert!(s.cycles.contains("A"));
        assert!(s.cycles.contains(&t_b));
    }

    #[test]
    fn cycle_order_3() {
        let content = "tipo C(a: A);\
        tipo A(b: B);\
        tipo B(a: A);";

        let s = get_semantic(content).unwrap();

        let t_a = String::from("A");
        let t_b = String::from("B");
        let t_c = String::from("C");

        let assert_1 = s.order == vec![t_a.clone(), t_b.clone(), t_c.clone()];
        let assert_2 = s.order == vec![t_a.clone(), t_c.clone(), t_b.clone()];
        let assert_3 = s.order == vec![t_b.clone(), t_a.clone(), t_c.clone()];

        assert!(assert_1 || assert_2 || assert_3);
        assert!(s.cycles.contains(&t_a));
        assert!(s.cycles.contains(&t_b));
    }
}
<commit_msg>Use Self instead of struct name. Avoid repetition.<commit_after>//! Semantic analyzer module.
//!
//! Usually a semantic analyzer checks if everything is in order (variables
//! are in scope, type inference...) before tying to compile the program.
//!
//! The aim with this one, for simplicity, is to walk dependencies (including
//! cyclic ones) to determine in what order they must be written.

use std::collections::hash_map::HashMap;
use std::collections::hash_set::HashSet;

use super::lexer::Token;
use super::parser::{Ast, Parser};

/// Value returned in [`Semantic::analyze`][0] if any errors are found.
///
/// In this analyzer, we only return error if we find any unexpected tokens.
/// Here we store all of them.
///
/// [0]: struct.Semantic.html#method.analyze
pub type UnexpectedTokens = Vec<Token>;

/// The semantic analyzer in our language.
///
/// Given an AST (from a [`Parser`][0]) determines the order in which all
/// definitions must go.
///
/// [0]: ../parser/struct.Parser.html
pub struct Semantic {
    /// All the type definitions. Since we consumed the AST, we have to store
    /// them somewhere.
    pub definitions: HashMap<String, Vec<(String, String)>>,
    /// The order in which to write the definitions.
    pub order: Vec<String>,
    /// If any cyclic dependency is found, all the types involved are stored
    /// here, so they can be handled accordingly.
    pub cycles: HashSet<String>,
}

impl Semantic {
    /// Builds the semantic analyzer and analyzes the AST.
    ///
    /// # Examples
    ///
    /// ```
    /// use simcom::lexer::Lexer;
    /// use simcom::parser::Parser;
    /// use simcom::semantic::Semantic;
    ///
    /// let content = "tipo A(x: X);";
    /// let ast = Parser::new(Lexer::new(content));
    ///
    /// if let Ok(s) = Semantic::analyze(ast) {
    ///     assert_eq!(s.order[0], String::from("X"));
    ///     assert_eq!(s.order[1], String::from("A"));
    /// } else {
    ///     panic!("Wrong if/else branch!");
    /// }
    /// ```
    ///
    /// ```
    /// use simcom::lexer::{Lexer, Token};
    /// use simcom::parser::Parser;
    /// use simcom::semantic::Semantic;
    ///
    /// // Note the two semicolons:
    /// let content = "tipo A(x: X);;";
    /// let s = Semantic::analyze(Parser::new(Lexer::new(content)));
    ///
    /// if let Err(ve) = s {
    ///     assert_eq!(ve[0], Token::Semicolon);
    /// } else {
    ///     panic!("Wrong if/else branch!");
    /// }
    /// ```
    pub fn analyze(ast: Parser) -> Result<Self, UnexpectedTokens> {
        let mut definitions = HashMap::new();
        let mut errors = Vec::new();

        for definition in ast {
            match definition {
                Ast::TypeDefinition(name, parameters) => {
                    definitions.insert(name, build_parameters(parameters));
                },
                Ast::Unexpected(token) => errors.push(token),
                _ => unreachable!(),
            }
        }

        match errors.is_empty() {
            true => SemanticBuilder::build(definitions),
            false => Err(errors),
        }
    }
}

struct SemanticBuilder {
    definitions: HashMap<String, Vec<(String, String)>>,

    order: Vec<String>,
    visited: HashSet<String>,
    cycles: HashSet<String>,
}

impl SemanticBuilder {
    fn build(definitions: HashMap<String, Vec<(String, String)>>) -> Result<Semantic, UnexpectedTokens> {
        let mut sb = Self {
            definitions,
            order: Vec::new(),
            visited: HashSet::new(),
            cycles: HashSet::new(),
        };

        for node in sb.definitions.clone().keys() {
            sb.visit(node);
        }

        Ok(Semantic {
            definitions: sb.definitions,
            order: sb.order,
            cycles: sb.cycles,
        })
    }

    fn visit(&mut self, node: &String) {
        if self.order.contains(node) {
            return;
        } else if self.visited.contains(node) {
            self.cycles = self.visited.clone();
            return;
        }

        self.visited.insert(node.clone());

        if let Some(d) = self.definitions.clone().get(node) {
            for &(_, ref v) in d {
                self.visit(v);
            }
        }

        self.order.push(node.clone());
        self.visited.remove(node);
    }
}

fn ast_to_parameter(ast: Ast) -> Option<(String, String)> {
    match ast {
        Ast::Parameter(name, typename) => Some((name, typename)),
        _ => None,
    }
}

fn build_parameters(ast: Vec<Ast>) -> Vec<(String, String)> {
    ast.into_iter()
        .filter_map(ast_to_parameter)
        .collect()
}

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

    fn get_semantic(content: &str) -> Result<Semantic, UnexpectedTokens> {
        Semantic::analyze(Parser::new(Lexer::new(content)))
    }

    #[test]
    fn order_ok() {
        let content = "tipo A(x: long);\
        tipo B(a: A);";

        let a = get_semantic(content).unwrap();

        assert_eq!(a.order[0], String::from("long"));
        assert_eq!(a.order[1], String::from("A"));
        assert_eq!(a.order[2], String::from("B"));
    }

    #[test]
    fn cycle() {
        let content = "tipo A(x: B);\
        tipo B (x: A);";

        let s = get_semantic(content).unwrap();

        assert!(s.cycles.contains("A"));
        assert!(s.cycles.contains("B"));
    }

    #[test]
    fn cycle_order_1() {
        let content = "tipo A(x: B);\
        tipo B(x: A);\
        tipo C(a: A, b: B);";

        let s = get_semantic(content).unwrap();

        assert_eq!(s.order[2], String::from("C"));
        assert!(s.cycles.contains("A"));
        assert!(s.cycles.contains("B"));
    }

    #[test]
    fn cycle_order_2() {
        let content = "tipo A(x: B, c: C);\
        tipo B(x: A);";

        let s = get_semantic(content).unwrap();

        let t_b = String::from("B");
        let t_c = String::from("C");

        let assert = s.order[0] == t_c || (s.order[0] == t_b && s.order[1] == t_c);

        assert!(assert);
        assert!(s.cycles.contains("A"));
        assert!(s.cycles.contains(&t_b));
    }

    #[test]
    fn cycle_order_3() {
        let content = "tipo C(a: A);\
        tipo A(b: B);\
        tipo B(a: A);";

        let s = get_semantic(content).unwrap();

        let t_a = String::from("A");
        let t_b = String::from("B");
        let t_c = String::from("C");

        let assert_1 = s.order == vec![t_a.clone(), t_b.clone(), t_c.clone()];
        let assert_2 = s.order == vec![t_a.clone(), t_c.clone(), t_b.clone()];
        let assert_3 = s.order == vec![t_b.clone(), t_a.clone(), t_c.clone()];

        assert!(assert_1 || assert_2 || assert_3);
        assert!(s.cycles.contains(&t_a));
        assert!(s.cycles.contains(&t_b));
    }
}
<|endoftext|>


<commit_before>//! combinators applying parsers in sequence

#[macro_use]
mod macros;

use internal::IResult;
use error::ParseError;

/// Gets an object from the first parser,
/// then gets another object from the second parser.
/// # Arguments
/// * `first` The first parser to apply.
/// * `second` The second parser to apply.
/// ```rust
/// # #[macro_use] extern crate nom;
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::pair;
/// use nom::bytes::complete::tag;
/// # fn main() {
/// let first_parser = |s: &'static str| {
///   tag::<_, _, (_, ErrorKind)>("abc")(s)
/// };
/// let second_parser = |s: &'static str| {
///   tag::<_, _, (_, ErrorKind)>("efg")(s)
/// };
/// let parser = |s: &'static str| {
///   pair::<_, _, _, (_, ErrorKind), _, _>(first_parser, second_parser)(s)
/// };
///
/// assert_eq!(parser("abcefg"), Ok(("", ("abc", "efg"))));
/// assert_eq!(parser("abcefghij"), Ok(("hij", ("abc", "efg"))));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// # }
/// ```
pub fn pair<I, O1, O2, E: ParseError<I>, F, G>(first: F, second: G) -> impl Fn(I) -> IResult<I, (O1, O2), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  move |input: I| {
    let (input, o1) = first(input)?;
    second(input).map(|(i, o2)| (i, (o1, o2)))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn pairc<I, O1, O2, E: ParseError<I>, F, G>(input: I, first: F, second: G) -> IResult<I, (O1, O2), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  pair(first, second)(input)
}

/// Matches an object from the first parser and discards it,
/// then gets an object from the second parser.
/// # Arguments
/// * `first` The opening parser.
/// * `second` The second parser to get object.
/// ```rust
/// # #[macro_use] extern crate nom;
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::preceded;
/// use nom::bytes::complete::tag;
/// # fn main() {
/// let first_parser = |s: &'static str| {
///   tag::<_, _, (_, ErrorKind)>("abc")(s)
/// };
/// let second_parser = |s: &'static str| {
///   tag::<_, _, (_, ErrorKind)>("efg")(s)
/// };
/// let parser = |s: &'static str| {
///   preceded::<_, _, _, (_, ErrorKind), _, _>(first_parser, second_parser)(s)
/// };
///
/// assert_eq!(parser("abcefg"), Ok(("", "efg")));
/// assert_eq!(parser("abcefghij"), Ok(("hij", "efg")));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// # }
/// ```
pub fn preceded<I, O1, O2, E: ParseError<I>, F, G>(first: F, second: G) -> impl Fn(I) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  move |input: I| {
    let (input, _) = first(input)?;
    second(input)
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn precededc<I, O1, O2, E: ParseError<I>, F, G>(input: I, first: F, second: G) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  preceded(first, second)(input)
}

/// Gets an object from the first parser,
/// then matches an object from the second parser and discards it.
/// # Arguments
/// * `first` The first parser to apply.
/// * `second` The second parser to match an object.
/// ```rust
/// # #[macro_use] extern crate nom;
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::terminated;
/// use nom::bytes::complete::tag;
/// # fn main() {
/// let first_parser = |s: &'static str| {
///   tag::<_, _, (_, ErrorKind)>("abc")(s)
/// };
/// let second_parser = |s: &'static str| {
///   tag::<_, _, (_, ErrorKind)>("efg")(s)
/// };
/// let parser = |s: &'static str| {
///   terminated::<_, _, _, (_, ErrorKind), _, _>(first_parser, second_parser)(s)
/// };
///
/// assert_eq!(parser("abcefg"), Ok(("", "abc")));
/// assert_eq!(parser("abcefghij"), Ok(("hij", "abc")));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// # }
/// ```
pub fn terminated<I, O1, O2, E: ParseError<I>, F, G>(first: F, second: G) -> impl Fn(I) -> IResult<I, O1, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  move |input: I| {
    let (input, o1) = first(input)?;
    second(input).map(|(i, _)| (i, o1))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn terminatedc<I, O1, O2, E: ParseError<I>, F, G>(input: I, first: F, second: G) -> IResult<I, O1, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  terminated(first, second)(input)
}

/// Gets an object from the first parser,
/// then matches an object from the sep_parser and discards it,
/// then gets another object from the second parser.
/// # Arguments
/// * `first` The first parser to apply.
/// * `sep` The separator parser to apply.
/// * `second` The second parser to apply.
/// ```rust
/// # #[macro_use] extern crate nom;
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::separated_pair;
/// use nom::bytes::complete::tag;
/// # fn main() {
/// let first_parser = |s: &'static str| {
///   tag::<_, _, (_, ErrorKind)>("abc")(s)
/// };
/// let sep_parser = |s: &'static str| {
///   tag::<_, _, (_, ErrorKind)>("|")(s)
/// };
/// let second_parser = |s: &'static str| {
///   tag::<_, _, (_, ErrorKind)>("efg")(s)
/// };
/// let parser = |s: &'static str| {
///   separated_pair::<_, _, _, _, (_, ErrorKind), _, _, _>(first_parser, sep_parser, second_parser)(s)
/// };
///
/// assert_eq!(parser("abc|efg"), Ok(("", ("abc", "efg"))));
/// assert_eq!(parser("abc|efghij"), Ok(("hij", ("abc", "efg"))));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// # }
/// ```
pub fn separated_pair<I, O1, O2, O3, E: ParseError<I>, F, G, H>(first: F, sep: G, second: H) -> impl Fn(I) -> IResult<I, (O1, O3), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  move |input: I| {
    let (input, o1) = first(input)?;
    let (input, _) = sep(input)?;
    second(input).map(|(i, o2)| (i, (o1, o2)))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn separated_pairc<I, O1, O2, O3, E: ParseError<I>, F, G, H>(input: I, first: F, sep: G, second: H) -> IResult<I, (O1, O3), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  separated_pair(first, sep, second)(input)
}

/// Matches an object from the first parser,
/// then gets an object from the sep_parser,
/// then matches another object from the second parser.
/// # Arguments
/// * `first` The first parser to apply.
/// * `sep` The separator parser to apply.
/// * `second` The second parser to apply.
/// ```rust
/// # #[macro_use] extern crate nom;
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::delimited;
/// use nom::bytes::complete::tag;
/// # fn main() {
/// let first_parser = |s: &'static str| {
///   tag::<_, _, (_, ErrorKind)>("abc")(s)
/// };
/// let sep_parser = |s: &'static str| {
///   tag::<_, _, (_, ErrorKind)>("|")(s)
/// };
/// let second_parser = |s: &'static str| {
///   tag::<_, _, (_, ErrorKind)>("efg")(s)
/// };
/// let parser = |s: &'static str| {
///   delimited::<_, _, _, _, (_, ErrorKind), _, _, _>(first_parser, sep_parser, second_parser)(s)
/// };
///
/// assert_eq!(parser("abc|efg"), Ok(("", "|")));
/// assert_eq!(parser("abc|efghij"), Ok(("hij", "|")));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// # }
/// ```
pub fn delimited<I, O1, O2, O3, E: ParseError<I>, F, G, H>(first: F, sep: G, second: H) -> impl Fn(I) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  move |input: I| {
    let (input, _) = first(input)?;
    let (input, o2) = sep(input)?;
    second(input).map(|(i, _)| (i, o2))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn delimitedc<I, O1, O2, O3, E: ParseError<I>, F, G, H>(input: I, first: F, sep: G, second: H) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  delimited(first, sep, second)(input)
}

/// helper trait for the tuple combinator
///
/// this trait is implemented for tuples of parsers of up to 21 elements
pub trait Tuple<I,O,E> {
  /// parses the input and returns a tuple of results of each parser
  fn parse(&self, input: I) -> IResult<I,O,E>;
}

macro_rules! tuple_trait(
  ($name1:ident $ty1:ident, $name2: ident $ty2:ident, $($name:ident $ty:ident),*) => (
    tuple_trait!(__impl $name1 $ty1, $name2 $ty2; $($name $ty),*);
  );
  (__impl $($name:ident $ty: ident),+; $name1:ident $ty1:ident, $($name2:ident $ty2:ident),*) => (
    tuple_trait_impl!($($name $ty),+);
    tuple_trait!(__impl $($name $ty),+ , $name1 $ty1; $($name2 $ty2),*);
  );
  (__impl $($name:ident $ty: ident),+; $name1:ident $ty1:ident) => (
    tuple_trait_impl!($($name $ty),+);
    tuple_trait_impl!($($name $ty),+, $name1 $ty1);
  );
);

macro_rules! tuple_trait_impl(
  ($($name:ident $ty: ident),+) => (
    impl<
      Input: Clone, $($ty),+ , Error: ParseError<Input>,
      $($name: Fn(Input) -> IResult<Input, $ty, Error>),+
    > Tuple<Input, ( $($ty),+ ), Error> for ( $($name),+ ) {

      fn parse(&self, input: Input) -> IResult<Input, ( $($ty),+ ), Error> {
        tuple_trait_inner!(0, self, input, (), $($name)+)

      }
    }
  );
);

macro_rules! tuple_trait_inner(
  ($it:tt, $self:expr, $input:expr, (), $head:ident $($id:ident)+) => ({
    let (i, o) = $self.$it($input.clone())?;

    succ!($it, tuple_trait_inner!($self, i, ( o ), $($id)+))
  });
  ($it:tt, $self:expr, $input:expr, ($($parsed:tt)*), $head:ident $($id:ident)+) => ({
    let (i, o) = $self.$it($input.clone())?;

    succ!($it, tuple_trait_inner!($self, i, ($($parsed)* , o), $($id)+))
  });
  ($it:tt, $self:expr, $input:expr, ($($parsed:tt)*), $head:ident) => ({
    let (i, o) = $self.$it($input.clone())?;

    Ok((i, ($($parsed)* , o)))
  });
);

tuple_trait!(FnA A, FnB B, FnC C, FnD D, FnE E, FnF F, FnG G, FnH H, FnI I, FnJ J, FnK K, FnL L,
  FnM M, FnN N, FnO O, FnP P, FnQ Q, FnR R, FnS S, FnT T, FnU U);

/// applies a tuple of parsers one by one and returns their results as a tuple
pub fn tuple<I: Clone, O, E: ParseError<I>, List: Tuple<I,O,E>>(l: List)  -> impl Fn(I) -> IResult<I, O, E> {
  move |i: I| {
    l.parse(i)
  }
}
<commit_msg>simplify doc examples in sequence module<commit_after>//! combinators applying parsers in sequence

#[macro_use]
mod macros;

use internal::IResult;
use error::ParseError;

/// Gets an object from the first parser,
/// then gets another object from the second parser.
///
/// # Arguments
/// * `first` The first parser to apply.
/// * `second` The second parser to apply.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::pair;
/// use nom::bytes::complete::tag;
///
/// let parser = pair(tag("abc"), tag("efg"));
///
/// assert_eq!(parser("abcefg"), Ok(("", ("abc", "efg"))));
/// assert_eq!(parser("abcefghij"), Ok(("hij", ("abc", "efg"))));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn pair<I, O1, O2, E: ParseError<I>, F, G>(first: F, second: G) -> impl Fn(I) -> IResult<I, (O1, O2), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  move |input: I| {
    let (input, o1) = first(input)?;
    second(input).map(|(i, o2)| (i, (o1, o2)))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn pairc<I, O1, O2, E: ParseError<I>, F, G>(input: I, first: F, second: G) -> IResult<I, (O1, O2), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  pair(first, second)(input)
}

/// Matches an object from the first parser and discards it,
/// then gets an object from the second parser.
///
/// # Arguments
/// * `first` The opening parser.
/// * `second` The second parser to get object.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::preceded;
/// use nom::bytes::complete::tag;
///
/// let parser = preceded(tag("abc"), tag("efg"));
///
/// assert_eq!(parser("abcefg"), Ok(("", "efg")));
/// assert_eq!(parser("abcefghij"), Ok(("hij", "efg")));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn preceded<I, O1, O2, E: ParseError<I>, F, G>(first: F, second: G) -> impl Fn(I) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  move |input: I| {
    let (input, _) = first(input)?;
    second(input)
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn precededc<I, O1, O2, E: ParseError<I>, F, G>(input: I, first: F, second: G) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  preceded(first, second)(input)
}

/// Gets an object from the first parser,
/// then matches an object from the second parser and discards it.
///
/// # Arguments
/// * `first` The first parser to apply.
/// * `second` The second parser to match an object.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::terminated;
/// use nom::bytes::complete::tag;
///
/// let parser = terminated(tag("abc"), tag("efg"));
///
/// assert_eq!(parser("abcefg"), Ok(("", "abc")));
/// assert_eq!(parser("abcefghij"), Ok(("hij", "abc")));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn terminated<I, O1, O2, E: ParseError<I>, F, G>(first: F, second: G) -> impl Fn(I) -> IResult<I, O1, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  move |input: I| {
    let (input, o1) = first(input)?;
    second(input).map(|(i, _)| (i, o1))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn terminatedc<I, O1, O2, E: ParseError<I>, F, G>(input: I, first: F, second: G) -> IResult<I, O1, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  terminated(first, second)(input)
}

/// Gets an object from the first parser,
/// then matches an object from the sep_parser and discards it,
/// then gets another object from the second parser.
///
/// # Arguments
/// * `first` The first parser to apply.
/// * `sep` The separator parser to apply.
/// * `second` The second parser to apply.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::separated_pair;
/// use nom::bytes::complete::tag;
///
/// let parser = separated_pair(tag("abc"), tag("|"), tag("efg"));
///
/// assert_eq!(parser("abc|efg"), Ok(("", ("abc", "efg"))));
/// assert_eq!(parser("abc|efghij"), Ok(("hij", ("abc", "efg"))));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn separated_pair<I, O1, O2, O3, E: ParseError<I>, F, G, H>(first: F, sep: G, second: H) -> impl Fn(I) -> IResult<I, (O1, O3), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  move |input: I| {
    let (input, o1) = first(input)?;
    let (input, _) = sep(input)?;
    second(input).map(|(i, o2)| (i, (o1, o2)))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn separated_pairc<I, O1, O2, O3, E: ParseError<I>, F, G, H>(input: I, first: F, sep: G, second: H) -> IResult<I, (O1, O3), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  separated_pair(first, sep, second)(input)
}

/// Matches an object from the first parser,
/// then gets an object from the sep_parser,
/// then matches another object from the second parser.
///
/// # Arguments
/// * `first` The first parser to apply.
/// * `sep` The separator parser to apply.
/// * `second` The second parser to apply.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::delimited;
/// use nom::bytes::complete::tag;
///
/// let parser = delimited(tag("abc"), tag("|"), tag("efg"));
///
/// assert_eq!(parser("abc|efg"), Ok(("", "|")));
/// assert_eq!(parser("abc|efghij"), Ok(("hij", "|")));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn delimited<I, O1, O2, O3, E: ParseError<I>, F, G, H>(first: F, sep: G, second: H) -> impl Fn(I) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  move |input: I| {
    let (input, _) = first(input)?;
    let (input, o2) = sep(input)?;
    second(input).map(|(i, _)| (i, o2))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn delimitedc<I, O1, O2, O3, E: ParseError<I>, F, G, H>(input: I, first: F, sep: G, second: H) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  delimited(first, sep, second)(input)
}

/// helper trait for the tuple combinator
///
/// this trait is implemented for tuples of parsers of up to 21 elements
pub trait Tuple<I,O,E> {
  /// parses the input and returns a tuple of results of each parser
  fn parse(&self, input: I) -> IResult<I,O,E>;
}

macro_rules! tuple_trait(
  ($name1:ident $ty1:ident, $name2: ident $ty2:ident, $($name:ident $ty:ident),*) => (
    tuple_trait!(__impl $name1 $ty1, $name2 $ty2; $($name $ty),*);
  );
  (__impl $($name:ident $ty: ident),+; $name1:ident $ty1:ident, $($name2:ident $ty2:ident),*) => (
    tuple_trait_impl!($($name $ty),+);
    tuple_trait!(__impl $($name $ty),+ , $name1 $ty1; $($name2 $ty2),*);
  );
  (__impl $($name:ident $ty: ident),+; $name1:ident $ty1:ident) => (
    tuple_trait_impl!($($name $ty),+);
    tuple_trait_impl!($($name $ty),+, $name1 $ty1);
  );
);

macro_rules! tuple_trait_impl(
  ($($name:ident $ty: ident),+) => (
    impl<
      Input: Clone, $($ty),+ , Error: ParseError<Input>,
      $($name: Fn(Input) -> IResult<Input, $ty, Error>),+
    > Tuple<Input, ( $($ty),+ ), Error> for ( $($name),+ ) {

      fn parse(&self, input: Input) -> IResult<Input, ( $($ty),+ ), Error> {
        tuple_trait_inner!(0, self, input, (), $($name)+)

      }
    }
  );
);

macro_rules! tuple_trait_inner(
  ($it:tt, $self:expr, $input:expr, (), $head:ident $($id:ident)+) => ({
    let (i, o) = $self.$it($input.clone())?;

    succ!($it, tuple_trait_inner!($self, i, ( o ), $($id)+))
  });
  ($it:tt, $self:expr, $input:expr, ($($parsed:tt)*), $head:ident $($id:ident)+) => ({
    let (i, o) = $self.$it($input.clone())?;

    succ!($it, tuple_trait_inner!($self, i, ($($parsed)* , o), $($id)+))
  });
  ($it:tt, $self:expr, $input:expr, ($($parsed:tt)*), $head:ident) => ({
    let (i, o) = $self.$it($input.clone())?;

    Ok((i, ($($parsed)* , o)))
  });
);

tuple_trait!(FnA A, FnB B, FnC C, FnD D, FnE E, FnF F, FnG G, FnH H, FnI I, FnJ J, FnK K, FnL L,
  FnM M, FnN N, FnO O, FnP P, FnQ Q, FnR R, FnS S, FnT T, FnU U);

/// applies a tuple of parsers one by one and returns their results as a tuple
pub fn tuple<I: Clone, O, E: ParseError<I>, List: Tuple<I,O,E>>(l: List)  -> impl Fn(I) -> IResult<I, O, E> {
  move |i: I| {
    l.parse(i)
  }
}
<|endoftext|>


<commit_before>#![cfg(feature = "fms")]

extern crate rusoto_core;
extern crate rusoto_fms;

use rusoto_core::{Region, RusotoError};
use rusoto_fms::{Fms, FmsClient, ListPoliciesRequest};

use std::str;

#[test]
fn should_list_policies() {
    let client = FmsClient::new(Region::UsEast1);
    let request = ListPoliciesRequest::default();

    // If our account doesn't have access, assume everything is fine:
    match client.list_policies(request).sync() {
        Err(e) => match e {
            RusotoError::Unknown(ref e) => assert!(
                str::from_utf8(&e.body)
                    .unwrap()
                    .contains("is not currently delegated by AWS FM"),
                "Missing error message"
            ),
            _ => panic!("Should have a typed error from FMS"),
        },
        Ok(res) => println!("Got these policies: {:?}", res),
    }
}
<commit_msg>Fix FMS integration test.<commit_after>#![cfg(feature = "fms")]

extern crate rusoto_core;
extern crate rusoto_fms;

use rusoto_core::{Region, RusotoError};
use rusoto_fms::{Fms, FmsClient, ListPoliciesRequest};

use std::str;

#[test]
fn should_list_policies() {
    let client = FmsClient::new(Region::UsEast1);
    let request = ListPoliciesRequest {
        max_results: Some(1),
        ..Default::default()
    };

    // If our account doesn't have access, assume everything is fine:
    match client.list_policies(request).sync() {
        Err(e) => match e {
            RusotoError::Unknown(ref e) => assert!(
                str::from_utf8(&e.body)
                    .unwrap()
                    .contains("is not currently delegated by AWS FM"),
                "Missing error message"
            ),
            _ => panic!("Should have a typed error from FMS, got {:?}", e),
        },
        Ok(res) => println!("Got these policies: {:?}", res),
    }
}
<|endoftext|>


<commit_before>use crate::deriving::generic::ty::*;
use crate::deriving::generic::*;
use crate::deriving::path_std;

use rustc_ast::ptr::P;
use rustc_ast::{self as ast, Expr, MetaItem};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, DUMMY_SP};

fn make_mut_borrow(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<Expr>) -> P<Expr> {
    cx.expr(sp, ast::ExprKind::AddrOf(ast::BorrowKind::Ref, ast::Mutability::Mut, expr))
}

pub fn expand_deriving_debug(
    cx: &mut ExtCtxt<'_>,
    span: Span,
    mitem: &MetaItem,
    item: &Annotatable,
    push: &mut dyn FnMut(Annotatable),
) {
    // &mut ::std::fmt::Formatter
    let fmtr =
        Ptr(Box::new(Literal(path_std!(fmt::Formatter))), Borrowed(None, ast::Mutability::Mut));

    let trait_def = TraitDef {
        span,
        attributes: Vec::new(),
        path: path_std!(fmt::Debug),
        additional_bounds: Vec::new(),
        generics: Bounds::empty(),
        is_unsafe: false,
        supports_unions: false,
        methods: vec![MethodDef {
            name: sym::fmt,
            generics: Bounds::empty(),
            explicit_self: borrowed_explicit_self(),
            args: vec![(fmtr, sym::f)],
            ret_ty: Literal(path_std!(fmt::Result)),
            attributes: Vec::new(),
            is_unsafe: false,
            unify_fieldless_variants: false,
            combine_substructure: combine_substructure(Box::new(|a, b, c| {
                show_substructure(a, b, c)
            })),
        }],
        associated_types: Vec::new(),
    };
    trait_def.expand(cx, mitem, item, push)
}

/// We use the debug builders to do the heavy lifting here
fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
    // build fmt.debug_struct(<name>).field(<fieldname>, &<fieldval>)....build()
    // or fmt.debug_tuple(<name>).field(&<fieldval>)....build()
    // based on the "shape".
    let (ident, vdata, fields) = match substr.fields {
        Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
        EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
        EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
            cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
        }
    };

    // We want to make sure we have the ctxt set so that we can use unstable methods
    let span = cx.with_def_site_ctxt(span);
    let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked));
    let builder = Ident::new(sym::debug_trait_builder, span);
    let builder_expr = cx.expr_ident(span, builder);

    let fmt = substr.nonself_args[0].clone();

    let mut stmts = Vec::with_capacity(fields.len() + 2);
    let fn_path_finish;
    match vdata {
        ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => {
            // tuple struct/"normal" variant
            let fn_path_debug_tuple = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_tuple]);
            let expr = cx.expr_call_global(span, fn_path_debug_tuple, vec![fmt, name]);
            stmts.push(cx.stmt_let(span, true, builder, expr));

            for field in fields {
                // Use double indirection to make sure this works for unsized types
                let field = cx.expr_addr_of(field.span, field.self_.clone());
                let field = cx.expr_addr_of(field.span, field);

                let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::field]);
                let builder_recv = make_mut_borrow(cx, span, builder_expr.clone());
                let expr = cx.expr_call_global(span, fn_path_field, vec![builder_recv, field]);

                // Use `let _ = expr;` to avoid triggering the
                // unused_results lint.
                stmts.push(stmt_let_underscore(cx, span, expr));
            }

            fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::finish]);
        }
        ast::VariantData::Struct(..) => {
            // normal struct/struct variant
            let fn_path_debug_struct =
                cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_struct]);
            let expr = cx.expr_call_global(
                span, fn_path_debug_struct, vec![fmt, name]
            );
            stmts.push(cx.stmt_let(DUMMY_SP, true, builder, expr));

            for field in fields {
                let name = cx.expr_lit(
                    field.span,
                    ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked),
                );

                // Use double indirection to make sure this works for unsized types
                let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::field]);
                let field = cx.expr_addr_of(field.span, field.self_.clone());
                let field = cx.expr_addr_of(field.span, field);
                let builder_recv = make_mut_borrow(cx, span, builder_expr.clone());
                let expr = cx.expr_call_global(
                    span,
                    fn_path_field,
                    vec![builder_recv, name, field],
                );
                stmts.push(stmt_let_underscore(cx, span, expr));
            }
            fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::finish]);
        }
    }

    let builder_recv = make_mut_borrow(cx, span, builder_expr);
    let expr = cx.expr_call_global(span, fn_path_finish, vec![builder_recv]);

    stmts.push(cx.stmt_expr(expr));
    let block = cx.block(span, stmts);
    cx.expr_block(block)
}

fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> ast::Stmt {
    let local = P(ast::Local {
        pat: cx.pat_wild(sp),
        ty: None,
        init: Some(expr),
        id: ast::DUMMY_NODE_ID,
        span: sp,
        attrs: ast::AttrVec::new(),
        tokens: None,
    });
    ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp }
}
<commit_msg>placate tidy.<commit_after>use crate::deriving::generic::ty::*;
use crate::deriving::generic::*;
use crate::deriving::path_std;

use rustc_ast::ptr::P;
use rustc_ast::{self as ast, Expr, MetaItem};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, DUMMY_SP};

fn make_mut_borrow(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<Expr>) -> P<Expr> {
    cx.expr(sp, ast::ExprKind::AddrOf(ast::BorrowKind::Ref, ast::Mutability::Mut, expr))
}

pub fn expand_deriving_debug(
    cx: &mut ExtCtxt<'_>,
    span: Span,
    mitem: &MetaItem,
    item: &Annotatable,
    push: &mut dyn FnMut(Annotatable),
) {
    // &mut ::std::fmt::Formatter
    let fmtr =
        Ptr(Box::new(Literal(path_std!(fmt::Formatter))), Borrowed(None, ast::Mutability::Mut));

    let trait_def = TraitDef {
        span,
        attributes: Vec::new(),
        path: path_std!(fmt::Debug),
        additional_bounds: Vec::new(),
        generics: Bounds::empty(),
        is_unsafe: false,
        supports_unions: false,
        methods: vec![MethodDef {
            name: sym::fmt,
            generics: Bounds::empty(),
            explicit_self: borrowed_explicit_self(),
            args: vec![(fmtr, sym::f)],
            ret_ty: Literal(path_std!(fmt::Result)),
            attributes: Vec::new(),
            is_unsafe: false,
            unify_fieldless_variants: false,
            combine_substructure: combine_substructure(Box::new(|a, b, c| {
                show_substructure(a, b, c)
            })),
        }],
        associated_types: Vec::new(),
    };
    trait_def.expand(cx, mitem, item, push)
}

/// We use the debug builders to do the heavy lifting here
fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
    // build fmt.debug_struct(<name>).field(<fieldname>, &<fieldval>)....build()
    // or fmt.debug_tuple(<name>).field(&<fieldval>)....build()
    // based on the "shape".
    let (ident, vdata, fields) = match substr.fields {
        Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
        EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
        EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
            cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
        }
    };

    // We want to make sure we have the ctxt set so that we can use unstable methods
    let span = cx.with_def_site_ctxt(span);
    let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked));
    let builder = Ident::new(sym::debug_trait_builder, span);
    let builder_expr = cx.expr_ident(span, builder);

    let fmt = substr.nonself_args[0].clone();

    let mut stmts = Vec::with_capacity(fields.len() + 2);
    let fn_path_finish;
    match vdata {
        ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => {
            // tuple struct/"normal" variant
            let fn_path_debug_tuple = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_tuple]);
            let expr = cx.expr_call_global(span, fn_path_debug_tuple, vec![fmt, name]);
            stmts.push(cx.stmt_let(span, true, builder, expr));

            for field in fields {
                // Use double indirection to make sure this works for unsized types
                let field = cx.expr_addr_of(field.span, field.self_.clone());
                let field = cx.expr_addr_of(field.span, field);

                let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::field]);
                let builder_recv = make_mut_borrow(cx, span, builder_expr.clone());
                let expr = cx.expr_call_global(span, fn_path_field, vec![builder_recv, field]);

                // Use `let _ = expr;` to avoid triggering the
                // unused_results lint.
                stmts.push(stmt_let_underscore(cx, span, expr));
            }

            fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::finish]);
        }
        ast::VariantData::Struct(..) => {
            // normal struct/struct variant
            let fn_path_debug_struct = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_struct]);
            let expr = cx.expr_call_global(span, fn_path_debug_struct, vec![fmt, name]);
            stmts.push(cx.stmt_let(DUMMY_SP, true, builder, expr));

            for field in fields {
                let name = cx.expr_lit(
                    field.span,
                    ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked),
                );

                // Use double indirection to make sure this works for unsized types
                let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::field]);
                let field = cx.expr_addr_of(field.span, field.self_.clone());
                let field = cx.expr_addr_of(field.span, field);
                let builder_recv = make_mut_borrow(cx, span, builder_expr.clone());
                let expr =
                    cx.expr_call_global(span, fn_path_field, vec![builder_recv, name, field]);
                stmts.push(stmt_let_underscore(cx, span, expr));
            }
            fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::finish]);
        }
    }

    let builder_recv = make_mut_borrow(cx, span, builder_expr);
    let expr = cx.expr_call_global(span, fn_path_finish, vec![builder_recv]);

    stmts.push(cx.stmt_expr(expr));
    let block = cx.block(span, stmts);
    cx.expr_block(block)
}

fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> ast::Stmt {
    let local = P(ast::Local {
        pat: cx.pat_wild(sp),
        ty: None,
        init: Some(expr),
        id: ast::DUMMY_NODE_ID,
        span: sp,
        attrs: ast::AttrVec::new(),
        tokens: None,
    });
    ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp }
}
<|endoftext|>


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

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("Column", inherited=False) %>

${helpers.predefined_type("column-width",
                          "length::NonNegativeLengthOrAuto",
                          "Either::Second(Auto)",
                          initial_specified_value="Either::Second(Auto)",
                          extra_prefixes="moz",
                          animation_value_type="NonNegativeLengthOrAuto",
                          servo_pref="layout.columns.enabled",
                          spec="https://drafts.csswg.org/css-multicol/#propdef-column-width",
                          servo_restyle_damage="rebuild_and_reflow")}

${helpers.predefined_type(
    "column-count",
    "ColumnCount",
    "computed::ColumnCount::auto()",
    initial_specified_value="specified::ColumnCount::auto()",
    servo_pref="layout.columns.enabled",
    animation_value_type="AnimatedColumnCount",
    extra_prefixes="moz",
    spec="https://drafts.csswg.org/css-multicol/#propdef-column-count",
    servo_restyle_damage="rebuild_and_reflow",
)}

<%
col_gap_type = "NonNegativeLengthOrPercentageOrNormal"
%>
${helpers.predefined_type(
    "column-gap",
    "length::%s" % col_gap_type,
    "Either::Second(Normal)",
    extra_prefixes="moz",
    servo_pref="layout.columns.enabled",
    animation_value_type=col_gap_type,
    spec="https://drafts.csswg.org/css-multicol/#propdef-column-gap",
    servo_restyle_damage = "reflow",
)}

${helpers.single_keyword("column-fill", "balance auto", extra_prefixes="moz",
                         products="gecko", animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-multicol/#propdef-column-fill")}

${helpers.predefined_type("column-rule-width",
                          "BorderSideWidth",
                          "::values::computed::NonNegativeLength::new(3.)",
                          initial_specified_value="specified::BorderSideWidth::Medium",
                          computed_type="::values::computed::NonNegativeLength",
                          products="gecko",
                          spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width",
                          animation_value_type="NonNegativeLength",
                          extra_prefixes="moz")}

// https://drafts.csswg.org/css-multicol-1/#crc
${helpers.predefined_type(
    "column-rule-color",
    "Color",
    "computed_value::T::currentcolor()",
    initial_specified_value="specified::Color::currentcolor()",
    products="gecko",
    animation_value_type="AnimatedColor",
    extra_prefixes="moz",
    ignored_when_colors_disabled=True,
    spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color",
)}

${helpers.single_keyword("column-span", "none all",
                         products="gecko", animation_value_type="discrete",
                         gecko_pref="layout.css.column-span.enabled",
                         spec="https://drafts.csswg.org/css-multicol/#propdef-column-span",
                         extra_prefixes="moz")}

${helpers.single_keyword("column-rule-style",
                         "none hidden dotted dashed solid double groove ridge inset outset",
                         products="gecko", extra_prefixes="moz",
                         gecko_constant_prefix="NS_STYLE_BORDER_STYLE",
                         animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style")}
<commit_msg>Remove col_gap_type variable<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("Column", inherited=False) %>

${helpers.predefined_type("column-width",
                          "length::NonNegativeLengthOrAuto",
                          "Either::Second(Auto)",
                          initial_specified_value="Either::Second(Auto)",
                          extra_prefixes="moz",
                          animation_value_type="NonNegativeLengthOrAuto",
                          servo_pref="layout.columns.enabled",
                          spec="https://drafts.csswg.org/css-multicol/#propdef-column-width",
                          servo_restyle_damage="rebuild_and_reflow")}

${helpers.predefined_type(
    "column-count",
    "ColumnCount",
    "computed::ColumnCount::auto()",
    initial_specified_value="specified::ColumnCount::auto()",
    servo_pref="layout.columns.enabled",
    animation_value_type="AnimatedColumnCount",
    extra_prefixes="moz",
    spec="https://drafts.csswg.org/css-multicol/#propdef-column-count",
    servo_restyle_damage="rebuild_and_reflow",
)}

${helpers.predefined_type(
    "column-gap",
    "length::NonNegativeLengthOrPercentageOrNormal",
    "Either::Second(Normal)",
    extra_prefixes="moz",
    servo_pref="layout.columns.enabled",
    animation_value_type="NonNegativeLengthOrPercentageOrNormal",
    spec="https://drafts.csswg.org/css-multicol/#propdef-column-gap",
    servo_restyle_damage = "reflow",
)}

${helpers.single_keyword("column-fill", "balance auto", extra_prefixes="moz",
                         products="gecko", animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-multicol/#propdef-column-fill")}

${helpers.predefined_type("column-rule-width",
                          "BorderSideWidth",
                          "::values::computed::NonNegativeLength::new(3.)",
                          initial_specified_value="specified::BorderSideWidth::Medium",
                          computed_type="::values::computed::NonNegativeLength",
                          products="gecko",
                          spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width",
                          animation_value_type="NonNegativeLength",
                          extra_prefixes="moz")}

// https://drafts.csswg.org/css-multicol-1/#crc
${helpers.predefined_type(
    "column-rule-color",
    "Color",
    "computed_value::T::currentcolor()",
    initial_specified_value="specified::Color::currentcolor()",
    products="gecko",
    animation_value_type="AnimatedColor",
    extra_prefixes="moz",
    ignored_when_colors_disabled=True,
    spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color",
)}

${helpers.single_keyword("column-span", "none all",
                         products="gecko", animation_value_type="discrete",
                         gecko_pref="layout.css.column-span.enabled",
                         spec="https://drafts.csswg.org/css-multicol/#propdef-column-span",
                         extra_prefixes="moz")}

${helpers.single_keyword("column-rule-style",
                         "none hidden dotted dashed solid double groove ridge inset outset",
                         products="gecko", extra_prefixes="moz",
                         gecko_constant_prefix="NS_STYLE_BORDER_STYLE",
                         animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style")}
<|endoftext|>