<commit_before>//
// Copyright (c) Pirmin Kalberer. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//

use crate::core::feature::{Feature, FeatureAttrValType};
use crate::core::geom;
use crate::core::geom::GeometryType;
use crate::core::layer::Layer;
use crate::core::screen;
use crate::mvt::geom_encoder::{CommandSequence, EncodableGeom};
use crate::mvt::vector_tile;
use flate2::read::GzDecoder;
use flate2::write::GzEncoder;
use flate2::Compression;
use protobuf::error::ProtobufError;
use protobuf::stream::CodedOutputStream;
use protobuf::{parse_from_reader, Message};
use std::fs::File;
use std::io::{BufReader, Read, Write};
use tile_grid::Extent;

pub struct Tile<'a> {
    pub mvt_tile: vector_tile::Tile,
    extent: &'a Extent,
    reverse_y: bool,
}

impl GeometryType {
    /// GeometryType to MVT geom type
    pub fn mvt_field_type(&self) -> vector_tile::Tile_GeomType {
        match self {
            &GeometryType::Point(_) => vector_tile::Tile_GeomType::POINT,
            &GeometryType::LineString(_) => vector_tile::Tile_GeomType::LINESTRING,
            &GeometryType::Polygon(_) => vector_tile::Tile_GeomType::POLYGON,
            &GeometryType::MultiPoint(_) => vector_tile::Tile_GeomType::POINT,
            &GeometryType::MultiLineString(_) => vector_tile::Tile_GeomType::LINESTRING,
            &GeometryType::MultiPolygon(_) => vector_tile::Tile_GeomType::POLYGON,
            &GeometryType::GeometryCollection(_) => vector_tile::Tile_GeomType::UNKNOWN,
        }
    }
}

pub trait ScreenGeom<T> {
    /// Convert geometry into screen coordinates
    fn from_geom(extent: &Extent, reverse_y: bool, tile_size: u32, geom: &T) -> Self;
}

impl ScreenGeom<geom::Point> for screen::Point {
    fn from_geom(extent: &Extent, reverse_y: bool, tile_size: u32, point: &geom::Point) -> Self {
        let x_span = extent.maxx - extent.minx;
        let y_span = extent.maxy - extent.miny;
        let mut screen_geom = screen::Point {
            x: ((point.x - extent.minx) * tile_size as f64 / x_span) as i32,
            y: ((point.y - extent.miny) * tile_size as f64 / y_span) as i32,
        };
        if reverse_y {
            screen_geom.y = (tile_size as i32).saturating_sub(screen_geom.y)
        };
        screen_geom
    }
}

impl ScreenGeom<geom::MultiPoint> for screen::MultiPoint {
    fn from_geom(
        extent: &Extent,
        reverse_y: bool,
        tile_size: u32,
        multipoint: &geom::MultiPoint,
    ) -> Self {
        let mut screen_geom = screen::MultiPoint { points: Vec::new() };
        for point in &multipoint.points {
            screen_geom.points.push(screen::Point::from_geom(
                extent, reverse_y, tile_size, point,
            ));
        }
        screen_geom
    }
}

impl ScreenGeom<geom::LineString> for screen::LineString {
    fn from_geom(
        extent: &Extent,
        reverse_y: bool,
        tile_size: u32,
        line: &geom::LineString,
    ) -> Self {
        let mut screen_geom = screen::LineString { points: Vec::new() };
        for point in &line.points {
            screen_geom.points.push(screen::Point::from_geom(
                extent, reverse_y, tile_size, point,
            ));
        }
        screen_geom.points.dedup();
        screen_geom
    }
}

impl ScreenGeom<geom::MultiLineString> for screen::MultiLineString {
    fn from_geom(
        extent: &Extent,
        reverse_y: bool,
        tile_size: u32,
        multiline: &geom::MultiLineString,
    ) -> Self {
        let mut screen_geom = screen::MultiLineString { lines: Vec::new() };
        for line in &multiline.lines {
            screen_geom.lines.push(screen::LineString::from_geom(
                extent, reverse_y, tile_size, line,
            ));
        }
        screen_geom
    }
}

impl ScreenGeom<geom::Polygon> for screen::Polygon {
    fn from_geom(
        extent: &Extent,
        reverse_y: bool,
        tile_size: u32,
        polygon: &geom::Polygon,
    ) -> Self {
        let mut screen_geom = screen::Polygon { rings: Vec::new() };
        for line in &polygon.rings {
            screen_geom.rings.push(screen::LineString::from_geom(
                extent, reverse_y, tile_size, line,
            ));
        }
        screen_geom
    }
}

impl ScreenGeom<geom::MultiPolygon> for screen::MultiPolygon {
    fn from_geom(
        extent: &Extent,
        reverse_y: bool,
        tile_size: u32,
        multipolygon: &geom::MultiPolygon,
    ) -> Self {
        let mut screen_geom = screen::MultiPolygon {
            polygons: Vec::new(),
        };
        for polygon in &multipolygon.polygons {
            screen_geom.polygons.push(screen::Polygon::from_geom(
                extent, reverse_y, tile_size, polygon,
            ));
        }
        screen_geom
    }
}

// --- Tile creation functions

impl<'a> Tile<'a> {
    pub fn new(extent: &Extent, reverse_y: bool) -> Tile {
        let mvt_tile = vector_tile::Tile::new();
        Tile {
            mvt_tile: mvt_tile,
            extent: extent,
            reverse_y: reverse_y,
        }
    }

    pub fn new_layer(&mut self, layer: &Layer) -> vector_tile::Tile_Layer {
        let mut mvt_layer = vector_tile::Tile_Layer::new();
        mvt_layer.set_version(2);
        mvt_layer.set_name(layer.name.clone());
        mvt_layer.set_extent(layer.tile_size);
        mvt_layer
    }

    pub fn encode_geom(&self, geom: geom::GeometryType, tile_size: u32) -> CommandSequence {
        match geom {
            GeometryType::Point(ref g) => {
                screen::Point::from_geom(&self.extent, self.reverse_y, tile_size, g).encode()
            }
            GeometryType::MultiPoint(ref g) => {
                screen::MultiPoint::from_geom(&self.extent, self.reverse_y, tile_size, g).encode()
            }
            GeometryType::LineString(ref g) => {
                screen::LineString::from_geom(&self.extent, self.reverse_y, tile_size, g).encode()
            }
            GeometryType::MultiLineString(ref g) => {
                screen::MultiLineString::from_geom(&self.extent, self.reverse_y, tile_size, g)
                    .encode()
            }
            GeometryType::Polygon(ref g) => {
                screen::Polygon::from_geom(&self.extent, self.reverse_y, tile_size, g).encode()
            }
            GeometryType::MultiPolygon(ref g) => {
                screen::MultiPolygon::from_geom(&self.extent, self.reverse_y, tile_size, g).encode()
            }
            GeometryType::GeometryCollection(_) => panic!("GeometryCollection not supported"),
        }
    }

    pub fn add_feature_attribute(
        mvt_layer: &mut vector_tile::Tile_Layer,
        mvt_feature: &mut vector_tile::Tile_Feature,
        key: String,
        mvt_value: vector_tile::Tile_Value,
    ) {
        let keyentry = mvt_layer.get_keys().iter().position(|k| *k == key);
        // Optimization: maintain a hash table with key/index pairs
        let keyidx = match keyentry {
            None => {
                mvt_layer.mut_keys().push(key);
                mvt_layer.get_keys().len() - 1
            }
            Some(idx) => idx,
        };
        mvt_feature.mut_tags().push(keyidx as u32);

        let valentry = mvt_layer.get_values().iter().position(|v| *v == mvt_value);
        // Optimization: maintain a hash table with value/index pairs
        let validx = match valentry {
            None => {
                mvt_layer.mut_values().push(mvt_value);
                mvt_layer.get_values().len() - 1
            }
            Some(idx) => idx,
        };
        mvt_feature.mut_tags().push(validx as u32);
    }

    pub fn add_feature(&self, mut mvt_layer: &mut vector_tile::Tile_Layer, feature: &dyn Feature) {
        let mut mvt_feature = vector_tile::Tile_Feature::new();
        if let Some(fid) = feature.fid() {
            mvt_feature.set_id(fid);
        }
        'attr: for attr in feature.attributes() {
            let mut mvt_value = vector_tile::Tile_Value::new();
            match attr.value {
                FeatureAttrValType::String(ref v) => {
                    mvt_value.set_string_value(v.clone());
                }
                FeatureAttrValType::Double(v) => {
                    mvt_value.set_double_value(v);
                }
                FeatureAttrValType::Float(v) => {
                    mvt_value.set_float_value(v);
                }
                FeatureAttrValType::Int(v) => {
                    mvt_value.set_int_value(v);
                }
                FeatureAttrValType::UInt(v) => {
                    mvt_value.set_uint_value(v);
                }
                FeatureAttrValType::SInt(v) => {
                    mvt_value.set_sint_value(v);
                }
                FeatureAttrValType::Bool(v) => {
                    mvt_value.set_bool_value(v);
                }
                FeatureAttrValType::VarcharArray(v) => {
                    for array_val in v {
                        Tile::add_feature_attribute(
                            &mut mvt_layer,
                            &mut mvt_feature,
                            format!("{}.{}", attr.key.clone(), array_val),
                            mvt_value.clone(),
                        );
                    }
                    continue 'attr;
                }
            }
            Tile::add_feature_attribute(
                &mut mvt_layer,
                &mut mvt_feature,
                attr.key.clone(),
                mvt_value,
            );
        }
        if let Ok(geom) = feature.geometry() {
            let g_type = geom.mvt_field_type();
            let enc_geom = self.encode_geom(geom, mvt_layer.get_extent()).vec();
            if !enc_geom.is_empty() {
                mvt_feature.set_field_type(g_type);
                mvt_feature.set_geometry(enc_geom);
                mvt_layer.mut_features().push(mvt_feature);
            }
        }
    }

    pub fn add_layer(&mut self, mvt_layer: vector_tile::Tile_Layer) {
        self.mvt_tile.mut_layers().push(mvt_layer);
    }

    pub fn write_to(mut out: &mut dyn Write, mvt_tile: &vector_tile::Tile) {
        let mut os = CodedOutputStream::new(&mut out);
        let _ = mvt_tile.write_to(&mut os);
        os.flush().unwrap();
    }

    pub fn write_gz_to(out: &mut dyn Write, mvt_tile: &vector_tile::Tile) {
        let mut gz = GzEncoder::new(out, Compression::default());
        {
            let mut os = CodedOutputStream::new(&mut gz);
            let _ = mvt_tile.write_to(&mut os);
            os.flush().unwrap();
        }
        let _ = gz.finish();
    }

    pub fn read_from(fin: &mut dyn Read) -> Result<vector_tile::Tile, ProtobufError> {
        let mut reader = BufReader::new(fin);
        parse_from_reader::<vector_tile::Tile>(&mut reader)
    }

    pub fn read_gz_from(fin: &mut dyn Read) -> Result<vector_tile::Tile, ProtobufError> {
        let gz = GzDecoder::new(fin);
        let mut reader = BufReader::new(gz);
        parse_from_reader::<vector_tile::Tile>(&mut reader)
    }

    pub fn tile_bytevec(mvt_tile: &vector_tile::Tile) -> Vec<u8> {
        let mut v = Vec::with_capacity(mvt_tile.compute_size() as usize);
        Self::write_to(&mut v, mvt_tile);
        v
    }

    pub fn tile_bytevec_gz(mvt_tile: &vector_tile::Tile) -> Vec<u8> {
        let mut v = Vec::with_capacity(mvt_tile.compute_size() as usize);
        Self::write_gz_to(&mut v, &mvt_tile);
        v
    }

    pub fn tile_content(tilegz: Vec<u8>, gzip: bool) -> Vec<u8> {
        if gzip {
            tilegz
        } else {
            let mut gz = GzDecoder::new(&tilegz[..]);
            let mut unc_tile = Vec::with_capacity(tilegz.len());
            let _ = gz.read_to_end(&mut unc_tile);
            unc_tile
        }
    }

    pub fn to_file(&self, fname: &str) {
        let mut f = File::create(fname).unwrap();
        Self::write_to(&mut f, &self.mvt_tile);
    }

    pub fn size(mvt_tile: &vector_tile::Tile) -> u32 {
        mvt_tile.compute_size()
    }
}
<commit_msg>Replace deprecated protobuf::parse_from_reader<commit_after>//
// Copyright (c) Pirmin Kalberer. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//

use crate::core::feature::{Feature, FeatureAttrValType};
use crate::core::layer::Layer;
use crate::core::screen;
use crate::core::{geom, geom::GeometryType};
use crate::mvt::geom_encoder::{CommandSequence, EncodableGeom};
use crate::mvt::vector_tile;
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
use protobuf::{error::ProtobufError, stream::CodedOutputStream, Message};
use std::fs::File;
use std::io::{BufReader, Read, Write};
use tile_grid::Extent;

pub struct Tile<'a> {
    pub mvt_tile: vector_tile::Tile,
    extent: &'a Extent,
    reverse_y: bool,
}

impl GeometryType {
    /// GeometryType to MVT geom type
    pub fn mvt_field_type(&self) -> vector_tile::Tile_GeomType {
        match self {
            &GeometryType::Point(_) => vector_tile::Tile_GeomType::POINT,
            &GeometryType::LineString(_) => vector_tile::Tile_GeomType::LINESTRING,
            &GeometryType::Polygon(_) => vector_tile::Tile_GeomType::POLYGON,
            &GeometryType::MultiPoint(_) => vector_tile::Tile_GeomType::POINT,
            &GeometryType::MultiLineString(_) => vector_tile::Tile_GeomType::LINESTRING,
            &GeometryType::MultiPolygon(_) => vector_tile::Tile_GeomType::POLYGON,
            &GeometryType::GeometryCollection(_) => vector_tile::Tile_GeomType::UNKNOWN,
        }
    }
}

pub trait ScreenGeom<T> {
    /// Convert geometry into screen coordinates
    fn from_geom(extent: &Extent, reverse_y: bool, tile_size: u32, geom: &T) -> Self;
}

impl ScreenGeom<geom::Point> for screen::Point {
    fn from_geom(extent: &Extent, reverse_y: bool, tile_size: u32, point: &geom::Point) -> Self {
        let x_span = extent.maxx - extent.minx;
        let y_span = extent.maxy - extent.miny;
        let mut screen_geom = screen::Point {
            x: ((point.x - extent.minx) * tile_size as f64 / x_span) as i32,
            y: ((point.y - extent.miny) * tile_size as f64 / y_span) as i32,
        };
        if reverse_y {
            screen_geom.y = (tile_size as i32).saturating_sub(screen_geom.y)
        };
        screen_geom
    }
}

impl ScreenGeom<geom::MultiPoint> for screen::MultiPoint {
    fn from_geom(
        extent: &Extent,
        reverse_y: bool,
        tile_size: u32,
        multipoint: &geom::MultiPoint,
    ) -> Self {
        let mut screen_geom = screen::MultiPoint { points: Vec::new() };
        for point in &multipoint.points {
            screen_geom.points.push(screen::Point::from_geom(
                extent, reverse_y, tile_size, point,
            ));
        }
        screen_geom
    }
}

impl ScreenGeom<geom::LineString> for screen::LineString {
    fn from_geom(
        extent: &Extent,
        reverse_y: bool,
        tile_size: u32,
        line: &geom::LineString,
    ) -> Self {
        let mut screen_geom = screen::LineString { points: Vec::new() };
        for point in &line.points {
            screen_geom.points.push(screen::Point::from_geom(
                extent, reverse_y, tile_size, point,
            ));
        }
        screen_geom.points.dedup();
        screen_geom
    }
}

impl ScreenGeom<geom::MultiLineString> for screen::MultiLineString {
    fn from_geom(
        extent: &Extent,
        reverse_y: bool,
        tile_size: u32,
        multiline: &geom::MultiLineString,
    ) -> Self {
        let mut screen_geom = screen::MultiLineString { lines: Vec::new() };
        for line in &multiline.lines {
            screen_geom.lines.push(screen::LineString::from_geom(
                extent, reverse_y, tile_size, line,
            ));
        }
        screen_geom
    }
}

impl ScreenGeom<geom::Polygon> for screen::Polygon {
    fn from_geom(
        extent: &Extent,
        reverse_y: bool,
        tile_size: u32,
        polygon: &geom::Polygon,
    ) -> Self {
        let mut screen_geom = screen::Polygon { rings: Vec::new() };
        for line in &polygon.rings {
            screen_geom.rings.push(screen::LineString::from_geom(
                extent, reverse_y, tile_size, line,
            ));
        }
        screen_geom
    }
}

impl ScreenGeom<geom::MultiPolygon> for screen::MultiPolygon {
    fn from_geom(
        extent: &Extent,
        reverse_y: bool,
        tile_size: u32,
        multipolygon: &geom::MultiPolygon,
    ) -> Self {
        let mut screen_geom = screen::MultiPolygon {
            polygons: Vec::new(),
        };
        for polygon in &multipolygon.polygons {
            screen_geom.polygons.push(screen::Polygon::from_geom(
                extent, reverse_y, tile_size, polygon,
            ));
        }
        screen_geom
    }
}

// --- Tile creation functions

impl<'a> Tile<'a> {
    pub fn new(extent: &Extent, reverse_y: bool) -> Tile {
        let mvt_tile = vector_tile::Tile::new();
        Tile {
            mvt_tile: mvt_tile,
            extent: extent,
            reverse_y: reverse_y,
        }
    }

    pub fn new_layer(&mut self, layer: &Layer) -> vector_tile::Tile_Layer {
        let mut mvt_layer = vector_tile::Tile_Layer::new();
        mvt_layer.set_version(2);
        mvt_layer.set_name(layer.name.clone());
        mvt_layer.set_extent(layer.tile_size);
        mvt_layer
    }

    pub fn encode_geom(&self, geom: geom::GeometryType, tile_size: u32) -> CommandSequence {
        match geom {
            GeometryType::Point(ref g) => {
                screen::Point::from_geom(&self.extent, self.reverse_y, tile_size, g).encode()
            }
            GeometryType::MultiPoint(ref g) => {
                screen::MultiPoint::from_geom(&self.extent, self.reverse_y, tile_size, g).encode()
            }
            GeometryType::LineString(ref g) => {
                screen::LineString::from_geom(&self.extent, self.reverse_y, tile_size, g).encode()
            }
            GeometryType::MultiLineString(ref g) => {
                screen::MultiLineString::from_geom(&self.extent, self.reverse_y, tile_size, g)
                    .encode()
            }
            GeometryType::Polygon(ref g) => {
                screen::Polygon::from_geom(&self.extent, self.reverse_y, tile_size, g).encode()
            }
            GeometryType::MultiPolygon(ref g) => {
                screen::MultiPolygon::from_geom(&self.extent, self.reverse_y, tile_size, g).encode()
            }
            GeometryType::GeometryCollection(_) => panic!("GeometryCollection not supported"),
        }
    }

    pub fn add_feature_attribute(
        mvt_layer: &mut vector_tile::Tile_Layer,
        mvt_feature: &mut vector_tile::Tile_Feature,
        key: String,
        mvt_value: vector_tile::Tile_Value,
    ) {
        let keyentry = mvt_layer.get_keys().iter().position(|k| *k == key);
        // Optimization: maintain a hash table with key/index pairs
        let keyidx = match keyentry {
            None => {
                mvt_layer.mut_keys().push(key);
                mvt_layer.get_keys().len() - 1
            }
            Some(idx) => idx,
        };
        mvt_feature.mut_tags().push(keyidx as u32);

        let valentry = mvt_layer.get_values().iter().position(|v| *v == mvt_value);
        // Optimization: maintain a hash table with value/index pairs
        let validx = match valentry {
            None => {
                mvt_layer.mut_values().push(mvt_value);
                mvt_layer.get_values().len() - 1
            }
            Some(idx) => idx,
        };
        mvt_feature.mut_tags().push(validx as u32);
    }

    pub fn add_feature(&self, mut mvt_layer: &mut vector_tile::Tile_Layer, feature: &dyn Feature) {
        let mut mvt_feature = vector_tile::Tile_Feature::new();
        if let Some(fid) = feature.fid() {
            mvt_feature.set_id(fid);
        }
        'attr: for attr in feature.attributes() {
            let mut mvt_value = vector_tile::Tile_Value::new();
            match attr.value {
                FeatureAttrValType::String(ref v) => {
                    mvt_value.set_string_value(v.clone());
                }
                FeatureAttrValType::Double(v) => {
                    mvt_value.set_double_value(v);
                }
                FeatureAttrValType::Float(v) => {
                    mvt_value.set_float_value(v);
                }
                FeatureAttrValType::Int(v) => {
                    mvt_value.set_int_value(v);
                }
                FeatureAttrValType::UInt(v) => {
                    mvt_value.set_uint_value(v);
                }
                FeatureAttrValType::SInt(v) => {
                    mvt_value.set_sint_value(v);
                }
                FeatureAttrValType::Bool(v) => {
                    mvt_value.set_bool_value(v);
                }
                FeatureAttrValType::VarcharArray(v) => {
                    for array_val in v {
                        Tile::add_feature_attribute(
                            &mut mvt_layer,
                            &mut mvt_feature,
                            format!("{}.{}", attr.key.clone(), array_val),
                            mvt_value.clone(),
                        );
                    }
                    continue 'attr;
                }
            }
            Tile::add_feature_attribute(
                &mut mvt_layer,
                &mut mvt_feature,
                attr.key.clone(),
                mvt_value,
            );
        }
        if let Ok(geom) = feature.geometry() {
            let g_type = geom.mvt_field_type();
            let enc_geom = self.encode_geom(geom, mvt_layer.get_extent()).vec();
            if !enc_geom.is_empty() {
                mvt_feature.set_field_type(g_type);
                mvt_feature.set_geometry(enc_geom);
                mvt_layer.mut_features().push(mvt_feature);
            }
        }
    }

    pub fn add_layer(&mut self, mvt_layer: vector_tile::Tile_Layer) {
        self.mvt_tile.mut_layers().push(mvt_layer);
    }

    pub fn write_to(mut out: &mut dyn Write, mvt_tile: &vector_tile::Tile) {
        let mut os = CodedOutputStream::new(&mut out);
        let _ = mvt_tile.write_to(&mut os);
        os.flush().unwrap();
    }

    pub fn write_gz_to(out: &mut dyn Write, mvt_tile: &vector_tile::Tile) {
        let mut gz = GzEncoder::new(out, Compression::default());
        {
            let mut os = CodedOutputStream::new(&mut gz);
            let _ = mvt_tile.write_to(&mut os);
            os.flush().unwrap();
        }
        let _ = gz.finish();
    }

    pub fn read_from(fin: &mut dyn Read) -> Result<vector_tile::Tile, ProtobufError> {
        let mut reader = BufReader::new(fin);
        vector_tile::Tile::parse_from_reader(&mut reader)
    }

    pub fn read_gz_from(fin: &mut dyn Read) -> Result<vector_tile::Tile, ProtobufError> {
        let gz = GzDecoder::new(fin);
        let mut reader = BufReader::new(gz);
        vector_tile::Tile::parse_from_reader(&mut reader)
    }

    pub fn tile_bytevec(mvt_tile: &vector_tile::Tile) -> Vec<u8> {
        let mut v = Vec::with_capacity(mvt_tile.compute_size() as usize);
        Self::write_to(&mut v, mvt_tile);
        v
    }

    pub fn tile_bytevec_gz(mvt_tile: &vector_tile::Tile) -> Vec<u8> {
        let mut v = Vec::with_capacity(mvt_tile.compute_size() as usize);
        Self::write_gz_to(&mut v, &mvt_tile);
        v
    }

    pub fn tile_content(tilegz: Vec<u8>, gzip: bool) -> Vec<u8> {
        if gzip {
            tilegz
        } else {
            let mut gz = GzDecoder::new(&tilegz[..]);
            let mut unc_tile = Vec::with_capacity(tilegz.len());
            let _ = gz.read_to_end(&mut unc_tile);
            unc_tile
        }
    }

    pub fn to_file(&self, fname: &str) {
        let mut f = File::create(fname).unwrap();
        Self::write_to(&mut f, &self.mvt_tile);
    }

    pub fn size(mvt_tile: &vector_tile::Tile) -> u32 {
        mvt_tile.compute_size()
    }
}
<|endoftext|>


<commit_before><commit_msg>change QueryPlan to contain a borrow of the IndexInfo instead of a clone<commit_after><|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/. */

use std::cell::Cell;
use std::iter::Iterator;
use std::ascii::AsciiExt;
use url::Url;

use encoding::EncodingRef;

use cssparser::{Parser, decode_stylesheet_bytes,
                QualifiedRuleParser, AtRuleParser, RuleListParser, AtRuleType};
use string_cache::{Atom, Namespace};
use selectors::parser::{Selector, parse_selector_list};
use parser::{ParserContext, log_css_error};
use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
use media_queries::{self, Device, MediaQueryList, parse_media_query_list};
use font_face::{FontFaceRule, Source, parse_font_face_block, iter_font_face_rules_inner};

#[derive(Clone, PartialEq, Eq, Copy, Debug)]
pub enum Origin {
    UserAgent,
    Author,
    User,
}

#[derive(Debug, PartialEq)]
pub struct Stylesheet {
    /// List of rules in the order they were found (important for
    /// cascading order)
    rules: Vec<CSSRule>,
    pub origin: Origin,
}

#[derive(Debug, PartialEq)]
pub enum CSSRule {
    Charset(String),
    Namespace(Option<String>, Namespace),
    Style(StyleRule),
    Media(MediaRule),
    FontFace(FontFaceRule),
}

#[derive(Debug, PartialEq)]
pub struct MediaRule {
    pub media_queries: MediaQueryList,
    pub rules: Vec<CSSRule>,
}

#[derive(Debug, PartialEq)]
pub struct StyleRule {
    pub selectors: Vec<Selector>,
    pub declarations: PropertyDeclarationBlock,
}

impl Stylesheet {
    pub fn from_bytes_iter<I: Iterator<Item=Vec<u8>>>(
            input: I, base_url: Url, protocol_encoding_label: Option<&str>,
            environment_encoding: Option<EncodingRef>, origin: Origin) -> Stylesheet {
        let mut bytes = vec![];
        // TODO: incremental decoding and tokenization/parsing
        for chunk in input {
            bytes.push_all(&chunk)
        }
        Stylesheet::from_bytes(&bytes, base_url, protocol_encoding_label,
                               environment_encoding, origin)
    }

    pub fn from_bytes(bytes: &[u8],
                      base_url: Url,
                      protocol_encoding_label: Option<&str>,
                      environment_encoding: Option<EncodingRef>,
                      origin: Origin)
                      -> Stylesheet {
        // TODO: bytes.as_slice could be bytes.container_as_bytes()
        let (string, _) = decode_stylesheet_bytes(
            bytes, protocol_encoding_label, environment_encoding);
        Stylesheet::from_str(&string, base_url, origin)
    }

    pub fn from_str<'i>(css: &'i str, base_url: Url, origin: Origin) -> Stylesheet {
        let rule_parser = TopLevelRuleParser {
            context: ParserContext::new(origin, &base_url),
            state: Cell::new(State::Start),
        };
        let mut input = Parser::new(css);
        let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser);
        let mut rules = Vec::new();
        while let Some(result) = iter.next() {
            match result {
                Ok(rule) => {
                    if let CSSRule::Namespace(ref prefix, ref namespace) = rule {
                        if let Some(prefix) = prefix.as_ref() {
                            iter.parser.context.selector_context.namespace_prefixes.insert(
                                prefix.clone(), namespace.clone());
                        } else {
                            iter.parser.context.selector_context.default_namespace =
                                Some(namespace.clone());
                        }
                    }
                    rules.push(rule);
                }
                Err(range) => {
                    let pos = range.start;
                    let message = format!("Invalid rule: '{}'", iter.input.slice(range));
                    log_css_error(iter.input, pos, &*message);
                }
            }
        }
        Stylesheet {
            origin: origin,
            rules: rules,
        }
    }
}

fn parse_nested_rules(context: &ParserContext, input: &mut Parser) -> Vec<CSSRule> {
    let mut iter = RuleListParser::new_for_nested_rule(input, NestedRuleParser { context: context });
    let mut rules = Vec::new();
    while let Some(result) = iter.next() {
        match result {
            Ok(rule) => rules.push(rule),
            Err(range) => {
                let pos = range.start;
                let message = format!("Unsupported rule: '{}'", iter.input.slice(range));
                log_css_error(iter.input, pos, &*message);
            }
        }
    }
    rules
}

struct TopLevelRuleParser<'a> {
    context: ParserContext<'a>,
    state: Cell<State>,
}

#[derive(Eq, PartialEq, Ord, PartialOrd, Copy)]
enum State {
    Start = 1,
    Imports = 2,
    Namespaces = 3,
    Body = 4,
}

enum AtRulePrelude {
    FontFace,
    Media(MediaQueryList),
}

impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
    type Prelude = AtRulePrelude;
    type AtRule = CSSRule;

    fn parse_prelude(&self, name: &str, input: &mut Parser)
                     -> Result<AtRuleType<AtRulePrelude, CSSRule>, ()> {
        match_ignore_ascii_case! { name,
            "charset" => {
                if self.state.get() <= State::Start {
                    // Valid @charset rules are just ignored
                    self.state.set(State::Imports);
                    let charset = try!(input.expect_string()).into_owned();
                    return Ok(AtRuleType::WithoutBlock(CSSRule::Charset(charset)))
                } else {
                    return Err(())  // "@charset must be the first rule"
                }
            },
            "import" => {
                if self.state.get() <= State::Imports {
                    self.state.set(State::Imports);
                    // TODO: support @import
                    return Err(())  // "@import is not supported yet"
                } else {
                    return Err(())  // "@import must be before any rule but @charset"
                }
            },
            "namespace" => {
                if self.state.get() <= State::Namespaces {
                    self.state.set(State::Namespaces);

                    let prefix = input.try(|input| input.expect_ident()).ok().map(|p| p.into_owned());
                    let url = Namespace(Atom::from_slice(&*try!(input.expect_url_or_string())));
                    return Ok(AtRuleType::WithoutBlock(CSSRule::Namespace(prefix, url)))
                } else {
                    return Err(())  // "@namespace must be before any rule but @charset and @import"
                }
            }
            _ => {}
        }

        self.state.set(State::Body);
        AtRuleParser::parse_prelude(&NestedRuleParser { context: &self.context }, name, input)
    }

    #[inline]
    fn parse_block(&self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CSSRule, ()> {
        AtRuleParser::parse_block(&NestedRuleParser { context: &self.context }, prelude, input)
    }
}

impl<'a> QualifiedRuleParser for TopLevelRuleParser<'a> {
    type Prelude = Vec<Selector>;
    type QualifiedRule = CSSRule;

    #[inline]
    fn parse_prelude(&self, input: &mut Parser) -> Result<Vec<Selector>, ()> {
        self.state.set(State::Body);
        QualifiedRuleParser::parse_prelude(&NestedRuleParser { context: &self.context }, input)
    }

    #[inline]
    fn parse_block(&self, prelude: Vec<Selector>, input: &mut Parser) -> Result<CSSRule, ()> {
        QualifiedRuleParser::parse_block(&NestedRuleParser { context: &self.context },
                                         prelude, input)
    }
}

struct NestedRuleParser<'a, 'b: 'a> {
    context: &'a ParserContext<'b>,
}

impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
    type Prelude = AtRulePrelude;
    type AtRule = CSSRule;

    fn parse_prelude(&self, name: &str, input: &mut Parser)
                     -> Result<AtRuleType<AtRulePrelude, CSSRule>, ()> {
        match_ignore_ascii_case! { name,
            "media" => {
                let media_queries = parse_media_query_list(input);
                Ok(AtRuleType::WithBlock(AtRulePrelude::Media(media_queries)))
            },
            "font-face" => {
                Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace))
            }
            _ => Err(())
        }
    }

    fn parse_block(&self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CSSRule, ()> {
        match prelude {
            AtRulePrelude::FontFace => {
                parse_font_face_block(self.context, input).map(CSSRule::FontFace)
            }
            AtRulePrelude::Media(media_queries) => {
                Ok(CSSRule::Media(MediaRule {
                    media_queries: media_queries,
                    rules: parse_nested_rules(self.context, input),
                }))
            }
        }
    }
}

impl<'a, 'b> QualifiedRuleParser for NestedRuleParser<'a, 'b> {
    type Prelude = Vec<Selector>;
    type QualifiedRule = CSSRule;

    fn parse_prelude(&self, input: &mut Parser) -> Result<Vec<Selector>, ()> {
        parse_selector_list(&self.context.selector_context, input)
    }

    fn parse_block(&self, prelude: Vec<Selector>, input: &mut Parser) -> Result<CSSRule, ()> {
        Ok(CSSRule::Style(StyleRule {
            selectors: prelude,
            declarations: parse_property_declaration_list(self.context, input)
        }))
    }
}

pub fn iter_style_rules<'a, F>(rules: &[CSSRule], device: &media_queries::Device,
                               callback: &mut F) where F: FnMut(&StyleRule) {
    for rule in rules.iter() {
        match *rule {
            CSSRule::Style(ref rule) => callback(rule),
            CSSRule::Media(ref rule) => if rule.media_queries.evaluate(device) {
                iter_style_rules(&rule.rules, device, callback)
            },
            CSSRule::FontFace(..) |
            CSSRule::Charset(..) |
            CSSRule::Namespace(..) => {}
        }
    }
}

pub fn iter_stylesheet_media_rules<F>(stylesheet: &Stylesheet, mut callback: F) where F: FnMut(&MediaRule) {
    for rule in stylesheet.rules.iter() {
        match *rule {
            CSSRule::Media(ref rule) => callback(rule),
            CSSRule::Style(..) |
            CSSRule::FontFace(..) |
            CSSRule::Charset(..) |
            CSSRule::Namespace(..) => {}
        }
    }
}

#[inline]
pub fn iter_stylesheet_style_rules<F>(stylesheet: &Stylesheet, device: &media_queries::Device,
                                      mut callback: F) where F: FnMut(&StyleRule) {
    iter_style_rules(&stylesheet.rules, device, &mut callback)
}

#[inline]
pub fn iter_font_face_rules<F>(stylesheet: &Stylesheet, device: &Device,
                               callback: &F) where F: Fn(&str, &Source) {
    iter_font_face_rules_inner(&stylesheet.rules, device, callback)
}

#[test]
fn test_parse_stylesheet() {
    use std::sync::Arc;
    use cssparser;
    use selectors::parser::*;
    use string_cache::Atom;
    use properties::{PropertyDeclaration, DeclaredValue, longhands};
    use std::borrow::ToOwned;

    let css = r"
        @namespace url(http://www.w3.org/1999/xhtml);
        /* FIXME: only if scripting is enabled */
        input[type=hidden i] { display: none !important; }
        html , body /**/ { display: block; }
        #d1 > .ok { background: blue; }
    ";
    let url = Url::parse("about::test").unwrap();
    let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent);
    assert_eq!(stylesheet, Stylesheet {
        origin: Origin::UserAgent,
        rules: vec![
            CSSRule::Namespace(None, ns!(HTML)),
            CSSRule::Style(StyleRule {
                selectors: vec![
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Namespace(ns!(HTML)),
                                SimpleSelector::LocalName(LocalName {
                                    name: atom!(input),
                                    lower_name: atom!(input),
                                }),
                                SimpleSelector::AttrEqual(AttrSelector {
                                    name: atom!(type),
                                    lower_name: atom!(type),
                                    namespace: NamespaceConstraint::Specific(ns!("")),
                                }, "hidden".to_owned(), CaseSensitivity::CaseInsensitive)
                            ],
                            next: None,
                        }),
                        pseudo_element: None,
                        specificity: (0 << 20) + (1 << 10) + (1 << 0),
                    },
                ],
                declarations: PropertyDeclarationBlock {
                    normal: Arc::new(vec![]),
                    important: Arc::new(vec![
                        PropertyDeclaration::Display(DeclaredValue::SpecifiedValue(
                            longhands::display::SpecifiedValue::none)),
                    ]),
                },
            }),
            CSSRule::Style(StyleRule {
                selectors: vec![
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Namespace(ns!(HTML)),
                                SimpleSelector::LocalName(LocalName {
                                    name: atom!(html),
                                    lower_name: atom!(html),
                                }),
                            ],
                            next: None,
                        }),
                        pseudo_element: None,
                        specificity: (0 << 20) + (0 << 10) + (1 << 0),
                    },
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Namespace(ns!(HTML)),
                                SimpleSelector::LocalName(LocalName {
                                    name: atom!(body),
                                    lower_name: atom!(body),
                                }),
                            ],
                            next: None,
                        }),
                        pseudo_element: None,
                        specificity: (0 << 20) + (0 << 10) + (1 << 0),
                    },
                ],
                declarations: PropertyDeclarationBlock {
                    normal: Arc::new(vec![
                        PropertyDeclaration::Display(DeclaredValue::SpecifiedValue(
                            longhands::display::SpecifiedValue::block)),
                    ]),
                    important: Arc::new(vec![]),
                },
            }),
            CSSRule::Style(StyleRule {
                selectors: vec![
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Class(Atom::from_slice("ok")),
                            ],
                            next: Some((box CompoundSelector {
                                simple_selectors: vec![
                                    SimpleSelector::ID(Atom::from_slice("d1")),
                                ],
                                next: None,
                            }, Combinator::Child)),
                        }),
                        pseudo_element: None,
                        specificity: (1 << 20) + (1 << 10) + (0 << 0),
                    },
                ],
                declarations: PropertyDeclarationBlock {
                    normal: Arc::new(vec![
                        PropertyDeclaration::BackgroundImage(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundAttachment(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundRepeat(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundPosition(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundColor(DeclaredValue::SpecifiedValue(
                            longhands::background_color::SpecifiedValue {
                                authored: Some("blue".to_owned()),
                                parsed: cssparser::Color::RGBA(cssparser::RGBA {
                                    red: 0., green: 0., blue: 1., alpha: 1.
                                }),
                            }
                        )),
                    ]),
                    important: Arc::new(vec![]),
                },
            }),
        ],
    });
}
<commit_msg>Fix up libstyle unit test for background-size.<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/. */

use std::cell::Cell;
use std::iter::Iterator;
use std::ascii::AsciiExt;
use url::Url;

use encoding::EncodingRef;

use cssparser::{Parser, decode_stylesheet_bytes,
                QualifiedRuleParser, AtRuleParser, RuleListParser, AtRuleType};
use string_cache::{Atom, Namespace};
use selectors::parser::{Selector, parse_selector_list};
use parser::{ParserContext, log_css_error};
use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
use media_queries::{self, Device, MediaQueryList, parse_media_query_list};
use font_face::{FontFaceRule, Source, parse_font_face_block, iter_font_face_rules_inner};

#[derive(Clone, PartialEq, Eq, Copy, Debug)]
pub enum Origin {
    UserAgent,
    Author,
    User,
}

#[derive(Debug, PartialEq)]
pub struct Stylesheet {
    /// List of rules in the order they were found (important for
    /// cascading order)
    rules: Vec<CSSRule>,
    pub origin: Origin,
}

#[derive(Debug, PartialEq)]
pub enum CSSRule {
    Charset(String),
    Namespace(Option<String>, Namespace),
    Style(StyleRule),
    Media(MediaRule),
    FontFace(FontFaceRule),
}

#[derive(Debug, PartialEq)]
pub struct MediaRule {
    pub media_queries: MediaQueryList,
    pub rules: Vec<CSSRule>,
}

#[derive(Debug, PartialEq)]
pub struct StyleRule {
    pub selectors: Vec<Selector>,
    pub declarations: PropertyDeclarationBlock,
}

impl Stylesheet {
    pub fn from_bytes_iter<I: Iterator<Item=Vec<u8>>>(
            input: I, base_url: Url, protocol_encoding_label: Option<&str>,
            environment_encoding: Option<EncodingRef>, origin: Origin) -> Stylesheet {
        let mut bytes = vec![];
        // TODO: incremental decoding and tokenization/parsing
        for chunk in input {
            bytes.push_all(&chunk)
        }
        Stylesheet::from_bytes(&bytes, base_url, protocol_encoding_label,
                               environment_encoding, origin)
    }

    pub fn from_bytes(bytes: &[u8],
                      base_url: Url,
                      protocol_encoding_label: Option<&str>,
                      environment_encoding: Option<EncodingRef>,
                      origin: Origin)
                      -> Stylesheet {
        // TODO: bytes.as_slice could be bytes.container_as_bytes()
        let (string, _) = decode_stylesheet_bytes(
            bytes, protocol_encoding_label, environment_encoding);
        Stylesheet::from_str(&string, base_url, origin)
    }

    pub fn from_str<'i>(css: &'i str, base_url: Url, origin: Origin) -> Stylesheet {
        let rule_parser = TopLevelRuleParser {
            context: ParserContext::new(origin, &base_url),
            state: Cell::new(State::Start),
        };
        let mut input = Parser::new(css);
        let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser);
        let mut rules = Vec::new();
        while let Some(result) = iter.next() {
            match result {
                Ok(rule) => {
                    if let CSSRule::Namespace(ref prefix, ref namespace) = rule {
                        if let Some(prefix) = prefix.as_ref() {
                            iter.parser.context.selector_context.namespace_prefixes.insert(
                                prefix.clone(), namespace.clone());
                        } else {
                            iter.parser.context.selector_context.default_namespace =
                                Some(namespace.clone());
                        }
                    }
                    rules.push(rule);
                }
                Err(range) => {
                    let pos = range.start;
                    let message = format!("Invalid rule: '{}'", iter.input.slice(range));
                    log_css_error(iter.input, pos, &*message);
                }
            }
        }
        Stylesheet {
            origin: origin,
            rules: rules,
        }
    }
}

fn parse_nested_rules(context: &ParserContext, input: &mut Parser) -> Vec<CSSRule> {
    let mut iter = RuleListParser::new_for_nested_rule(input, NestedRuleParser { context: context });
    let mut rules = Vec::new();
    while let Some(result) = iter.next() {
        match result {
            Ok(rule) => rules.push(rule),
            Err(range) => {
                let pos = range.start;
                let message = format!("Unsupported rule: '{}'", iter.input.slice(range));
                log_css_error(iter.input, pos, &*message);
            }
        }
    }
    rules
}

struct TopLevelRuleParser<'a> {
    context: ParserContext<'a>,
    state: Cell<State>,
}

#[derive(Eq, PartialEq, Ord, PartialOrd, Copy)]
enum State {
    Start = 1,
    Imports = 2,
    Namespaces = 3,
    Body = 4,
}

enum AtRulePrelude {
    FontFace,
    Media(MediaQueryList),
}

impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
    type Prelude = AtRulePrelude;
    type AtRule = CSSRule;

    fn parse_prelude(&self, name: &str, input: &mut Parser)
                     -> Result<AtRuleType<AtRulePrelude, CSSRule>, ()> {
        match_ignore_ascii_case! { name,
            "charset" => {
                if self.state.get() <= State::Start {
                    // Valid @charset rules are just ignored
                    self.state.set(State::Imports);
                    let charset = try!(input.expect_string()).into_owned();
                    return Ok(AtRuleType::WithoutBlock(CSSRule::Charset(charset)))
                } else {
                    return Err(())  // "@charset must be the first rule"
                }
            },
            "import" => {
                if self.state.get() <= State::Imports {
                    self.state.set(State::Imports);
                    // TODO: support @import
                    return Err(())  // "@import is not supported yet"
                } else {
                    return Err(())  // "@import must be before any rule but @charset"
                }
            },
            "namespace" => {
                if self.state.get() <= State::Namespaces {
                    self.state.set(State::Namespaces);

                    let prefix = input.try(|input| input.expect_ident()).ok().map(|p| p.into_owned());
                    let url = Namespace(Atom::from_slice(&*try!(input.expect_url_or_string())));
                    return Ok(AtRuleType::WithoutBlock(CSSRule::Namespace(prefix, url)))
                } else {
                    return Err(())  // "@namespace must be before any rule but @charset and @import"
                }
            }
            _ => {}
        }

        self.state.set(State::Body);
        AtRuleParser::parse_prelude(&NestedRuleParser { context: &self.context }, name, input)
    }

    #[inline]
    fn parse_block(&self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CSSRule, ()> {
        AtRuleParser::parse_block(&NestedRuleParser { context: &self.context }, prelude, input)
    }
}

impl<'a> QualifiedRuleParser for TopLevelRuleParser<'a> {
    type Prelude = Vec<Selector>;
    type QualifiedRule = CSSRule;

    #[inline]
    fn parse_prelude(&self, input: &mut Parser) -> Result<Vec<Selector>, ()> {
        self.state.set(State::Body);
        QualifiedRuleParser::parse_prelude(&NestedRuleParser { context: &self.context }, input)
    }

    #[inline]
    fn parse_block(&self, prelude: Vec<Selector>, input: &mut Parser) -> Result<CSSRule, ()> {
        QualifiedRuleParser::parse_block(&NestedRuleParser { context: &self.context },
                                         prelude, input)
    }
}

struct NestedRuleParser<'a, 'b: 'a> {
    context: &'a ParserContext<'b>,
}

impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
    type Prelude = AtRulePrelude;
    type AtRule = CSSRule;

    fn parse_prelude(&self, name: &str, input: &mut Parser)
                     -> Result<AtRuleType<AtRulePrelude, CSSRule>, ()> {
        match_ignore_ascii_case! { name,
            "media" => {
                let media_queries = parse_media_query_list(input);
                Ok(AtRuleType::WithBlock(AtRulePrelude::Media(media_queries)))
            },
            "font-face" => {
                Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace))
            }
            _ => Err(())
        }
    }

    fn parse_block(&self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CSSRule, ()> {
        match prelude {
            AtRulePrelude::FontFace => {
                parse_font_face_block(self.context, input).map(CSSRule::FontFace)
            }
            AtRulePrelude::Media(media_queries) => {
                Ok(CSSRule::Media(MediaRule {
                    media_queries: media_queries,
                    rules: parse_nested_rules(self.context, input),
                }))
            }
        }
    }
}

impl<'a, 'b> QualifiedRuleParser for NestedRuleParser<'a, 'b> {
    type Prelude = Vec<Selector>;
    type QualifiedRule = CSSRule;

    fn parse_prelude(&self, input: &mut Parser) -> Result<Vec<Selector>, ()> {
        parse_selector_list(&self.context.selector_context, input)
    }

    fn parse_block(&self, prelude: Vec<Selector>, input: &mut Parser) -> Result<CSSRule, ()> {
        Ok(CSSRule::Style(StyleRule {
            selectors: prelude,
            declarations: parse_property_declaration_list(self.context, input)
        }))
    }
}

pub fn iter_style_rules<'a, F>(rules: &[CSSRule], device: &media_queries::Device,
                               callback: &mut F) where F: FnMut(&StyleRule) {
    for rule in rules.iter() {
        match *rule {
            CSSRule::Style(ref rule) => callback(rule),
            CSSRule::Media(ref rule) => if rule.media_queries.evaluate(device) {
                iter_style_rules(&rule.rules, device, callback)
            },
            CSSRule::FontFace(..) |
            CSSRule::Charset(..) |
            CSSRule::Namespace(..) => {}
        }
    }
}

pub fn iter_stylesheet_media_rules<F>(stylesheet: &Stylesheet, mut callback: F) where F: FnMut(&MediaRule) {
    for rule in stylesheet.rules.iter() {
        match *rule {
            CSSRule::Media(ref rule) => callback(rule),
            CSSRule::Style(..) |
            CSSRule::FontFace(..) |
            CSSRule::Charset(..) |
            CSSRule::Namespace(..) => {}
        }
    }
}

#[inline]
pub fn iter_stylesheet_style_rules<F>(stylesheet: &Stylesheet, device: &media_queries::Device,
                                      mut callback: F) where F: FnMut(&StyleRule) {
    iter_style_rules(&stylesheet.rules, device, &mut callback)
}

#[inline]
pub fn iter_font_face_rules<F>(stylesheet: &Stylesheet, device: &Device,
                               callback: &F) where F: Fn(&str, &Source) {
    iter_font_face_rules_inner(&stylesheet.rules, device, callback)
}

#[test]
fn test_parse_stylesheet() {
    use std::sync::Arc;
    use cssparser;
    use selectors::parser::*;
    use string_cache::Atom;
    use properties::{PropertyDeclaration, DeclaredValue, longhands};
    use std::borrow::ToOwned;

    let css = r"
        @namespace url(http://www.w3.org/1999/xhtml);
        /* FIXME: only if scripting is enabled */
        input[type=hidden i] { display: none !important; }
        html , body /**/ { display: block; }
        #d1 > .ok { background: blue; }
    ";
    let url = Url::parse("about::test").unwrap();
    let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent);
    assert_eq!(stylesheet, Stylesheet {
        origin: Origin::UserAgent,
        rules: vec![
            CSSRule::Namespace(None, ns!(HTML)),
            CSSRule::Style(StyleRule {
                selectors: vec![
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Namespace(ns!(HTML)),
                                SimpleSelector::LocalName(LocalName {
                                    name: atom!(input),
                                    lower_name: atom!(input),
                                }),
                                SimpleSelector::AttrEqual(AttrSelector {
                                    name: atom!(type),
                                    lower_name: atom!(type),
                                    namespace: NamespaceConstraint::Specific(ns!("")),
                                }, "hidden".to_owned(), CaseSensitivity::CaseInsensitive)
                            ],
                            next: None,
                        }),
                        pseudo_element: None,
                        specificity: (0 << 20) + (1 << 10) + (1 << 0),
                    },
                ],
                declarations: PropertyDeclarationBlock {
                    normal: Arc::new(vec![]),
                    important: Arc::new(vec![
                        PropertyDeclaration::Display(DeclaredValue::SpecifiedValue(
                            longhands::display::SpecifiedValue::none)),
                    ]),
                },
            }),
            CSSRule::Style(StyleRule {
                selectors: vec![
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Namespace(ns!(HTML)),
                                SimpleSelector::LocalName(LocalName {
                                    name: atom!(html),
                                    lower_name: atom!(html),
                                }),
                            ],
                            next: None,
                        }),
                        pseudo_element: None,
                        specificity: (0 << 20) + (0 << 10) + (1 << 0),
                    },
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Namespace(ns!(HTML)),
                                SimpleSelector::LocalName(LocalName {
                                    name: atom!(body),
                                    lower_name: atom!(body),
                                }),
                            ],
                            next: None,
                        }),
                        pseudo_element: None,
                        specificity: (0 << 20) + (0 << 10) + (1 << 0),
                    },
                ],
                declarations: PropertyDeclarationBlock {
                    normal: Arc::new(vec![
                        PropertyDeclaration::Display(DeclaredValue::SpecifiedValue(
                            longhands::display::SpecifiedValue::block)),
                    ]),
                    important: Arc::new(vec![]),
                },
            }),
            CSSRule::Style(StyleRule {
                selectors: vec![
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Class(Atom::from_slice("ok")),
                            ],
                            next: Some((box CompoundSelector {
                                simple_selectors: vec![
                                    SimpleSelector::ID(Atom::from_slice("d1")),
                                ],
                                next: None,
                            }, Combinator::Child)),
                        }),
                        pseudo_element: None,
                        specificity: (1 << 20) + (1 << 10) + (0 << 0),
                    },
                ],
                declarations: PropertyDeclarationBlock {
                    normal: Arc::new(vec![
                        PropertyDeclaration::BackgroundSize(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundImage(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundAttachment(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundRepeat(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundPosition(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundColor(DeclaredValue::SpecifiedValue(
                            longhands::background_color::SpecifiedValue {
                                authored: Some("blue".to_owned()),
                                parsed: cssparser::Color::RGBA(cssparser::RGBA {
                                    red: 0., green: 0., blue: 1., alpha: 1.
                                }),
                            }
                        )),
                    ]),
                    important: Arc::new(vec![]),
                },
            }),
        ],
    });
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Code for using the `x86` and `x86_64` segmentation hardware.
//!
//! For more information, refer to the _Intel® 64 and IA-32 Architectures
//! Software Developer’s Manual_, Vol. 3A, section 3.2, "Using Segments".
//! Some of the documentation present in this module was taken from the Intel
//! manual.
use core::{fmt, mem};
use super::PrivilegeLevel;

bitflags! {
    /// A segment selector is a 16-bit identifier for a segment.
    ///
    /// It does not point directly to the segment, but instead points to the
    /// segment descriptor that defines the segment.
    ///
    /// A segment selector contains the following items:
    ///
    /// + *Requested Privilege Level (RPL)*: bits 0 and 1.
    ///    Specifies the privelege level of the selector.
    /// + *Table Indicator*: bit 2. Specifies which descriptor table to use.
    /// + *Index*: bits 3 through 15. Selects one of 8192 descriptors in the
    ///    GDT or LDT. The processor multiplies the index value by 8 (the number
    ///    of bytes in a segment descriptor) and adds the result to the base
    ///    address of the GDT or LDT (from the `%gdtr` or `%ldtr` register,
    ///    respectively).
    pub flags Selector: u16 { const RPL_RING_0 = 0b00
                            , const RPL_RING_1 = 0b01
                            , const RPL_RING_2 = 0b10
                            , const RPL_RING_3 = 0b11

                            , /// Requested Prrivelege Level (RPL)
                              const RPL = RPL_RING_0.bits
                               | RPL_RING_1.bits
                               | RPL_RING_2.bits
                               | RPL_RING_3.bits

                            , /// If the Table Indicator (TI) is 0, use the GDT
                              const TI_GDT = 0 << 3

                            , /// If the TI is 1, use the LDT
                              const TI_LDT = 1 << 3
                            }
}

impl Selector {
    /// Create a new `Selector`
    ///
    /// # Arguments:
    ///   - `index`: the index in the GDT or LDT
    pub const fn new(index: u16) -> Self {
        Selector { bits: index << 3 }
    }

    /// Create a new `Selector` from raw bits
    pub const fn from_raw(bits: u16) -> Self {
        Selector { bits: bits }
    }

    /// Returns the current value of the code segment register.
    pub fn from_cs() -> Self {
        let cs: u16;
        unsafe {
            asm!( "mov $0, cs"
                : "=r"(cs)
                ::: "intel" )
        };
        Selector::from_bits_truncate(cs)
    }

    /// Extracts the index from a segment selector
    #[inline] pub fn index(&self) -> u16 {
        self.bits >> 3
    }

    /// Sets this segment selector to be a GDT segment.
    ///
    /// If the segment is already a GDT segment, this will quietly do nothing.
    #[inline] pub fn set_global(&mut self) -> &mut Self {
        self.remove(TI_LDT);
        self
    }

    /// Sets this segment selector to be an LDT segment.
    ///
    /// If the segment is already an LDT segment, this will quietly do nothing.
    #[inline] pub fn set_local(&mut self) -> &mut Self {
        self.insert(TI_GDT);
        self
    }

    /// Sets the Requested Priveliege Level (RPL)
    ///
    /// The RPL must be in the range between 0 and 3.
    #[inline] pub fn set_rpl(&mut self, rpl: PrivilegeLevel) -> &mut Self {
        self.bits &= rpl as u16;
        self
    }

    /// Checks the segment's privelige.
    #[inline] pub fn get_rpl(&self) -> PrivilegeLevel {
        unsafe { mem::transmute(*self & RPL) }
    }

    /// Load this selector into the stack segment register (`ss`).
    pub unsafe fn load_ss(&self) {
        asm!(  "mov ss, $0"
            :: "r"(*self)
            :  "memory"
            :  "intel");
    }

    /// Load this selector into the data segment register (`ds`).
    pub unsafe fn load_ds(&self) {
        asm!(  "mov ds, $0"
            :: "r"(*self)
            :  "memory"
            :  "intel");
    }

    /// Load this selector into the `es` segment register.
    pub unsafe fn load_es(&self) {
        asm!(  "mov es, $0"
            :: "r"(*self)
            :  "memory"
            :  "intel");
    }

    /// Load this selector into the `fs` segment register.
    pub unsafe fn load_fs(&self) {
        asm!(  "mov fs, $0"
            :: "r"(*self)
            :  "memory"
            :  "intel");
    }

    /// Load this selector into the `gs` segment register.
    pub unsafe fn load_gs(&self) {
        asm!(  "mov gs, $0"
            :: "r"(*self)
            :  "memory"
            :  "intel");
    }

    /// Load this selector into the code segment register.
    ///
    /// N.B. that as we cannot `mov` directly to `cs`, we have to do this
    /// differently. We push the selector and return value onto the stack,
    /// and use `lret` to reload `cs`.
    #[cfg(target_arch = "x86_64")]
    pub unsafe fn load_cs(&self) {
        asm!(  "push $0
                lea rax, [rip + 1]
                push rax
                iret
                1:"
            :: "r"(self.bits as u64)
            :  "rax", "memory"
            :  "intel");
    }

}

impl fmt::Display for Selector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // TODO: this could be much less ugly.
        let ring = if self.contains(RPL_RING_3) { "3" }
                   else if self.contains(RPL_RING_2) { "2" }
                   else if self.contains(RPL_RING_1) { "1" }
                   else if self.contains(RPL_RING_0) { "0" }
                   else { unreachable!() };
        let table = if self.contains(TI_GDT) { "GDT" }
                    else { "LDT" };
        write!(f, "Index {} in {} at Ring {}", self.index(), table, ring)
    }
}

/// A segment descriptor is an entry in an IDT or GDT.
///
/// A segment descriptor is a data structure in a GDT or LDT that provides the
/// processor with the size and location of a segment, as well as access control
/// and status information. Segment descriptors are typically created by
/// compilers, linkers, loaders, or the operating system or executive, but not
/// application programs.
///
#[repr(C, packed)]
pub struct Descriptor { /// The last 8 bits of the base address
                        pub base_high: u8
                      , /// The next 16 bits are bitflags
                        pub flags: Flags
                      , /// The middle 8 bits of the base address
                        pub base_mid: u8
                      , /// The first 16 bits of the base address
                        pub base_low: u16
                      , /// the segment limit
                        pub limit: u16
                      }

impl Descriptor {

    /// Constructs a new null `Descriptor`
    pub const fn null() -> Self {
        Descriptor { base_high: 0
                   , flags: Flags::null()
                   , base_mid: 0
                   , base_low: 0
                   , limit: 0
                   }
    }

    /// Constructs a new `Descriptor` from a `limit` and a `base` address
    pub fn new(base: u32, limit: u32) -> Self {
        let (hi, mid, lo): (u8, u8, u16) = unsafe { mem::transmute(base) };
        let (limit_lo, limit_hi): (u16, u16) = unsafe { mem::transmute(limit) };
        // I hope this is right...
        let flags = (limit_hi & 0b1111) << 8;

        Descriptor { base_high: hi
                   , flags: Flags::from_bits_truncate(flags)
                   , base_mid: mid
                   , base_low: lo
                   , limit: limit_lo
                   }
    }

    /// Extract the limit part from the flags and limit fields.
    #[inline]
    pub fn get_limit(&self) -> u32 {
        // TODO: i hope this is right...
        self.flags.get_limit_part() & self.limit as u32
    }

}

bitflags! {
    pub flags Flags: u16 {
        /// 1 if this is a code or data segment that has been accessed
        const CODE_DATA_ACC = 1 << 0
      , const SEGMENT_TYPE  = 0b0000_0000_0000_1111
      , const DESCR_TYPE    = 1 << 4
      , const DPL           = 0b0000_0000_0110_0000
      , const PRESENT       = 1 << 7
      , const LIMIT         = 0b0000_1111_0000_0000
      , const AVAILABLE     = 1 << 12
      , const LENGTH        = 1 << 13
      , const DEFAULT_SIZE  = 1 << 14
      , const GRANULARITY   = 1 << 15
      , /// If this is a code or data segment and the accessed bit is set,
        /// it has been accessed.
        const ACCESSED      = DESCR_TYPE.bits & CODE_DATA_ACC.bits
    }
}

impl Flags {

    const fn null() -> Self {
        Flags { bits: 0 }
    }

    /// Get the Descriptor Privilege Level (DPL) from the flags
    #[inline] pub fn get_dpl(&self) -> PrivilegeLevel {
        unsafe { mem::transmute((*self & DPL).bits >> 5) }
    }

    /// Returns true if this segment is a system segment.
    ///
    /// Returns false if it is a code or data segment.
    #[inline] pub fn is_system(&self) -> bool {
        !self.contains(DESCR_TYPE)
    }

    /// Returns false if this segment is present
    #[inline] pub fn is_present(&self) -> bool {
        !self.contains(PRESENT)
    }

    /// Returns false if this segment is available to system software
    #[inline] pub fn is_available(&self) -> bool {
        self.contains(AVAILABLE)
    }

    /// Returns true if this is a code or data segment that has been accessed.
    ///
    /// Returns false if it has not been accessed OR if it is a system segment.
    #[inline] pub fn is_accessed(&self) -> bool {
        self.contains(ACCESSED)
    }

    /// Returns the system type indicator, if this is a system segment.
    ///
    /// # Returns
    /// + `Some(SysType)` if this is a system segment
    /// + `None` if this is a code or data segment
    pub fn get_system_type(&self) -> Option<SysType> {
        if self.is_system() {
            Some(unsafe { mem::transmute((*self & SEGMENT_TYPE).bits) })
        } else {
            None
        }
    }

    /// Returns the code type indicator.
    ///
    /// # Returns
    /// + `Some(CodeType)` if this is not a system segment
    /// + `None` if this is a system segment
    pub fn get_code_type(&self) -> Option<CodeFlags> {
        if self.is_system() {
            None
        } else {
            Some(CodeFlags::from_bits_truncate(self.bits))
        }
    }

    /// Returns the data type indicator.
    ///
    /// # Returns
    /// + `Some(DataType)` if this is not a system segment
    /// + `None` if this is a system segment
    pub fn get_data_type(&self) -> Option<DataFlags> {
        if self.is_system() {
            None
        } else {
            Some(DataFlags::from_bits_truncate(self.bits))
        }
    }

    #[inline]
    fn get_limit_part(&self) -> u32 {
        ((*self & LIMIT).bits as u32) << 8
    }

}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Type { System(SysType)
              , Code(CodeFlags)
              , Data(DataFlags)
              }

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(u16)]
pub enum SysType { Ldt           = 0b0010
                 , TssAvailable  = 0b1001
                 , TssBusy       = 0b1011
                 , CallGate      = 0b1100
                 , InterruptGate = 0b1110
                 , TrapGate      = 0b1111
                 }

bitflags! {
    pub flags DataFlags: u16 {
        const DATA_ACCESSED = 0b0001
      , const WRITE         = 0b0010
      , const EXPAND_DOWN   = 0b0100
    }
}

impl DataFlags {
    /// Returns true if the data segment is read-only
    #[inline] pub fn is_read_only(&self) -> bool {
        !self.contains(WRITE)
    }

    /// Returns true if the data segment has been accessed
    #[inline] pub fn is_accessed(&self) -> bool {
        self.contains(DATA_ACCESSED)
    }

    /// Returns true if the data segment expands down
    #[inline] pub fn is_expand_down(&self) -> bool {
        self.contains(EXPAND_DOWN)
    }
}

bitflags! {
    pub flags CodeFlags: u16 {
        const CODE_ACCESSED = 0b0001
      , const READ          = 0b0010
      , const EXECUTE       = 0b1000
      , const CONFORMING    = 0b0100
      , const EXEC_ONLY     = EXECUTE.bits & !READ.bits
    }
}

impl CodeFlags {
    /// Returns true if the code segment is execute-only (not readable)
    #[inline] pub fn is_exec_only(&self) -> bool {
        self.contains(EXEC_ONLY)
    }

    /// Returns true if the code segment is readable
    #[inline] pub fn is_readable(&self) -> bool {
        self.contains(READ)
    }

    /// Returns true if the code segment has been accessed.
    #[inline] pub fn is_accessed(&self) -> bool {
        self.contains(CODE_ACCESSED)
    }

    /// Returns true if the code segment is conforming.
    #[inline] pub fn is_conforming(&self) -> bool {
        self.contains(CONFORMING)
    }
}
<commit_msg>[x86_all] make segment selector formatting somewhat less verbose<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Code for using the `x86` and `x86_64` segmentation hardware.
//!
//! For more information, refer to the _Intel® 64 and IA-32 Architectures
//! Software Developer’s Manual_, Vol. 3A, section 3.2, "Using Segments".
//! Some of the documentation present in this module was taken from the Intel
//! manual.
use core::{fmt, mem};
use super::PrivilegeLevel;

bitflags! {
    /// A segment selector is a 16-bit identifier for a segment.
    ///
    /// It does not point directly to the segment, but instead points to the
    /// segment descriptor that defines the segment.
    ///
    /// A segment selector contains the following items:
    ///
    /// + *Requested Privilege Level (RPL)*: bits 0 and 1.
    ///    Specifies the privelege level of the selector.
    /// + *Table Indicator*: bit 2. Specifies which descriptor table to use.
    /// + *Index*: bits 3 through 15. Selects one of 8192 descriptors in the
    ///    GDT or LDT. The processor multiplies the index value by 8 (the number
    ///    of bytes in a segment descriptor) and adds the result to the base
    ///    address of the GDT or LDT (from the `%gdtr` or `%ldtr` register,
    ///    respectively).
    pub flags Selector: u16 { const RPL_RING_0 = 0b00
                            , const RPL_RING_1 = 0b01
                            , const RPL_RING_2 = 0b10
                            , const RPL_RING_3 = 0b11

                            , /// Requested Prrivelege Level (RPL)
                              const RPL = RPL_RING_0.bits
                               | RPL_RING_1.bits
                               | RPL_RING_2.bits
                               | RPL_RING_3.bits

                            , /// If the Table Indicator (TI) is 0, use the GDT
                              const TI_GDT = 0 << 3

                            , /// If the TI is 1, use the LDT
                              const TI_LDT = 1 << 3
                            }
}

impl Selector {
    /// Create a new `Selector`
    ///
    /// # Arguments:
    ///   - `index`: the index in the GDT or LDT
    pub const fn new(index: u16) -> Self {
        Selector { bits: index << 3 }
    }

    /// Create a new `Selector` from raw bits
    pub const fn from_raw(bits: u16) -> Self {
        Selector { bits: bits }
    }

    /// Returns the current value of the code segment register.
    pub fn from_cs() -> Self {
        let cs: u16;
        unsafe {
            asm!( "mov $0, cs"
                : "=r"(cs)
                ::: "intel" )
        };
        Selector::from_bits_truncate(cs)
    }

    /// Extracts the index from a segment selector
    #[inline] pub fn index(&self) -> u16 {
        self.bits >> 3
    }

    /// Sets this segment selector to be a GDT segment.
    ///
    /// If the segment is already a GDT segment, this will quietly do nothing.
    #[inline] pub fn set_global(&mut self) -> &mut Self {
        self.remove(TI_LDT);
        self
    }

    /// Sets this segment selector to be an LDT segment.
    ///
    /// If the segment is already an LDT segment, this will quietly do nothing.
    #[inline] pub fn set_local(&mut self) -> &mut Self {
        self.insert(TI_GDT);
        self
    }

    /// Sets the Requested Priveliege Level (RPL)
    ///
    /// The RPL must be in the range between 0 and 3.
    #[inline] pub fn set_rpl(&mut self, rpl: PrivilegeLevel) -> &mut Self {
        self.bits &= rpl as u16;
        self
    }

    /// Checks the segment's privelige.
    #[inline] pub fn get_rpl(&self) -> PrivilegeLevel {
        unsafe { mem::transmute(*self & RPL) }
    }

    /// Load this selector into the stack segment register (`ss`).
    pub unsafe fn load_ss(&self) {
        asm!(  "mov ss, $0"
            :: "r"(*self)
            :  "memory"
            :  "intel");
    }

    /// Load this selector into the data segment register (`ds`).
    pub unsafe fn load_ds(&self) {
        asm!(  "mov ds, $0"
            :: "r"(*self)
            :  "memory"
            :  "intel");
    }

    /// Load this selector into the `es` segment register.
    pub unsafe fn load_es(&self) {
        asm!(  "mov es, $0"
            :: "r"(*self)
            :  "memory"
            :  "intel");
    }

    /// Load this selector into the `fs` segment register.
    pub unsafe fn load_fs(&self) {
        asm!(  "mov fs, $0"
            :: "r"(*self)
            :  "memory"
            :  "intel");
    }

    /// Load this selector into the `gs` segment register.
    pub unsafe fn load_gs(&self) {
        asm!(  "mov gs, $0"
            :: "r"(*self)
            :  "memory"
            :  "intel");
    }

    /// Load this selector into the code segment register.
    ///
    /// N.B. that as we cannot `mov` directly to `cs`, we have to do this
    /// differently. We push the selector and return value onto the stack,
    /// and use `lret` to reload `cs`.
    #[cfg(target_arch = "x86_64")]
    pub unsafe fn load_cs(&self) {
        asm!(  "push $0
                lea rax, [rip + 1]
                push rax
                iret
                1:"
            :: "r"(self.bits as u64)
            :  "rax", "memory"
            :  "intel");
    }

}

impl fmt::Display for Selector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // TODO: this could be much less ugly.
        let ring = if self.contains(RPL_RING_3) { "3" }
                   else if self.contains(RPL_RING_2) { "2" }
                   else if self.contains(RPL_RING_1) { "1" }
                   else if self.contains(RPL_RING_0) { "0" }
                   else { unreachable!() };
        let table = if self.contains(TI_GDT) { "GDT" }
                    else { "LDT" };
        write!(f, "{}[{}], Ring {}", table, self.index(), ring)
    }
}

/// A segment descriptor is an entry in an IDT or GDT.
///
/// A segment descriptor is a data structure in a GDT or LDT that provides the
/// processor with the size and location of a segment, as well as access control
/// and status information. Segment descriptors are typically created by
/// compilers, linkers, loaders, or the operating system or executive, but not
/// application programs.
///
#[repr(C, packed)]
pub struct Descriptor { /// The last 8 bits of the base address
                        pub base_high: u8
                      , /// The next 16 bits are bitflags
                        pub flags: Flags
                      , /// The middle 8 bits of the base address
                        pub base_mid: u8
                      , /// The first 16 bits of the base address
                        pub base_low: u16
                      , /// the segment limit
                        pub limit: u16
                      }

impl Descriptor {

    /// Constructs a new null `Descriptor`
    pub const fn null() -> Self {
        Descriptor { base_high: 0
                   , flags: Flags::null()
                   , base_mid: 0
                   , base_low: 0
                   , limit: 0
                   }
    }

    /// Constructs a new `Descriptor` from a `limit` and a `base` address
    pub fn new(base: u32, limit: u32) -> Self {
        let (hi, mid, lo): (u8, u8, u16) = unsafe { mem::transmute(base) };
        let (limit_lo, limit_hi): (u16, u16) = unsafe { mem::transmute(limit) };
        // I hope this is right...
        let flags = (limit_hi & 0b1111) << 8;

        Descriptor { base_high: hi
                   , flags: Flags::from_bits_truncate(flags)
                   , base_mid: mid
                   , base_low: lo
                   , limit: limit_lo
                   }
    }

    /// Extract the limit part from the flags and limit fields.
    #[inline]
    pub fn get_limit(&self) -> u32 {
        // TODO: i hope this is right...
        self.flags.get_limit_part() & self.limit as u32
    }

}

bitflags! {
    pub flags Flags: u16 {
        /// 1 if this is a code or data segment that has been accessed
        const CODE_DATA_ACC = 1 << 0
      , const SEGMENT_TYPE  = 0b0000_0000_0000_1111
      , const DESCR_TYPE    = 1 << 4
      , const DPL           = 0b0000_0000_0110_0000
      , const PRESENT       = 1 << 7
      , const LIMIT         = 0b0000_1111_0000_0000
      , const AVAILABLE     = 1 << 12
      , const LENGTH        = 1 << 13
      , const DEFAULT_SIZE  = 1 << 14
      , const GRANULARITY   = 1 << 15
      , /// If this is a code or data segment and the accessed bit is set,
        /// it has been accessed.
        const ACCESSED      = DESCR_TYPE.bits & CODE_DATA_ACC.bits
    }
}

impl Flags {

    const fn null() -> Self {
        Flags { bits: 0 }
    }

    /// Get the Descriptor Privilege Level (DPL) from the flags
    #[inline] pub fn get_dpl(&self) -> PrivilegeLevel {
        unsafe { mem::transmute((*self & DPL).bits >> 5) }
    }

    /// Returns true if this segment is a system segment.
    ///
    /// Returns false if it is a code or data segment.
    #[inline] pub fn is_system(&self) -> bool {
        !self.contains(DESCR_TYPE)
    }

    /// Returns false if this segment is present
    #[inline] pub fn is_present(&self) -> bool {
        !self.contains(PRESENT)
    }

    /// Returns false if this segment is available to system software
    #[inline] pub fn is_available(&self) -> bool {
        self.contains(AVAILABLE)
    }

    /// Returns true if this is a code or data segment that has been accessed.
    ///
    /// Returns false if it has not been accessed OR if it is a system segment.
    #[inline] pub fn is_accessed(&self) -> bool {
        self.contains(ACCESSED)
    }

    /// Returns the system type indicator, if this is a system segment.
    ///
    /// # Returns
    /// + `Some(SysType)` if this is a system segment
    /// + `None` if this is a code or data segment
    pub fn get_system_type(&self) -> Option<SysType> {
        if self.is_system() {
            Some(unsafe { mem::transmute((*self & SEGMENT_TYPE).bits) })
        } else {
            None
        }
    }

    /// Returns the code type indicator.
    ///
    /// # Returns
    /// + `Some(CodeType)` if this is not a system segment
    /// + `None` if this is a system segment
    pub fn get_code_type(&self) -> Option<CodeFlags> {
        if self.is_system() {
            None
        } else {
            Some(CodeFlags::from_bits_truncate(self.bits))
        }
    }

    /// Returns the data type indicator.
    ///
    /// # Returns
    /// + `Some(DataType)` if this is not a system segment
    /// + `None` if this is a system segment
    pub fn get_data_type(&self) -> Option<DataFlags> {
        if self.is_system() {
            None
        } else {
            Some(DataFlags::from_bits_truncate(self.bits))
        }
    }

    #[inline]
    fn get_limit_part(&self) -> u32 {
        ((*self & LIMIT).bits as u32) << 8
    }

}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Type { System(SysType)
              , Code(CodeFlags)
              , Data(DataFlags)
              }

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(u16)]
pub enum SysType { Ldt           = 0b0010
                 , TssAvailable  = 0b1001
                 , TssBusy       = 0b1011
                 , CallGate      = 0b1100
                 , InterruptGate = 0b1110
                 , TrapGate      = 0b1111
                 }

bitflags! {
    pub flags DataFlags: u16 {
        const DATA_ACCESSED = 0b0001
      , const WRITE         = 0b0010
      , const EXPAND_DOWN   = 0b0100
    }
}

impl DataFlags {
    /// Returns true if the data segment is read-only
    #[inline] pub fn is_read_only(&self) -> bool {
        !self.contains(WRITE)
    }

    /// Returns true if the data segment has been accessed
    #[inline] pub fn is_accessed(&self) -> bool {
        self.contains(DATA_ACCESSED)
    }

    /// Returns true if the data segment expands down
    #[inline] pub fn is_expand_down(&self) -> bool {
        self.contains(EXPAND_DOWN)
    }
}

bitflags! {
    pub flags CodeFlags: u16 {
        const CODE_ACCESSED = 0b0001
      , const READ          = 0b0010
      , const EXECUTE       = 0b1000
      , const CONFORMING    = 0b0100
      , const EXEC_ONLY     = EXECUTE.bits & !READ.bits
    }
}

impl CodeFlags {
    /// Returns true if the code segment is execute-only (not readable)
    #[inline] pub fn is_exec_only(&self) -> bool {
        self.contains(EXEC_ONLY)
    }

    /// Returns true if the code segment is readable
    #[inline] pub fn is_readable(&self) -> bool {
        self.contains(READ)
    }

    /// Returns true if the code segment has been accessed.
    #[inline] pub fn is_accessed(&self) -> bool {
        self.contains(CODE_ACCESSED)
    }

    /// Returns true if the code segment is conforming.
    #[inline] pub fn is_conforming(&self) -> bool {
        self.contains(CONFORMING)
    }
}
<|endoftext|>