<commit_before>use crate::{model::Msg, node::FIELD_CLASSES};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::{
    collections::{BTreeMap, HashMap},
    convert::TryInto,
    ops::Deref,
    rc::Rc,
};
use wasm_bindgen::{JsCast, UnwrapThrowExt};
use web_sys::{HtmlInputElement, HtmlTextAreaElement, InputEvent};
use yew::{html, prelude::*, Html};

pub type Ref = String;

pub type Hash = String;

pub fn new_ref() -> Ref {
    uuid::Uuid::new_v4().to_hyphenated().to_string()
}

#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
pub struct Selector {
    pub field_id: usize,
    pub index: usize,
}

pub type Path = Vec<Selector>;

pub fn append(path: &[Selector], selector: Selector) -> Path {
    let mut new_path = path.to_vec();
    new_path.push(selector);
    new_path
}

pub fn parent(path: &[Selector]) -> &[Selector] {
    if path.is_empty() {
        path
    } else {
        path.split_last().unwrap().1
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Mode {
    Normal,
    Edit,
}

pub fn hash(value: &[u8]) -> Hash {
    let bytes: [u8; 32] = Sha256::digest(&value).try_into().unwrap();
    hex::encode(bytes)
}

pub fn hash_node(node: &Node) -> Hash {
    let node_json = serde_json::to_string_pretty(node).unwrap();
    let node_bytes = node_json.as_bytes();
    hash(node_bytes)
}

#[derive(Default, PartialEq, Clone)]
pub struct NodeState {
    // TODO: Errors.
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct NodeStore {
    pub nodes: HashMap<Hash, Node>,
    pub root: Hash,
    pub log: Vec<(Ref, Node)>,
}

#[derive(Clone, Debug)]
pub struct Cursor {
    pub parent: Option<(Box<Cursor>, Selector)>,
    pub hash: Hash,
}

impl Cursor {
    pub fn next(&self, node_store: &NodeStore) -> Option<Cursor> {
        todo!()
    }
    pub fn prev(&self, node_store: &NodeStore) -> Option<Cursor> {
        todo!()
    }
    pub fn parent(&self, _node_store: &NodeStore) -> Option<Cursor> {
        self.parent
            .as_ref()
            .map(|(parent_cursor, _selector)| (**parent_cursor).clone())
    }
    pub fn traverse(&self, node_store: &NodeStore, path: &[Selector]) -> Option<Cursor> {
        match path.split_first() {
            Some((selector, rest)) => {
                // child_hash may or may not be valid at this point.
                let child_hash = self
                    .node(node_store)?
                    .links
                    .get(&selector.field_id)?
                    .get(selector.index)?;
                let child = Cursor {
                    parent: Some((Box::new(self.clone()), selector.clone())),
                    hash: child_hash.clone(),
                };
                child.traverse(node_store, rest)
            }
            None => Some(self.clone()),
        }
    }
    pub fn node<'a>(&self, node_store: &'a NodeStore) -> Option<&'a Node> {
        node_store.lookup_hash(&self.hash)
    }
    pub fn path(&self) -> Vec<Selector> {
        match &self.parent {
            Some((parent_cursor, selector)) => {
                let mut path = parent_cursor.path();
                path.push(selector.clone());
                path
            }
            None => vec![],
        }
    }
}

impl PartialEq for NodeStore {
    fn eq(&self, other: &Self) -> bool {
        // Only compare the size of the hashmap, since it is effectively append-only.
        self.nodes.len() == other.nodes.len()
            && self.root == other.root
            && self.log.len() == other.log.len()
    }
}

impl NodeStore {
    pub fn root(&self) -> Cursor {
        Cursor {
            parent: None,
            hash: self.root.clone(),
        }
    }

    pub fn path(&self, path: &[Selector]) -> Option<Cursor> {
        self.root().traverse(self, path)
    }

    // TODO: remove.
    pub fn lookup_hash(&self, hash: &Hash) -> Option<&Node> {
        self.nodes.get(hash)
    }

    #[must_use]
    pub fn add_node(&mut self, node: &Node) -> Hash {
        let h = hash_node(node);
        self.nodes.insert(h.clone(), node.clone());
        h
    }

    #[must_use]
    pub fn replace_node(&mut self, path: &[Selector], node: &Node) -> Option<Hash> {
        self.replace_node_from(&self.root.clone(), path, node)
    }

    #[must_use]
    fn replace_node_from(&mut self, base: &Hash, path: &[Selector], node: &Node) -> Option<Hash> {
        if path.is_empty() {
            Some(self.add_node(node))
        } else {
            let mut base = self.nodes.get(base)?.clone();
            let selector = path[0].clone();
            match base
                .links
                .get(&selector.field_id)
                .and_then(|v| v.get(selector.index))
            {
                Some(old_child_hash) => {
                    let new_child_hash =
                        self.replace_node_from(old_child_hash, &path[1..], node)?;
                    base.links.get_mut(&selector.field_id)?[selector.index] = new_child_hash;
                }
                None => {
                    // WARN: Only works for one level of children.
                    let new_child_hash = self.add_node(node);
                    base.links
                        .entry(selector.field_id)
                        .or_default()
                        .push(new_child_hash);
                }
            };
            Some(self.add_node(&base))
        }
    }
}

// TODO: Navigate to children directly, but use :var to navigate to variables, otherwise skip them
// when navigating.
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct Node {
    // UUID.
    pub kind: String,
    pub value: String,
    // Keyed by field id.
    pub links: BTreeMap<usize, Vec<Hash>>,
}

pub fn display_selector(selector: &Selector) -> Html {
    display_selector_text(&format!("{}", selector.field_id), selector.index)
}

pub fn display_selector_text(field_name: &str, index: usize) -> Html {
    // TODO: lookup.
    html! {

          { field_name }
          { format!("{}", index) }

    }
}

pub fn display_cursor(cursor: &Path) -> Html {
    let segments = cursor
        .iter()
        .map(display_selector)
        .intersperse(html! { { ">" }});
    html! {

{ for segments }

    }
}

pub struct Action {
    pub image: Option<String>,
    pub text: String,
    pub msg: Msg,
}

pub fn get_value_from_input_event(e: InputEvent) -> String {
    let event: Event = e.dyn_into().unwrap_throw();
    let event_target = event.target().unwrap_throw();
    if let Ok(target) = event_target.clone().dyn_into::<HtmlInputElement>() {
        return target.value();
    }
    if let Ok(target) = event_target.dyn_into::<HtmlTextAreaElement>() {
        return target.value();
    }
    panic!("unexpected input event");
    // let target: HtmlTextareaElement = event_target.dyn_into().unwrap_throw();
    // web_sys::console::log_1(&target.value().into());
    // target.value()
}
<commit_msg>Next traversal<commit_after>use crate::{model::Msg, node::FIELD_CLASSES};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::{
    collections::{BTreeMap, HashMap},
    convert::TryInto,
    ops::Deref,
    rc::Rc,
};
use wasm_bindgen::{JsCast, UnwrapThrowExt};
use web_sys::{HtmlInputElement, HtmlTextAreaElement, InputEvent};
use yew::{html, prelude::*, Html};

pub type Ref = String;

pub type Hash = String;

pub fn new_ref() -> Ref {
    uuid::Uuid::new_v4().to_hyphenated().to_string()
}

#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
pub struct Selector {
    pub field_id: usize,
    pub index: usize,
}

pub type Path = Vec<Selector>;

pub fn append(path: &[Selector], selector: Selector) -> Path {
    let mut new_path = path.to_vec();
    new_path.push(selector);
    new_path
}

pub fn parent(path: &[Selector]) -> &[Selector] {
    if path.is_empty() {
        path
    } else {
        path.split_last().unwrap().1
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Mode {
    Normal,
    Edit,
}

pub fn hash(value: &[u8]) -> Hash {
    let bytes: [u8; 32] = Sha256::digest(&value).try_into().unwrap();
    hex::encode(bytes)
}

pub fn hash_node(node: &Node) -> Hash {
    let node_json = serde_json::to_string_pretty(node).unwrap();
    let node_bytes = node_json.as_bytes();
    hash(node_bytes)
}

#[derive(Default, PartialEq, Clone)]
pub struct NodeState {
    // TODO: Errors.
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct NodeStore {
    pub nodes: HashMap<Hash, Node>,
    pub root: Hash,
    pub log: Vec<(Ref, Node)>,
}

#[derive(Clone, Debug)]
pub struct Cursor {
    pub parent: Option<(Box<Cursor>, Selector)>,
    pub hash: Hash,
}

impl Cursor {
    pub fn next(&self, node_store: &NodeStore) -> Option<Cursor> {
        // This must work even if the current hash / reference is invalid.
        self.node(node_store)
            .and_then(|node| {
                node.links.iter().next().map(|(field_id, children)| {
                    // Depth first.
                    Cursor {
                        parent: Some((
                            Box::new(self.clone()),
                            Selector {
                                field_id: *field_id,
                                index: 0,
                            },
                        )),
                        hash: children[0].clone(),
                    }
                })
            })
            .or_else(||
                // Try one level up.
                self.parent
                .as_ref()
                    .and_then(|(parent, selector)| parent.child_after(node_store, &selector)))
    }
    fn child_after(&self, node_store: &NodeStore, selector: &Selector) -> Option<Cursor> {
        self.node(node_store).and_then(|node| {
            let children = node.links.get(&selector.field_id).unwrap();
            if (selector.index + 1) < children.len() {
                //  Next index.
                let next_selector = Selector {
                    field_id: selector.field_id,
                    index: selector.index + 1,
                };
                self.traverse(node_store, &[next_selector])
            } else if let Some((next_field_id, _next_children)) = node
                .links
                .range((
                    std::ops::Bound::Excluded(selector.field_id),
                    std::ops::Bound::Unbounded,
                ))
                .next()
            {
                // Next field.
                let next_selector = Selector {
                    field_id: *next_field_id,
                    index: 0,
                };
                self.traverse(node_store, &[next_selector])
            } else {
                // Go up.
                self.parent
                    .as_ref()
                    .and_then(|(parent, selector)| parent.child_after(node_store, &selector))
            }
        })
    }
    pub fn prev(&self, node_store: &NodeStore) -> Option<Cursor> {
        todo!()
    }
    pub fn parent(&self, _node_store: &NodeStore) -> Option<Cursor> {
        self.parent
            .as_ref()
            .map(|(parent_cursor, _selector)| (**parent_cursor).clone())
    }
    pub fn traverse(&self, node_store: &NodeStore, path: &[Selector]) -> Option<Cursor> {
        match path.split_first() {
            Some((selector, rest)) => {
                // child_hash may or may not be valid at this point.
                let child_hash = self
                    .node(node_store)?
                    .links
                    .get(&selector.field_id)?
                    .get(selector.index)?;
                let child = Cursor {
                    parent: Some((Box::new(self.clone()), selector.clone())),
                    hash: child_hash.clone(),
                };
                child.traverse(node_store, rest)
            }
            None => Some(self.clone()),
        }
    }
    pub fn node<'a>(&self, node_store: &'a NodeStore) -> Option<&'a Node> {
        node_store.lookup_hash(&self.hash)
    }
    pub fn path(&self) -> Vec<Selector> {
        match &self.parent {
            Some((parent_cursor, selector)) => {
                let mut path = parent_cursor.path();
                path.push(selector.clone());
                path
            }
            None => vec![],
        }
    }
}

impl PartialEq for NodeStore {
    fn eq(&self, other: &Self) -> bool {
        // Only compare the size of the hashmap, since it is effectively append-only.
        self.nodes.len() == other.nodes.len()
            && self.root == other.root
            && self.log.len() == other.log.len()
    }
}

impl NodeStore {
    pub fn root(&self) -> Cursor {
        Cursor {
            parent: None,
            hash: self.root.clone(),
        }
    }

    pub fn path(&self, path: &[Selector]) -> Option<Cursor> {
        self.root().traverse(self, path)
    }

    // TODO: remove.
    pub fn lookup_hash(&self, hash: &Hash) -> Option<&Node> {
        self.nodes.get(hash)
    }

    #[must_use]
    pub fn add_node(&mut self, node: &Node) -> Hash {
        let h = hash_node(node);
        self.nodes.insert(h.clone(), node.clone());
        h
    }

    #[must_use]
    pub fn replace_node(&mut self, path: &[Selector], node: &Node) -> Option<Hash> {
        self.replace_node_from(&self.root.clone(), path, node)
    }

    #[must_use]
    fn replace_node_from(&mut self, base: &Hash, path: &[Selector], node: &Node) -> Option<Hash> {
        if path.is_empty() {
            Some(self.add_node(node))
        } else {
            let mut base = self.nodes.get(base)?.clone();
            let selector = path[0].clone();
            match base
                .links
                .get(&selector.field_id)
                .and_then(|v| v.get(selector.index))
            {
                Some(old_child_hash) => {
                    let new_child_hash =
                        self.replace_node_from(old_child_hash, &path[1..], node)?;
                    base.links.get_mut(&selector.field_id)?[selector.index] = new_child_hash;
                }
                None => {
                    // WARN: Only works for one level of children.
                    let new_child_hash = self.add_node(node);
                    base.links
                        .entry(selector.field_id)
                        .or_default()
                        .push(new_child_hash);
                }
            };
            Some(self.add_node(&base))
        }
    }
}

// TODO: Navigate to children directly, but use :var to navigate to variables, otherwise skip them
// when navigating.
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct Node {
    // UUID.
    pub kind: String,
    pub value: String,
    // Keyed by field id.
    pub links: BTreeMap<usize, Vec<Hash>>,
}

pub fn display_selector(selector: &Selector) -> Html {
    display_selector_text(&format!("{}", selector.field_id), selector.index)
}

pub fn display_selector_text(field_name: &str, index: usize) -> Html {
    // TODO: lookup.
    html! {

          { field_name }
          { format!("{}", index) }

    }
}

pub fn display_cursor(cursor: &Path) -> Html {
    let segments = cursor
        .iter()
        .map(display_selector)
        .intersperse(html! { { ">" }});
    html! {

{ for segments }

    }
}

pub struct Action {
    pub image: Option<String>,
    pub text: String,
    pub msg: Msg,
}

pub fn get_value_from_input_event(e: InputEvent) -> String {
    let event: Event = e.dyn_into().unwrap_throw();
    let event_target = event.target().unwrap_throw();
    if let Ok(target) = event_target.clone().dyn_into::<HtmlInputElement>() {
        return target.value();
    }
    if let Ok(target) = event_target.dyn_into::<HtmlTextAreaElement>() {
        return target.value();
    }
    panic!("unexpected input event");
    // let target: HtmlTextareaElement = event_target.dyn_into().unwrap_throw();
    // web_sys::console::log_1(&target.value().into());
    // target.value()
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

/// should fit 1 to 18
pub type AtomGroup = u8;

/// sould fit 0.0 to 294.0+, with normal precision [ gram / mol ]
pub type AtomMass = f32;

/// should fit 0 to 118+
pub type AtomNumber = u8;

/// should fit 0.0 to 1e5+, with high precision [ Joule ]
pub type Energy = f64;

/// should fit -7 to 7
pub type IonCharge = i8;

/// should fit 0.0 to 1e5+, with high precision [ gram ]
pub type Mass = f64;

/// should fit 0.0 to 1e5+, with high precision [ mol ]
pub type Moles = f64;

/// should fit -5.0 to 5.0, with normal precision [ volt ]
pub type SEP = f32;
<commit_msg>fix typo<commit_after>#![allow(dead_code)]

/// should fit 1 to 18
pub type AtomGroup = u8;

/// should fit 0.0 to 294.0+, with normal precision [ gram / mol ]
pub type AtomMass = f32;

/// should fit 0 to 118+
pub type AtomNumber = u8;

/// should fit 0.0 to 1e5+, with high precision [ Joule ]
pub type Energy = f64;

/// should fit -7 to 7
pub type IonCharge = i8;

/// should fit 0.0 to 1e5+, with high precision [ gram ]
pub type Mass = f64;

/// should fit 0.0 to 1e5+, with high precision [ mol ]
pub type Moles = f64;

/// should fit -5.0 to 5.0, with normal precision [ volt ]
pub type SEP = f32;
<|endoftext|>


<commit_before>use dao::value::Array;
use dao::Value;

#[derive(Debug, Serialize, PartialEq, Clone)]
pub enum SqlType {
    Bool,
    Tinyint,
    Smallint,
    Int,
    Bigint,

    Real,
    Float,
    Double,
    Numeric,

    Tinyblob,
    Mediumblob,
    Blob,
    Longblob,
    Varbinary,

    Char,
    Varchar,
    Tinytext,
    Mediumtext,
    Text,
    Json,
    TsVector,

    Uuid,
    Date,
    Timestamp,
    TimestampTz,

    Time,
    TimeTz,

    IpAddress,

    Point,

    // enum list with the choices value
    Enum(String, Vec<String>),
    ArrayType(ArrayType),
}

#[derive(Debug, Serialize, PartialEq, Clone)]
pub enum ArrayType {
    Bool,
    Tinyint,
    Smallint,
    Int,
    Bigint,

    Real,
    Float,
    Double,
    Numeric,

    Char,
    Varchar,
    Tinytext,
    Mediumtext,
    Text,

    Uuid,
    Date,
    Timestamp,
    TimestampTz,

    Enum(String, Vec<String>),
}

impl SqlType {
    pub fn same_type(&self, value: &Value) -> bool {
        macro_rules! match_value {
            ($variant:ident) => {
                match *value {
                    Value::$variant(_) => true,
                    _ => false,
                }
            };
        }
        match *self {
            SqlType::Bool => match_value!(Bool),
            SqlType::Tinyint => match_value!(Tinyint),
            SqlType::Smallint => match_value!(Smallint),
            SqlType::Int => match_value!(Int),
            SqlType::Bigint => match_value!(Bigint),
            SqlType::Float => match_value!(Float),
            SqlType::Double => match_value!(Double),
            SqlType::Numeric => match_value!(BigDecimal),
            SqlType::Blob => match_value!(Blob),
            SqlType::Char => match_value!(Char),
            SqlType::Text | SqlType::Varchar => match_value!(Text),
            SqlType::Json => match_value!(Json),
            SqlType::Point => match_value!(Point),
            SqlType::Uuid => match_value!(Uuid),
            SqlType::Date => match_value!(Date),
            SqlType::Timestamp => match_value!(Timestamp),
            SqlType::TimestampTz => match_value!(Timestamp),
            SqlType::Enum(_, _) => match_value!(Text),
            SqlType::ArrayType(ArrayType::Text) => match *value {
                Value::Array(Array::Text(_)) => true,
                _ => false,
            },
            SqlType::ArrayType(ArrayType::Enum(_, _)) => match *value {
                Value::Array(Array::Text(_)) => true,
                _ => false,
            },
            SqlType::Real => match *value {
                Value::Float(_) => true,
                _ => false,
            },
            SqlType::ArrayType(ArrayType::Float) => match *value {
                Value::Array(Array::Float(_)) => true,
                _ => false,
            },
            SqlType::TsVector => match_value!(Text),
            _ => panic!("not yet implemented for checking {:?} to {:?}", self, value),
        }
    }
}
<commit_msg>Add methods to determine type if its integer or decimal types<commit_after>use dao::value::Array;
use dao::Value;

#[derive(Debug, Serialize, PartialEq, Clone)]
pub enum SqlType {
    Bool,
    Tinyint,
    Smallint,
    Int,
    Bigint,

    Real,
    Float,
    Double,
    Numeric,

    Tinyblob,
    Mediumblob,
    Blob,
    Longblob,
    Varbinary,

    Char,
    Varchar,
    Tinytext,
    Mediumtext,
    Text,
    Json,
    TsVector,

    Uuid,
    Date,
    Timestamp,
    TimestampTz,

    Time,
    TimeTz,

    IpAddress,

    Point,

    // enum list with the choices value
    Enum(String, Vec<String>),
    ArrayType(ArrayType),
}

impl SqlType {
    pub fn is_integer_type(&self) -> bool {
        match *self {
            SqlType::Int => true,
            SqlType::Tinyint => true,
            SqlType::Smallint => true,
            SqlType::Bigint => true,
            _ => false,
        }
    }

    pub fn is_decimal_type(&self) -> bool {
        match *self {
            SqlType::Real => true,
            SqlType::Float => true,
            SqlType::Double => true,
            SqlType::Numeric => true,
            _ => false,
        }
    }
}

#[derive(Debug, Serialize, PartialEq, Clone)]
pub enum ArrayType {
    Bool,
    Tinyint,
    Smallint,
    Int,
    Bigint,

    Real,
    Float,
    Double,
    Numeric,

    Char,
    Varchar,
    Tinytext,
    Mediumtext,
    Text,

    Uuid,
    Date,
    Timestamp,
    TimestampTz,

    Enum(String, Vec<String>),
}

impl SqlType {
    pub fn same_type(&self, value: &Value) -> bool {
        macro_rules! match_value {
            ($variant:ident) => {
                match *value {
                    Value::$variant(_) => true,
                    _ => false,
                }
            };
        }
        match *self {
            SqlType::Bool => match_value!(Bool),
            SqlType::Tinyint => match_value!(Tinyint),
            SqlType::Smallint => match_value!(Smallint),
            SqlType::Int => match_value!(Int),
            SqlType::Bigint => match_value!(Bigint),
            SqlType::Float => match_value!(Float),
            SqlType::Double => match_value!(Double),
            SqlType::Numeric => match_value!(BigDecimal),
            SqlType::Blob => match_value!(Blob),
            SqlType::Char => match_value!(Char),
            SqlType::Text | SqlType::Varchar => match_value!(Text),
            SqlType::Json => match_value!(Json),
            SqlType::Point => match_value!(Point),
            SqlType::Uuid => match_value!(Uuid),
            SqlType::Date => match_value!(Date),
            SqlType::Timestamp => match_value!(Timestamp),
            SqlType::TimestampTz => match_value!(Timestamp),
            SqlType::Enum(_, _) => match_value!(Text),
            SqlType::ArrayType(ArrayType::Text) => match *value {
                Value::Array(Array::Text(_)) => true,
                _ => false,
            },
            SqlType::ArrayType(ArrayType::Enum(_, _)) => match *value {
                Value::Array(Array::Text(_)) => true,
                _ => false,
            },
            SqlType::Real => match *value {
                Value::Float(_) => true,
                _ => false,
            },
            SqlType::ArrayType(ArrayType::Float) => match *value {
                Value::Array(Array::Float(_)) => true,
                _ => false,
            },
            SqlType::TsVector => match_value!(Text),
            _ => panic!("not yet implemented for checking {:?} to {:?}", self, value),
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::rc::Rc;

use serde::{Serialize, Serializer};
use serde::ser::SerializeMap;
use serde::ser::SerializeSeq;
use serde::ser::SerializeStruct;
use serde_json::Value as JsonValue;

use schema::{RecordSchema, Schema};

#[derive(Clone, Debug, PartialEq)]
pub enum Value {
    Null,
    Boolean(bool),
    Int(i32),
    Long(i64),
    Float(f32),
    Double(f64),
    Bytes(Vec<u8>),
    String(String),
    Fixed(usize, Vec<u8>),
    Union(Option<Box<Value>>),
    Array(Vec<Value>),
    Map(HashMap<String, Value>),
    Record(HashMap<String, Value>),
}

pub trait ToAvro {
    fn avro(self) -> Value;
}

macro_rules! to_avro(
    ($t:ty, $v:expr) => (
        impl ToAvro for $t {
            fn avro(self) -> Value {
                $v(self)
            }
        }
    );
);

/*
to_avro!(bool, Value::Boolean, Schema::Boolean);
to_avro!(i32, Value::Int, Schema::Int);  // TODO: int to long/float/double, etc...
to_avro!(i64, Value::Long, Schema::Long);
to_avro!(f32, Value::Float, Schema::Float);
to_avro!(f64, Value::Double, Schema::Double);
to_avro!(String, Value::String, Schema::String);

impl ToAvro for () {
    fn avro(self) -> Value {
        Value::Null
    }
}

impl ToAvro for usize {
    fn avro(self) -> Value {
        (self as i64).avro()
    }
}

impl<'a> ToAvro for &'a str {
    fn avro(self, schema: &Schema) -> Option<Value> {
        match schema {
            &Schema::String => Some(Value::String(self.to_owned())),
            _ => None,
        }
    }
}

impl<'a> ToAvro for &'a [u8] {
    fn avro(self, schema: &Schema) -> Option<Value> {
        match schema {
            &Schema::Bytes => Some(Value::Bytes(self.to_owned())),
            &Schema::Fixed { size, .. } if size == self.len() => Some(Value::Fixed(self.len(), self.to_owned())),
            _ => None,
        }

    }
}

impl<T> ToAvro for Option<T> where T: ToAvro {
    fn avro(self, schema: &Schema) -> Option<Value> {
        match schema {
            &Schema::Union(ref inner) => Value::Union(self.map(|v| Box::new(v.avro(inner)))),
        }
    }
}

impl<T> ToAvro for HashMap<String, T> where T: ToAvro {
    fn avro(self) -> Value {
        Value::Map(self
            .into_iter()
            .map(|(key, value)| (key, value.avro()))
            .collect::<_>())
    }
}

impl<'a, T> ToAvro for HashMap<&'a str, T> where T: ToAvro {
    fn avro(self) -> Value {
        Value::Map(self
            .into_iter()
            .map(|(key, value)| (key.to_owned(), value.avro()))
            .collect::<_>())
    }
}

impl ToAvro for Value {
    fn avro(self) -> Value {
        self
    }
}

impl<T> ToAvro for Box<T> where T: ToAvro {
    fn avro(self) -> Value {
        (*self).avro()
    }
}
*/

impl<S: Serialize> ToAvro for S {
    fn avro(self) -> Value {
        use ser::Serializer;

        self.serialize(&mut Serializer::new()).unwrap()
    }
}

impl Serialize for Value {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
        S: Serializer {
        match self {
            &Value::Null => serializer.serialize_unit(),
            &Value::Boolean(b) => serializer.serialize_bool(b),
            &Value::Int(i) => serializer.serialize_i32(i),
            &Value::Long(i) => serializer.serialize_i64(i),
            &Value::Float(x) => serializer.serialize_f32(x),
            &Value::Double(x) => serializer.serialize_f64(x),
            &Value::Bytes(ref bytes) => serializer.serialize_bytes(bytes),
            &Value::String(ref string) => serializer.serialize_str(string),
            &Value::Fixed(_, ref bytes) => serializer.serialize_bytes(bytes),
            &Value::Union(None) => serializer.serialize_none(),
            &Value::Union(Some(ref inner)) => serializer.serialize_some(inner),
            &Value::Array(ref items) => {
                let mut seq = serializer.serialize_seq(Some(items.len()))?;

                for item in items.iter() {
                    seq.serialize_element(item)?;
                }

                seq.end()
            },
            &Value::Map(ref items) => {
                let mut map = serializer.serialize_map(Some(items.len()))?;

                for (key, value) in items.iter() {
                    map.serialize_key(key)?;
                    map.serialize_value(value)?;
                }

                map.end()
            },
            &Value::Record(ref fields) => {
                let record = serializer.serialize_struct("", fields.len())?;

                /*
                grblrbkdjkg serde
                for (field, value) in fields {
                    record.serialize_field(field, value)?;
                }
                */

                record.end()
            },
        }
    }
}

#[derive(Debug)]
pub struct Record {
    pub rschema: Rc<RecordSchema>,
    pub fields: HashMap<String, Value>,
    // lookup: HashMap<String, usize>,
}

impl Record {
    pub fn new(schema: &Schema) -> Option<Record> {
        match schema {
            &Schema::Record(ref rschema) => {
                Some(Record {
                    rschema: rschema.clone(),
                    // lookup: rschema.lookup(),
                    fields: HashMap::new(),
                })
            },
            _ => None,
        }
    }

    /*
    pub fn from_value(value: &Value) -> Option<Record> {
        match value {
            &Value::Record(ref fields) => {
                Some(Record {
                    rschema: rschema.clone(),
                    // lookup: rschema.lookup(),
                    fields: fields.clone(),
                })
            },
            _ => None,
        }
    }
    */

    pub fn put<V>(&mut self, field: &str, value: V) where V: ToAvro {
        let lookup = self.rschema.lookup();  // TODO
        if lookup.get(field).is_none() {
            return
        }

        self.fields.insert(field.to_owned(), value.avro());
    }

    pub fn schema(&self) -> Schema {
        Schema::Record(self.rschema.clone())
    }
}

impl ToAvro for Record {
    fn avro(self) -> Value {
        Value::Record(self.fields)
    }
}

/*
impl ToAvro for JsonValue {
    fn avro(self) -> Value {
        match self {
            JsonValue::Null => Value::Null,
            JsonValue::Bool(b) => Value::Boolean(b),
            JsonValue::Number(ref n) if n.is_i64() => Value::Long(n.as_i64().unwrap()),
            JsonValue::Number(ref n) if n.is_f64() => Value::Double(n.as_f64().unwrap()),
            JsonValue::Number(n) => Value::Long(n.as_u64().unwrap() as i64),  // TODO: Not so great
            JsonValue::String(s) => Value::String(s),
            JsonValue::Array(items) =>
                Value::Array(items.into_iter()
                    .map(|item| item.avro())
                    .collect::<_>()),
            JsonValue::Object(items) =>
                Value::Map(items.into_iter()
                    .map(|(key, value)| (key, value.avro()))
                    .collect::<_>()),
        }
    }
}
*/

impl Value {
    pub fn with_schema(self, schema: &Schema) -> Option<Value> {
        match schema {
            &Schema::Null => self.with_null(),
            &Schema::Boolean => self.with_boolean(),
            &Schema::Int => self.with_int(),
            &Schema::Long => self.with_long(),
            &Schema::Float => self.with_float(),
            &Schema::Double => self.with_double(),
            &Schema::Bytes => self.with_bytes(),
            &Schema::String => self.with_string(),
            &Schema::Array(ref inner) => self.with_array(inner),
            &Schema::Map(ref inner) => self.with_map(inner),
            &Schema::Union(ref inner) => self.with_union(inner),
            &Schema::Record(ref rschema) => self.with_record(rschema.clone()),
            &Schema::Enum { ref symbols, .. } => self.with_enum(symbols),
            &Schema::Fixed { ref size, .. } => self.with_fixed(*size),
        }
    }

    fn with_null(self) -> Option<Value> {
        match self {
            Value::Null => Some(Value::Null),
            _ => None,
        }
    }

    fn with_boolean(self) -> Option<Value> {
        match self {
            Value::Boolean(b) => Some(Value::Boolean(b)),
            _ => None,
        }
    }

    fn with_int(self) -> Option<Value> {
        match self {
            Value::Int(i) => Some(Value::Int(i)),
            _ => None,
        }
    }

    fn with_long(self) -> Option<Value> {
        match self {
            Value::Int(i) => Some(Value::Long(i as i64)),
            Value::Long(i) => Some(Value::Long(i)),
            _ => None,
        }
    }

    fn with_float(self) -> Option<Value> {
        match self {
            Value::Int(i) => Some(Value::Float(i as f32)),
            Value::Long(i) => Some(Value::Float(i as f32)),
            Value::Float(x) => Some(Value::Float(x)),
            _ => None,
        }
    }

    fn with_double(self) -> Option<Value> {
        match self {
            Value::Int(i) => Some(Value::Double(i as f64)),
            Value::Long(i) => Some(Value::Double(i as f64)),
            Value::Float(x) => Some(Value::Double(x as f64)),
            Value::Double(x) => Some(Value::Double(x)),
            _ => None,
        }
    }

    fn with_bytes(self) -> Option<Value> {
        match self {
            Value::Bytes(bytes)
            | Value::Fixed(_, bytes) => Some(Value::Bytes(bytes)),
            Value::String(s) => Some(Value::Bytes(s.into_bytes())),
            _ => None,
        }
    }

    fn with_string(self) -> Option<Value> {
        match self {
            Value::String(s) => Some(Value::String(s)),
            Value::Bytes(bytes)
            | Value::Fixed(_, bytes) => String::from_utf8(bytes).ok().map(Value::String),
            _ => None,
        }
    }

    fn with_array(self, schema: &Schema) -> Option<Value> {
        match self {
            Value::Array(items) =>
                items.into_iter()
                    .map(|item| item.with_schema(schema))
                    .collect::<Option<_>>()
                    .map(Value::Array),
            _ => None,
        }
    }

    fn with_map(self, schema: &Schema) -> Option<Value> {
        match self {
            Value::Map(items) =>
                items.into_iter()
                    .map(|(key, value)| value.with_schema(schema).map(|v| (key, v)))
                    .collect::<Option<_>>()
                    .map(Value::Map),
            _ => None,
        }
    }

    fn with_union(self, schema: &Schema) -> Option<Value> {
        match self {
            Value::Union(None) => Some(Value::Union(None)),
            Value::Union(Some(inner)) => (*inner).with_schema(schema).map(|i| Value::Union(Some(Box::new(i)))),
            value => value.with_schema(schema),
        }
    }

    fn with_enum(self, symbols: &Vec<String>) -> Option<Value> {
        // TODO Value::Enum
        match self {
            Value::Int(i) if i >= 0 && i < symbols.len() as i32 => Some(Value::Int(i)),
            _ => None,
        }
    }

    fn with_fixed(self, size: usize) -> Option<Value> {
        match self {
            Value::Fixed(s, bytes) => {
                if s == size {
                    Some(Value::Fixed(size, bytes))
                } else {
                    None
                }
            },
            Value::String(s) => {
                if s.as_bytes().len() == size {
                    Some(Value::Fixed(size, s.into_bytes()))
                } else {
                    None
                }
            },
            Value::Bytes(bytes) => {
                if bytes.len() == size {
                    Some(Value::Fixed(size, bytes))
                } else {
                    None
                }
            },
            _ => None,
        }
    }

    fn with_record(self, rschema: Rc<RecordSchema>) -> Option<Value> {
        match self {
            Value::Record(mut items) => {
                // Fill in defaults if needed
                for field in rschema.fields.iter() {
                    if !items.contains_key(&field.name) {
                        if let Some(default) = field.default.clone() {
                            items.insert(field.name.clone(), default.avro());
                        } else {
                            return None
                        }
                    }
                }

                // Remove fields that do not exist
                let lookup = rschema.lookup();
                let items = items.into_iter()
                    .filter_map(|(key, value)| lookup.get::<str>(&key).map(|_| (key, value)))
                    .collect::<HashMap<_, _>>();

                Some(Value::Record(items))
            },
            _ => None,
        }
    }
}

/*
impl Serialize for Value {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
        S: Serializer {

    }
}
*/
<commit_msg>Remove comments<commit_after>use std::collections::HashMap;
use std::rc::Rc;

use serde::{Serialize, Serializer};
use serde::ser::SerializeMap;
use serde::ser::SerializeSeq;
use serde::ser::SerializeStruct;
use serde_json::Value as JsonValue;

use schema::{RecordSchema, Schema};

#[derive(Clone, Debug, PartialEq)]
pub enum Value {
    Null,
    Boolean(bool),
    Int(i32),
    Long(i64),
    Float(f32),
    Double(f64),
    Bytes(Vec<u8>),
    String(String),
    Fixed(usize, Vec<u8>),
    Union(Option<Box<Value>>),
    Array(Vec<Value>),
    Map(HashMap<String, Value>),
    Record(HashMap<String, Value>),
}

pub trait ToAvro {
    fn avro(self) -> Value;
}

impl<S: Serialize> ToAvro for S {
    fn avro(self) -> Value {
        use ser::Serializer;

        self.serialize(&mut Serializer::new()).unwrap()
    }
}

impl Serialize for Value {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
        S: Serializer {
        match self {
            &Value::Null => serializer.serialize_unit(),
            &Value::Boolean(b) => serializer.serialize_bool(b),
            &Value::Int(i) => serializer.serialize_i32(i),
            &Value::Long(i) => serializer.serialize_i64(i),
            &Value::Float(x) => serializer.serialize_f32(x),
            &Value::Double(x) => serializer.serialize_f64(x),
            &Value::Bytes(ref bytes) => serializer.serialize_bytes(bytes),
            &Value::String(ref string) => serializer.serialize_str(string),
            &Value::Fixed(_, ref bytes) => serializer.serialize_bytes(bytes),
            &Value::Union(None) => serializer.serialize_none(),
            &Value::Union(Some(ref inner)) => serializer.serialize_some(inner),
            &Value::Array(ref items) => {
                let mut seq = serializer.serialize_seq(Some(items.len()))?;

                for item in items.iter() {
                    seq.serialize_element(item)?;
                }

                seq.end()
            },
            &Value::Map(ref items) => {
                let mut map = serializer.serialize_map(Some(items.len()))?;

                for (key, value) in items.iter() {
                    map.serialize_key(key)?;
                    map.serialize_value(value)?;
                }

                map.end()
            },
            &Value::Record(ref fields) => {
                let record = serializer.serialize_struct("", fields.len())?;

                /*
                grblrbkdjkg serde
                for (field, value) in fields {
                    record.serialize_field(field, value)?;
                }
                */

                record.end()
            },
        }
    }
}

#[derive(Debug)]
pub struct Record {
    pub rschema: Rc<RecordSchema>,
    pub fields: HashMap<String, Value>,
    // lookup: HashMap<String, usize>,
}

impl Record {
    pub fn new(schema: &Schema) -> Option<Record> {
        match schema {
            &Schema::Record(ref rschema) => {
                Some(Record {
                    rschema: rschema.clone(),
                    // lookup: rschema.lookup(),
                    fields: HashMap::new(),
                })
            },
            _ => None,
        }
    }

    pub fn put<V>(&mut self, field: &str, value: V) where V: ToAvro {
        let lookup = self.rschema.lookup();  // TODO
        if lookup.get(field).is_none() {
            return
        }

        self.fields.insert(field.to_owned(), value.avro());
    }

    pub fn schema(&self) -> Schema {
        Schema::Record(self.rschema.clone())
    }
}

impl ToAvro for Record {
    fn avro(self) -> Value {
        Value::Record(self.fields)
    }
}

impl Value {
    pub fn with_schema(self, schema: &Schema) -> Option<Value> {
        match schema {
            &Schema::Null => self.with_null(),
            &Schema::Boolean => self.with_boolean(),
            &Schema::Int => self.with_int(),
            &Schema::Long => self.with_long(),
            &Schema::Float => self.with_float(),
            &Schema::Double => self.with_double(),
            &Schema::Bytes => self.with_bytes(),
            &Schema::String => self.with_string(),
            &Schema::Array(ref inner) => self.with_array(inner),
            &Schema::Map(ref inner) => self.with_map(inner),
            &Schema::Union(ref inner) => self.with_union(inner),
            &Schema::Record(ref rschema) => self.with_record(rschema.clone()),
            &Schema::Enum { ref symbols, .. } => self.with_enum(symbols),
            &Schema::Fixed { ref size, .. } => self.with_fixed(*size),
        }
    }

    fn with_null(self) -> Option<Value> {
        match self {
            Value::Null => Some(Value::Null),
            _ => None,
        }
    }

    fn with_boolean(self) -> Option<Value> {
        match self {
            Value::Boolean(b) => Some(Value::Boolean(b)),
            _ => None,
        }
    }

    fn with_int(self) -> Option<Value> {
        match self {
            Value::Int(i) => Some(Value::Int(i)),
            _ => None,
        }
    }

    fn with_long(self) -> Option<Value> {
        match self {
            Value::Int(i) => Some(Value::Long(i as i64)),
            Value::Long(i) => Some(Value::Long(i)),
            _ => None,
        }
    }

    fn with_float(self) -> Option<Value> {
        match self {
            Value::Int(i) => Some(Value::Float(i as f32)),
            Value::Long(i) => Some(Value::Float(i as f32)),
            Value::Float(x) => Some(Value::Float(x)),
            _ => None,
        }
    }

    fn with_double(self) -> Option<Value> {
        match self {
            Value::Int(i) => Some(Value::Double(i as f64)),
            Value::Long(i) => Some(Value::Double(i as f64)),
            Value::Float(x) => Some(Value::Double(x as f64)),
            Value::Double(x) => Some(Value::Double(x)),
            _ => None,
        }
    }

    fn with_bytes(self) -> Option<Value> {
        match self {
            Value::Bytes(bytes)
            | Value::Fixed(_, bytes) => Some(Value::Bytes(bytes)),
            Value::String(s) => Some(Value::Bytes(s.into_bytes())),
            _ => None,
        }
    }

    fn with_string(self) -> Option<Value> {
        match self {
            Value::String(s) => Some(Value::String(s)),
            Value::Bytes(bytes)
            | Value::Fixed(_, bytes) => String::from_utf8(bytes).ok().map(Value::String),
            _ => None,
        }
    }

    fn with_array(self, schema: &Schema) -> Option<Value> {
        match self {
            Value::Array(items) =>
                items.into_iter()
                    .map(|item| item.with_schema(schema))
                    .collect::<Option<_>>()
                    .map(Value::Array),
            _ => None,
        }
    }

    fn with_map(self, schema: &Schema) -> Option<Value> {
        match self {
            Value::Map(items) =>
                items.into_iter()
                    .map(|(key, value)| value.with_schema(schema).map(|v| (key, v)))
                    .collect::<Option<_>>()
                    .map(Value::Map),
            _ => None,
        }
    }

    fn with_union(self, schema: &Schema) -> Option<Value> {
        match self {
            Value::Union(None) => Some(Value::Union(None)),
            Value::Union(Some(inner)) => (*inner).with_schema(schema).map(|i| Value::Union(Some(Box::new(i)))),
            value => value.with_schema(schema),
        }
    }

    fn with_enum(self, symbols: &Vec<String>) -> Option<Value> {
        // TODO Value::Enum
        match self {
            Value::Int(i) if i >= 0 && i < symbols.len() as i32 => Some(Value::Int(i)),
            _ => None,
        }
    }

    fn with_fixed(self, size: usize) -> Option<Value> {
        match self {
            Value::Fixed(s, bytes) => {
                if s == size {
                    Some(Value::Fixed(size, bytes))
                } else {
                    None
                }
            },
            Value::String(s) => {
                if s.as_bytes().len() == size {
                    Some(Value::Fixed(size, s.into_bytes()))
                } else {
                    None
                }
            },
            Value::Bytes(bytes) => {
                if bytes.len() == size {
                    Some(Value::Fixed(size, bytes))
                } else {
                    None
                }
            },
            _ => None,
        }
    }

    fn with_record(self, rschema: Rc<RecordSchema>) -> Option<Value> {
        match self {
            Value::Record(mut items) => {
                // Fill in defaults if needed
                for field in rschema.fields.iter() {
                    if !items.contains_key(&field.name) {
                        if let Some(default) = field.default.clone() {
                            items.insert(field.name.clone(), default.avro());
                        } else {
                            return None
                        }
                    }
                }

                // Remove fields that do not exist
                let lookup = rschema.lookup();
                let items = items.into_iter()
                    .filter_map(|(key, value)| lookup.get::<str>(&key).map(|_| (key, value)))
                    .collect::<HashMap<_, _>>();

                Some(Value::Record(items))
            },
            _ => None,
        }
    }
}

/*
impl Serialize for Value {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
        S: Serializer {

    }
}
*/
<|endoftext|>


<commit_before>/*!
The types module provides a way of associating glob patterns on file names to
file types.
*/

use std::collections::HashMap;
use std::error::Error as StdError;
use std::fmt;
use std::path::Path;

use regex;

use gitignore::{Match, Pattern};
use globset::{self, MatchOptions};

const TYPE_EXTENSIONS: &'static [(&'static str, &'static [&'static str])] = &[
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("awk", &["*.awk"]),
    ("c", &["*.c", "*.h", "*.H"]),
    ("cbor", &["*.cbor"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("cpp", &[
        "*.C", "*.cc", "*.cpp", "*.cxx",
        "*.h", "*.H", "*.hh", "*.hpp",
    ]),
    ("csharp", &["*.cs"]),
    ("css", &["*.css"]),
    ("cython", &["*.pyx"]),
    ("dart", &["*.dart"]),
    ("d", &["*.d"]),
    ("elisp", &["*.el"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("go", &["*.go"]),
    ("groovy", &["*.groovy"]),
    ("haskell", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html"]),
    ("java", &["*.java"]),
    ("js", &[
        "*.js", "*.jsx", "*.vue",
    ]),
    ("json", &["*.json"]),
    ("jsonl", &["*.jsonl"]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lua", &["*.lua"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &["gnumakefile", "Gnumakefile", "makefile", "Makefile", "*.mk"]),
    ("markdown", &["*.md"]),
    ("matlab", &["*.m"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("nim", &["*.nim"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("py", &["*.py"]),
    ("readme", &["README*", "*README"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("rst", &["*.rst"]),
    ("ruby", &["*.rb"]),
    ("rust", &["*.rs"]),
    ("scala", &["*.scala"]),
    ("sh", &["*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh"]),
    ("sql", &["*.sql"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("swift", &["*.swift"]),
    ("tex", &["*.tex", "*.cls", "*.sty"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("txt", &["*.txt"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vimscript", &["*.vim"]),
    ("xml", &["*.xml"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
];

/// Describes all the possible failure conditions for building a file type
/// matcher.
#[derive(Debug)]
pub enum Error {
    /// We tried to select (or negate) a file type that is not defined.
    UnrecognizedFileType(String),
    /// A user specified file type definition could not be parsed.
    InvalidDefinition,
    /// There was an error building the matcher (probably a bad glob).
    Glob(globset::Error),
    /// There was an error compiling a glob as a regex.
    Regex(regex::Error),
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnrecognizedFileType(_) => "unrecognized file type",
            Error::InvalidDefinition => "invalid definition",
            Error::Glob(ref err) => err.description(),
            Error::Regex(ref err) => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::UnrecognizedFileType(ref ty) => {
                write!(f, "unrecognized file type: {}", ty)
            }
            Error::InvalidDefinition => {
                write!(f, "invalid definition (format is type:glob, e.g., \
                           html:*.html)")
            }
            Error::Glob(ref err) => err.fmt(f),
            Error::Regex(ref err) => err.fmt(f),
        }
    }
}

impl From<globset::Error> for Error {
    fn from(err: globset::Error) -> Error {
        Error::Glob(err)
    }
}

impl From<regex::Error> for Error {
    fn from(err: regex::Error) -> Error {
        Error::Regex(err)
    }
}

/// A single file type definition.
#[derive(Clone, Debug)]
pub struct FileTypeDef {
    name: String,
    pats: Vec<String>,
}

impl FileTypeDef {
    /// Return the name of this file type.
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Return the glob patterns used to recognize this file type.
    pub fn patterns(&self) -> &[String] {
        &self.pats
    }
}

/// Types is a file type matcher.
#[derive(Clone, Debug)]
pub struct Types {
    defs: Vec<FileTypeDef>,
    selected: Option<globset::SetYesNo>,
    negated: Option<globset::SetYesNo>,
    has_selected: bool,
    unmatched_pat: Pattern,
}

impl Types {
    /// Creates a new file type matcher from the given Gitignore matcher. If
    /// not Gitignore matcher is provided, then the file type matcher has no
    /// effect.
    ///
    /// If has_selected is true, then at least one file type was selected.
    /// Therefore, any non-matches should be ignored.
    fn new(
        selected: Option<globset::SetYesNo>,
        negated: Option<globset::SetYesNo>,
        has_selected: bool,
        defs: Vec<FileTypeDef>,
    ) -> Types {
        Types {
            defs: defs,
            selected: selected,
            negated: negated,
            has_selected: has_selected,
            unmatched_pat: Pattern {
                from: Path::new("<filetype>").to_path_buf(),
                original: "<N/A>".to_string(),
                pat: "<N/A>".to_string(),
                whitelist: false,
                only_dir: false,
            },
        }
    }

    /// Creates a new file type matcher that never matches.
    pub fn empty() -> Types {
        Types::new(None, None, false, vec![])
    }

    /// Returns a match for the given path against this file type matcher.
    ///
    /// The path is considered whitelisted if it matches a selected file type.
    /// The path is considered ignored if it matched a negated file type.
    /// If at least one file type is selected and path doesn't match, then
    /// the path is also considered ignored.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        // If we don't have any matcher, then we can't do anything.
        if self.negated.is_none() && self.selected.is_none() {
            return Match::None;
        }
        // File types don't apply to directories.
        if is_dir {
            return Match::None;
        }
        let path = path.as_ref();
        let name = match path.file_name() {
            Some(name) => name.to_string_lossy(),
            None if self.has_selected => {
                return Match::Ignored(&self.unmatched_pat);
            }
            None => {
                return Match::None;
            }
        };
        if self.negated.as_ref().map(|s| s.is_match(&*name)).unwrap_or(false) {
            return Match::Ignored(&self.unmatched_pat);
        }
        if self.selected.as_ref().map(|s|s.is_match(&*name)).unwrap_or(false) {
            return Match::Whitelist(&self.unmatched_pat);
        }
        if self.has_selected {
            Match::Ignored(&self.unmatched_pat)
        } else {
            Match::None
        }
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> &[FileTypeDef] {
        &self.defs
    }
}

/// TypesBuilder builds a type matcher from a set of file type definitions and
/// a set of file type selections.
pub struct TypesBuilder {
    types: HashMap<String, Vec<String>>,
    selected: Vec<String>,
    negated: Vec<String>,
}

impl TypesBuilder {
    /// Create a new builder for a file type matcher.
    pub fn new() -> TypesBuilder {
        TypesBuilder {
            types: HashMap::new(),
            selected: vec![],
            negated: vec![],
        }
    }

    /// Build the current set of file type definitions *and* selections into
    /// a file type matcher.
    pub fn build(&self) -> Result<Types, Error> {
        let opts = MatchOptions {
            require_literal_separator: true, ..MatchOptions::default()
        };
        let selected_globs =
            if self.selected.is_empty() {
                None
            } else {
                let mut bset = globset::SetBuilder::new();
                for name in &self.selected {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        try!(bset.add_with(glob, &opts));
                    }
                }
                Some(try!(bset.build_yesno()))
            };
        let negated_globs =
            if self.negated.is_empty() {
                None
            } else {
                let mut bset = globset::SetBuilder::new();
                for name in &self.negated {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        try!(bset.add_with(glob, &opts));
                    }
                }
                Some(try!(bset.build_yesno()))
            };
        Ok(Types::new(
            selected_globs,
            negated_globs,
            !self.selected.is_empty(),
            self.definitions(),
        ))
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> Vec<FileTypeDef> {
        let mut defs = vec![];
        for (ref name, ref pats) in &self.types {
            let mut pats = pats.to_vec();
            pats.sort();
            defs.push(FileTypeDef {
                name: name.to_string(),
                pats: pats,
            });
        }
        defs.sort_by(|def1, def2| def1.name().cmp(def2.name()));
        defs
    }

    /// Select the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are selected.
    pub fn select(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.selected.push(name.to_string());
            }
        } else {
            self.selected.push(name.to_string());
        }
        self
    }

    /// Ignore the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are negated.
    pub fn negate(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.negated.push(name.to_string());
            }
        } else {
            self.negated.push(name.to_string());
        }
        self
    }

    /// Clear any file type definitions for the type given.
    pub fn clear(&mut self, name: &str) -> &mut TypesBuilder {
        self.types.remove(name);
        self
    }

    /// Add a new file type definition. `name` can be arbitrary and `pat`
    /// should be a glob recognizing file paths belonging to the `name` type.
    pub fn add(&mut self, name: &str, pat: &str) -> &mut TypesBuilder {
        self.types.entry(name.to_string())
            .or_insert(vec![]).push(pat.to_string());
        self
    }

    /// Add a new file type definition specified in string form. The format
    /// is `name:glob`. Names may not include a colon.
    pub fn add_def(&mut self, def: &str) -> Result<(), Error> {
        let name: String = def.chars().take_while(|&c| c != ':').collect();
        let pat: String = def.chars().skip(name.chars().count() + 1).collect();
        if name.is_empty() || pat.is_empty() {
            return Err(Error::InvalidDefinition);
        }
        self.add(&name, &pat);
        Ok(())
    }

    /// Add a set of default file type definitions.
    pub fn add_defaults(&mut self) -> &mut TypesBuilder {
        for &(name, exts) in TYPE_EXTENSIONS {
            for ext in exts {
                self.add(name, ext);
            }
        }
        self
    }
}

#[cfg(test)]
mod tests {
    use super::TypesBuilder;

    macro_rules! matched {
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, true);
        };
        (not, $name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, false);
        };
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr, $matched:expr) => {
            #[test]
            fn $name() {
                let mut btypes = TypesBuilder::new();
                for tydef in $types {
                    btypes.add_def(tydef).unwrap();
                }
                for sel in $sel {
                    btypes.select(sel);
                }
                for selnot in $selnot {
                    btypes.negate(selnot);
                }
                let types = btypes.build().unwrap();
                let mat = types.matched($path, false);
                assert_eq!($matched, !mat.is_ignored());
            }
        };
    }

    fn types() -> Vec<&'static str> {
        vec![
            "html:*.html",
            "html:*.htm",
            "rust:*.rs",
            "js:*.js",
        ]
    }

    matched!(match1, types(), vec!["rust"], vec![], "lib.rs");
    matched!(match2, types(), vec!["html"], vec![], "index.html");
    matched!(match3, types(), vec!["html"], vec![], "index.htm");
    matched!(match4, types(), vec!["html", "rust"], vec![], "main.rs");
    matched!(match5, types(), vec![], vec![], "index.html");
    matched!(match6, types(), vec![], vec!["rust"], "index.html");

    matched!(not, matchnot1, types(), vec!["rust"], vec![], "index.html");
    matched!(not, matchnot2, types(), vec![], vec!["rust"], "main.rs");
}
<commit_msg>add jinja type for *.jinja and *.jinja2<commit_after>/*!
The types module provides a way of associating glob patterns on file names to
file types.
*/

use std::collections::HashMap;
use std::error::Error as StdError;
use std::fmt;
use std::path::Path;

use regex;

use gitignore::{Match, Pattern};
use globset::{self, MatchOptions};

const TYPE_EXTENSIONS: &'static [(&'static str, &'static [&'static str])] = &[
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("awk", &["*.awk"]),
    ("c", &["*.c", "*.h", "*.H"]),
    ("cbor", &["*.cbor"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("cpp", &[
        "*.C", "*.cc", "*.cpp", "*.cxx",
        "*.h", "*.H", "*.hh", "*.hpp",
    ]),
    ("csharp", &["*.cs"]),
    ("css", &["*.css"]),
    ("cython", &["*.pyx"]),
    ("dart", &["*.dart"]),
    ("d", &["*.d"]),
    ("elisp", &["*.el"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("go", &["*.go"]),
    ("groovy", &["*.groovy"]),
    ("haskell", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html"]),
    ("java", &["*.java"]),
    ("jinja", &["*.jinja", "*.jinja2"]),
    ("js", &[
        "*.js", "*.jsx", "*.vue",
    ]),
    ("json", &["*.json"]),
    ("jsonl", &["*.jsonl"]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lua", &["*.lua"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &["gnumakefile", "Gnumakefile", "makefile", "Makefile", "*.mk"]),
    ("markdown", &["*.md"]),
    ("matlab", &["*.m"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("nim", &["*.nim"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("py", &["*.py"]),
    ("readme", &["README*", "*README"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("rst", &["*.rst"]),
    ("ruby", &["*.rb"]),
    ("rust", &["*.rs"]),
    ("scala", &["*.scala"]),
    ("sh", &["*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh"]),
    ("sql", &["*.sql"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("swift", &["*.swift"]),
    ("tex", &["*.tex", "*.cls", "*.sty"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("txt", &["*.txt"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vimscript", &["*.vim"]),
    ("xml", &["*.xml"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
];

/// Describes all the possible failure conditions for building a file type
/// matcher.
#[derive(Debug)]
pub enum Error {
    /// We tried to select (or negate) a file type that is not defined.
    UnrecognizedFileType(String),
    /// A user specified file type definition could not be parsed.
    InvalidDefinition,
    /// There was an error building the matcher (probably a bad glob).
    Glob(globset::Error),
    /// There was an error compiling a glob as a regex.
    Regex(regex::Error),
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnrecognizedFileType(_) => "unrecognized file type",
            Error::InvalidDefinition => "invalid definition",
            Error::Glob(ref err) => err.description(),
            Error::Regex(ref err) => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::UnrecognizedFileType(ref ty) => {
                write!(f, "unrecognized file type: {}", ty)
            }
            Error::InvalidDefinition => {
                write!(f, "invalid definition (format is type:glob, e.g., \
                           html:*.html)")
            }
            Error::Glob(ref err) => err.fmt(f),
            Error::Regex(ref err) => err.fmt(f),
        }
    }
}

impl From<globset::Error> for Error {
    fn from(err: globset::Error) -> Error {
        Error::Glob(err)
    }
}

impl From<regex::Error> for Error {
    fn from(err: regex::Error) -> Error {
        Error::Regex(err)
    }
}

/// A single file type definition.
#[derive(Clone, Debug)]
pub struct FileTypeDef {
    name: String,
    pats: Vec<String>,
}

impl FileTypeDef {
    /// Return the name of this file type.
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Return the glob patterns used to recognize this file type.
    pub fn patterns(&self) -> &[String] {
        &self.pats
    }
}

/// Types is a file type matcher.
#[derive(Clone, Debug)]
pub struct Types {
    defs: Vec<FileTypeDef>,
    selected: Option<globset::SetYesNo>,
    negated: Option<globset::SetYesNo>,
    has_selected: bool,
    unmatched_pat: Pattern,
}

impl Types {
    /// Creates a new file type matcher from the given Gitignore matcher. If
    /// not Gitignore matcher is provided, then the file type matcher has no
    /// effect.
    ///
    /// If has_selected is true, then at least one file type was selected.
    /// Therefore, any non-matches should be ignored.
    fn new(
        selected: Option<globset::SetYesNo>,
        negated: Option<globset::SetYesNo>,
        has_selected: bool,
        defs: Vec<FileTypeDef>,
    ) -> Types {
        Types {
            defs: defs,
            selected: selected,
            negated: negated,
            has_selected: has_selected,
            unmatched_pat: Pattern {
                from: Path::new("<filetype>").to_path_buf(),
                original: "<N/A>".to_string(),
                pat: "<N/A>".to_string(),
                whitelist: false,
                only_dir: false,
            },
        }
    }

    /// Creates a new file type matcher that never matches.
    pub fn empty() -> Types {
        Types::new(None, None, false, vec![])
    }

    /// Returns a match for the given path against this file type matcher.
    ///
    /// The path is considered whitelisted if it matches a selected file type.
    /// The path is considered ignored if it matched a negated file type.
    /// If at least one file type is selected and path doesn't match, then
    /// the path is also considered ignored.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        // If we don't have any matcher, then we can't do anything.
        if self.negated.is_none() && self.selected.is_none() {
            return Match::None;
        }
        // File types don't apply to directories.
        if is_dir {
            return Match::None;
        }
        let path = path.as_ref();
        let name = match path.file_name() {
            Some(name) => name.to_string_lossy(),
            None if self.has_selected => {
                return Match::Ignored(&self.unmatched_pat);
            }
            None => {
                return Match::None;
            }
        };
        if self.negated.as_ref().map(|s| s.is_match(&*name)).unwrap_or(false) {
            return Match::Ignored(&self.unmatched_pat);
        }
        if self.selected.as_ref().map(|s|s.is_match(&*name)).unwrap_or(false) {
            return Match::Whitelist(&self.unmatched_pat);
        }
        if self.has_selected {
            Match::Ignored(&self.unmatched_pat)
        } else {
            Match::None
        }
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> &[FileTypeDef] {
        &self.defs
    }
}

/// TypesBuilder builds a type matcher from a set of file type definitions and
/// a set of file type selections.
pub struct TypesBuilder {
    types: HashMap<String, Vec<String>>,
    selected: Vec<String>,
    negated: Vec<String>,
}

impl TypesBuilder {
    /// Create a new builder for a file type matcher.
    pub fn new() -> TypesBuilder {
        TypesBuilder {
            types: HashMap::new(),
            selected: vec![],
            negated: vec![],
        }
    }

    /// Build the current set of file type definitions *and* selections into
    /// a file type matcher.
    pub fn build(&self) -> Result<Types, Error> {
        let opts = MatchOptions {
            require_literal_separator: true, ..MatchOptions::default()
        };
        let selected_globs =
            if self.selected.is_empty() {
                None
            } else {
                let mut bset = globset::SetBuilder::new();
                for name in &self.selected {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        try!(bset.add_with(glob, &opts));
                    }
                }
                Some(try!(bset.build_yesno()))
            };
        let negated_globs =
            if self.negated.is_empty() {
                None
            } else {
                let mut bset = globset::SetBuilder::new();
                for name in &self.negated {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        try!(bset.add_with(glob, &opts));
                    }
                }
                Some(try!(bset.build_yesno()))
            };
        Ok(Types::new(
            selected_globs,
            negated_globs,
            !self.selected.is_empty(),
            self.definitions(),
        ))
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> Vec<FileTypeDef> {
        let mut defs = vec![];
        for (ref name, ref pats) in &self.types {
            let mut pats = pats.to_vec();
            pats.sort();
            defs.push(FileTypeDef {
                name: name.to_string(),
                pats: pats,
            });
        }
        defs.sort_by(|def1, def2| def1.name().cmp(def2.name()));
        defs
    }

    /// Select the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are selected.
    pub fn select(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.selected.push(name.to_string());
            }
        } else {
            self.selected.push(name.to_string());
        }
        self
    }

    /// Ignore the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are negated.
    pub fn negate(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.negated.push(name.to_string());
            }
        } else {
            self.negated.push(name.to_string());
        }
        self
    }

    /// Clear any file type definitions for the type given.
    pub fn clear(&mut self, name: &str) -> &mut TypesBuilder {
        self.types.remove(name);
        self
    }

    /// Add a new file type definition. `name` can be arbitrary and `pat`
    /// should be a glob recognizing file paths belonging to the `name` type.
    pub fn add(&mut self, name: &str, pat: &str) -> &mut TypesBuilder {
        self.types.entry(name.to_string())
            .or_insert(vec![]).push(pat.to_string());
        self
    }

    /// Add a new file type definition specified in string form. The format
    /// is `name:glob`. Names may not include a colon.
    pub fn add_def(&mut self, def: &str) -> Result<(), Error> {
        let name: String = def.chars().take_while(|&c| c != ':').collect();
        let pat: String = def.chars().skip(name.chars().count() + 1).collect();
        if name.is_empty() || pat.is_empty() {
            return Err(Error::InvalidDefinition);
        }
        self.add(&name, &pat);
        Ok(())
    }

    /// Add a set of default file type definitions.
    pub fn add_defaults(&mut self) -> &mut TypesBuilder {
        for &(name, exts) in TYPE_EXTENSIONS {
            for ext in exts {
                self.add(name, ext);
            }
        }
        self
    }
}

#[cfg(test)]
mod tests {
    use super::TypesBuilder;

    macro_rules! matched {
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, true);
        };
        (not, $name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, false);
        };
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr, $matched:expr) => {
            #[test]
            fn $name() {
                let mut btypes = TypesBuilder::new();
                for tydef in $types {
                    btypes.add_def(tydef).unwrap();
                }
                for sel in $sel {
                    btypes.select(sel);
                }
                for selnot in $selnot {
                    btypes.negate(selnot);
                }
                let types = btypes.build().unwrap();
                let mat = types.matched($path, false);
                assert_eq!($matched, !mat.is_ignored());
            }
        };
    }

    fn types() -> Vec<&'static str> {
        vec![
            "html:*.html",
            "html:*.htm",
            "rust:*.rs",
            "js:*.js",
        ]
    }

    matched!(match1, types(), vec!["rust"], vec![], "lib.rs");
    matched!(match2, types(), vec!["html"], vec![], "index.html");
    matched!(match3, types(), vec!["html"], vec![], "index.htm");
    matched!(match4, types(), vec!["html", "rust"], vec![], "main.rs");
    matched!(match5, types(), vec![], vec![], "index.html");
    matched!(match6, types(), vec![], vec!["rust"], "index.html");

    matched!(not, matchnot1, types(), vec!["rust"], vec![], "index.html");
    matched!(not, matchnot2, types(), vec![], vec!["rust"], "main.rs");
}
<|endoftext|>


<commit_before>//! Types for HVIF images

#[cfg(feature = "core")]
use collections::vec::Vec;
#[cfg(not(feature = "core"))]
use std::vec::Vec;

#[derive(Debug)]
/// A fully specified HVIF image
pub struct HVIFImage {
  styles: Vec<HVIFStyle>,
  paths: Vec<HVIFPath>,
  shapes: Vec<HVIFShape>
}

#[derive(Debug)]
/// A single HVIF style
pub enum HVIFStyle {
  /// A solid aRGB color
  SolidColor { #[doc="alpha channel"] alpha: u8, #[doc="red channel"] red: u8, #[doc="green channel"] green: u8, #[doc="blue channel"] blue: u8 },
  /// A gradient between multiple aRGB colors
  Gradient(HVIFGradient),
  /// A solid opaque RGB color
  SolidColorNoAlpha { #[doc="red channel"] red: u8, #[doc="green channel"] green: u8, #[doc="blue channel"] blue: u8 },
  /// A solid greyscale color with an alpha channel
  SolidGray { #[doc="alpha channel"] alpha: u8, #[doc="value on red, green, and blue channels"] value: u8},
  /// A solid greyscale color without an alpha channel
  SolidGrayNoAlpha { #[doc="value on red, green, and blue channels"] value: u8 }
}

#[derive(Debug)]
/// A gradient between mutiple aRGB colors
pub struct HVIFGradient {
  gradient_type: HVIFGradientType,
  flags: u8,
  colors: Vec<HVIFGradientColor>
}

#[derive(Debug, Copy, Clone)]
/// The type of a gradient - determines how the gradient renders spatially
pub enum HVIFGradientType {
  /// A linear gradient; follows a line from one point to another
  Linear,
  /// A circular gradient; changes radially from the center to the edge of a circular region
  Circular,
  /// A diamond graident; changes linearly from the center to the edge of a diagonal region
  Diamond,
  /// A conic gradient; changes angularly across a circular region
  Conic,
  /// An XY gradient; changes linearly in both the X and Y directions
  XY,
  /// A square root XY gradient: changes quadratically in both the X and Y directions

  SqrtXY
}

#[derive(Debug, Copy, Clone)]
/// Flags that modify the nature of a gradient
pub enum GradientFlags {
  /// ?
  Transform       = 0b0000_0001,
  /// ?
  NoAlpha         = 0b0000_0010,
  /// ?
  Colors16Bit     = 0b0000_0100,
  /// ?
  Grays           = 0b0000_1000,
}

#[derive(Debug, Copy, Clone)]
/// Color of a gradient, along with a stop offset (distance between colors)
pub struct HVIFGradientColor {
  stop_offset: u8,
  alpha: u8,
  red: u8,
  green: u8,
  blue: u8
}

#[derive(Debug)]
/// An HVIF path, drawn between points
pub struct HVIFPath {
  flags: u8,
  points: Vec<HVIFPointCommand>
}

#[derive(Debug, Copy, Clone)]
/// Flags that modify the nature of a path
pub enum HVIFPathFlags {
  /// The path's last point is connected to its first point
  Closed       = 0b0000_0001,
  /// The path has a command section, and can use HLine and VLine commands
  UsesCommands = 0b0000_0010,
  /// The path is made up entirely of straight lines
  NoCurves     = 0b0000_0100,
}

#[derive(Debug, Copy, Clone)]
/// One or more points, and a command that specifies how the point/s are to be interpreted
pub enum HVIFPointCommand {
  /// A horizontal line from the previous x-coordinate to this one
  HLine { #[doc="target x-coordinate"] x_coord: f32 },
  /// A vertical line from the previous y-coordinate to this one
  VLine { #[doc="target y-coordinate"] y_coord: f32 },
  /// A straight line from the previous point to this one
  Line  { #[doc="target point"] point: HVIFPoint },
  /// A cubic Bezier curve from the previous point to this one
  Curve { #[doc="inital control point"] point_in: HVIFPoint, #[doc="target point"] point: HVIFPoint, #[doc="final control point"] point_out: HVIFPoint},
}

#[derive(Debug, Copy, Clone)]
/// A simple 2D point in the XY plane, where each coordinate is a floating point value
pub struct HVIFPoint { x: f32, y: f32 }

#[derive(Debug)]
/// An HVIF shape, consisting of a single style, one or more paths, and optional additional transformation data
pub struct HVIFShape {
  style_index: u8,
  path_indices: Vec<u8>,
  flags: u8,
  transform: Option<HVIFMatrix>,
  translate: Option<HVIFPoint>,
  lod_scale: Option<HVIFLODScale>,
  transformer_list: Vec<HVIFTransformer>
}

#[derive(Debug, Copy, Clone)]
/// Flags that modify the nature of a shape
pub enum HVIFShapeFlags {
  /// ?
  Transform       = 0b0000_0001,
  /// ?
  Hinting         = 0b0000_0010,
  /// ?
  LODScale        = 0b0000_0100,
  /// ?
  HasTransformers = 0b0000_1000,
  /// ?
  Translation     = 0b0001_0000,
}

#[derive(Debug, Copy, Clone)]
/// A 2D affine transformation matrix
/// As per HVIF spec, consists of six f24 values
pub struct HVIFMatrix {
  x1: HVIFf24, y1: HVIFf24, z1: HVIFf24,
  x2: HVIFf24, y2: HVIFf24, z2: HVIFf24,
}
#[derive(Debug, Copy, Clone)]
/// A temporary implementation of an f24 value, as three bytes
pub struct HVIFf24 { fst: u8, snd: u8, thr: u8 }

#[derive(Debug, Copy, Clone)]
/// Level of Detail scales
/// Represents the allowable scales at which this shape will appear correctly
pub struct HVIFLODScale { min: f32, max: f32 }

#[derive(Debug, Copy, Clone)]
/// Some transformation on a basic HVIF shape
pub enum HVIFTransformer {
  /// An affine transformation using a single matrix
  Affine(HVIFMatrix),
  /// ?
  Contour { #[doc="?"] width: f32, #[doc="?"] line_join: u8, #[doc="?"] miter_limit: u8 },
  /// ?
  Perspective,
  /// ?
  Stroke { #[doc="?"] width: f32, #[doc="?"] line_join: u8, #[doc="?"] line_cap: u8, #[doc="?"] miter_limit: u8 }
}

/// Test function
fn empty_image() -> HVIFImage {
  HVIFImage {
    styles: Vec::new(),
    paths:  Vec::new(),
    shapes: Vec::new()
  }
}

#[cfg(test)]
mod tests {
    use empty_image;
    #[test]
    fn it_works() {
        empty_image();
    }
}
<commit_msg>Fix build, remove useless tests<commit_after>//! Types for HVIF images

#[cfg(feature = "core")]
use collections::vec::Vec;
#[cfg(not(feature = "core"))]
use std::vec::Vec;

#[derive(Debug)]
/// A fully specified HVIF image
pub struct HVIFImage {
  styles: Vec<HVIFStyle>,
  paths: Vec<HVIFPath>,
  shapes: Vec<HVIFShape>
}

#[derive(Debug)]
/// A single HVIF style
pub enum HVIFStyle {
  /// A solid aRGB color
  SolidColor { #[doc="alpha channel"] alpha: u8, #[doc="red channel"] red: u8, #[doc="green channel"] green: u8, #[doc="blue channel"] blue: u8 },
  /// A gradient between multiple aRGB colors
  Gradient(HVIFGradient),
  /// A solid opaque RGB color
  SolidColorNoAlpha { #[doc="red channel"] red: u8, #[doc="green channel"] green: u8, #[doc="blue channel"] blue: u8 },
  /// A solid greyscale color with an alpha channel
  SolidGray { #[doc="alpha channel"] alpha: u8, #[doc="value on red, green, and blue channels"] value: u8},
  /// A solid greyscale color without an alpha channel
  SolidGrayNoAlpha { #[doc="value on red, green, and blue channels"] value: u8 }
}

#[derive(Debug)]
/// A gradient between mutiple aRGB colors
pub struct HVIFGradient {
  gradient_type: HVIFGradientType,
  flags: u8,
  colors: Vec<HVIFGradientColor>
}

#[derive(Debug, Copy, Clone)]
/// The type of a gradient - determines how the gradient renders spatially
pub enum HVIFGradientType {
  /// A linear gradient; follows a line from one point to another
  Linear,
  /// A circular gradient; changes radially from the center to the edge of a circular region
  Circular,
  /// A diamond graident; changes linearly from the center to the edge of a diagonal region
  Diamond,
  /// A conic gradient; changes angularly across a circular region
  Conic,
  /// An XY gradient; changes linearly in both the X and Y directions
  XY,
  /// A square root XY gradient: changes quadratically in both the X and Y directions

  SqrtXY
}

#[derive(Debug, Copy, Clone)]
/// Flags that modify the nature of a gradient
pub enum GradientFlags {
  /// ?
  Transform       = 0b0000_0001,
  /// ?
  NoAlpha         = 0b0000_0010,
  /// ?
  Colors16Bit     = 0b0000_0100,
  /// ?
  Grays           = 0b0000_1000,
}

#[derive(Debug, Copy, Clone)]
/// Color of a gradient, along with a stop offset (distance between colors)
pub struct HVIFGradientColor {
  stop_offset: u8,
  alpha: u8,
  red: u8,
  green: u8,
  blue: u8
}

#[derive(Debug)]
/// An HVIF path, drawn between points
pub struct HVIFPath {
  flags: u8,
  points: Vec<HVIFPointCommand>
}

#[derive(Debug, Copy, Clone)]
/// Flags that modify the nature of a path
pub enum HVIFPathFlags {
  /// The path's last point is connected to its first point
  Closed       = 0b0000_0001,
  /// The path has a command section, and can use HLine and VLine commands
  UsesCommands = 0b0000_0010,
  /// The path is made up entirely of straight lines
  NoCurves     = 0b0000_0100,
}

#[derive(Debug, Copy, Clone)]
/// One or more points, and a command that specifies how the point/s are to be interpreted
pub enum HVIFPointCommand {
  /// A horizontal line from the previous x-coordinate to this one
  HLine { #[doc="target x-coordinate"] x_coord: f32 },
  /// A vertical line from the previous y-coordinate to this one
  VLine { #[doc="target y-coordinate"] y_coord: f32 },
  /// A straight line from the previous point to this one
  Line  { #[doc="target point"] point: HVIFPoint },
  /// A cubic Bezier curve from the previous point to this one
  Curve { #[doc="inital control point"] point_in: HVIFPoint, #[doc="target point"] point: HVIFPoint, #[doc="final control point"] point_out: HVIFPoint},
}

#[derive(Debug, Copy, Clone)]
/// A simple 2D point in the XY plane, where each coordinate is a floating point value
pub struct HVIFPoint { x: f32, y: f32 }

#[derive(Debug)]
/// An HVIF shape, consisting of a single style, one or more paths, and optional additional transformation data
pub struct HVIFShape {
  style_index: u8,
  path_indices: Vec<u8>,
  flags: u8,
  transform: Option<HVIFMatrix>,
  translate: Option<HVIFPoint>,
  lod_scale: Option<HVIFLODScale>,
  transformer_list: Vec<HVIFTransformer>
}

#[derive(Debug, Copy, Clone)]
/// Flags that modify the nature of a shape
pub enum HVIFShapeFlags {
  /// ?
  Transform       = 0b0000_0001,
  /// ?
  Hinting         = 0b0000_0010,
  /// ?
  LODScale        = 0b0000_0100,
  /// ?
  HasTransformers = 0b0000_1000,
  /// ?
  Translation     = 0b0001_0000,
}

#[derive(Debug, Copy, Clone)]
/// A 2D affine transformation matrix
/// As per HVIF spec, consists of six f24 values
pub struct HVIFMatrix {
  x1: HVIFf24, y1: HVIFf24, z1: HVIFf24,
  x2: HVIFf24, y2: HVIFf24, z2: HVIFf24,
}
#[derive(Debug, Copy, Clone)]
/// A temporary implementation of an f24 value, as three bytes
pub struct HVIFf24 { fst: u8, snd: u8, thr: u8 }

#[derive(Debug, Copy, Clone)]
/// Level of Detail scales
/// Represents the allowable scales at which this shape will appear correctly
pub struct HVIFLODScale { min: f32, max: f32 }

#[derive(Debug, Copy, Clone)]
/// Some transformation on a basic HVIF shape
pub enum HVIFTransformer {
  /// An affine transformation using a single matrix
  Affine(HVIFMatrix),
  /// ?
  Contour { #[doc="?"] width: f32, #[doc="?"] line_join: u8, #[doc="?"] miter_limit: u8 },
  /// ?
  Perspective,
  /// ?
  Stroke { #[doc="?"] width: f32, #[doc="?"] line_join: u8, #[doc="?"] line_cap: u8, #[doc="?"] miter_limit: u8 }
}
<|endoftext|>