<commit_before>use std::ops::Mul;
use na::{self, Matrix2, Matrix4, Similarity3, U3};

use {gamma, Point3f, Vector2f, Vector3f};

#[derive(Debug, Clone)]
pub struct Transform {
    pub m: Matrix4<f32>,
    pub m_inv: Matrix4<f32>,
}

impl Transform {
    pub fn new(t: Vector3f, r: Vector3f, s: f32) -> Transform {
        Transform::from_similarity(&Similarity3::new(t, r, s))
    }

    pub fn from_similarity(s: &Similarity3<f32>) -> Transform {
        Transform {
            m: s.to_homogeneous(),
            m_inv: s.inverse().to_homogeneous(),
        }
    }

    pub fn rotate(angle: f32, v: Vector3f) -> Transform {
        Transform::new(na::zero(), v * angle.to_radians(), 1.0)
    }

    pub fn rot_x(angle: f32) -> Transform {
        Transform::new(na::zero(), Vector3f::x() * angle.to_radians(), 1.0)
    }

    pub fn rot_y(angle: f32) -> Transform {
        Transform::new(na::zero(), Vector3f::y() * angle.to_radians(), 1.0)
    }

    pub fn rot_z(angle: f32) -> Transform {
        Transform::new(na::zero(), Vector3f::z() * angle.to_radians(), 1.0)
    }

    pub fn rot(ax: f32, ay: f32, az: f32) -> Transform {
        Transform::new(na::zero(), Vector3f::new(ax, ay, az), 1.0)
    }

    pub fn translate(t: Vector3f) -> Transform {
        Transform::new(t, na::zero(), 1.0)
    }

    pub fn translate_x(t: f32) -> Transform {
        Transform::new(Vector3f::x() * t, na::zero(), 1.0)
    }

    pub fn translate_y(t: f32) -> Transform {
        Transform::new(Vector3f::y() * t, na::zero(), 1.0)
    }

    pub fn translate_z(t: f32) -> Transform {
        Transform::new(Vector3f::z() * t, na::zero(), 1.0)
    }

    pub fn scale_uniform(s: f32) -> Transform {
        Transform::new(na::zero(), na::zero(), s)
    }

    pub fn scale(sx: f32, sy: f32, sz: f32) -> Transform {
        let mat = Matrix4::new_nonuniform_scaling(&Vector3f::new(sx, sy, sz));
        Transform {
            m: mat,
            m_inv: mat.try_inverse().unwrap(),
        }
    }

    pub fn inverse(&self) -> Self {
        Transform {
            m: self.m_inv,
            m_inv: self.m,
        }
    }

    /// Transform the given point using the given transformation and also return a vector of the
    /// absolute error introduced for each coordinate.
    pub fn transform_point(&self, p: &Point3f) -> (Point3f, Vector3f) {
        let (x, y, z) = (p.x, p.y, p.z);
        let tp = self * p;
        let m = self.m;
        let x_abs_sum =
            (m[(0, 0)] * x).abs() + (m[(0, 1)] * y).abs() + (m[(0, 2)] * z).abs() + m[(0, 3)].abs();
        let y_abs_sum =
            (m[(1, 0)] * x).abs() + (m[(1, 1)] * y).abs() + (m[(1, 2)] * z).abs() + m[(1, 3)].abs();
        let z_abs_sum =
            (m[(2, 0)] * x).abs() + (m[(2, 1)] * y).abs() + (m[(2, 2)] * z).abs() + m[(2, 3)].abs();
        let p_err = gamma(3) * Vector3f::new(x_abs_sum, y_abs_sum, z_abs_sum);

        (tp, p_err)
    }

    pub fn transform_point_with_error(
        &self,
        p: &Point3f,
        p_error: &Vector3f,
    ) -> (Point3f, Vector3f) {
        let (x, y, z) = (p.x, p.y, p.z);
        let tp = self * p;
        let m = self.m;
        let x_abs_err = (gamma(3) + 1.0)
            * ((m[(0, 0)] * p_error.x).abs() + (m[(0, 1)] * p_error.y).abs()
                + (m[(0, 2)] * p_error.z).abs())
            + gamma(3)
                * ((m[(0, 0)] * x).abs() + (m[(0, 1)] * y).abs() + (m[(0, 2)] * z).abs()
                    + m[(0, 3)].abs());
        let y_abs_err = (gamma(3) + 1.0)
            * ((m[(1, 0)] * p_error.x).abs() + (m[(1, 1)] * p_error.y).abs()
                + (m[(1, 2)] * p_error.z).abs())
            + gamma(3)
                * ((m[(1, 0)] * x).abs() + (m[(1, 1)] * y).abs() + (m[(1, 2)] * z).abs()
                    + m[(1, 3)].abs());
        let z_abs_err = (gamma(3) + 1.0)
            * ((m[(2, 0)] * p_error.x).abs() + (m[(2, 1)] * p_error.y).abs()
                + (m[(2, 2)] * p_error.z).abs())
            + gamma(3)
                * ((m[(2, 0)] * x).abs() + (m[(2, 1)] * y).abs() + (m[(2, 2)] * z).abs()
                    + m[(2, 3)].abs());
        let p_err = Vector3f::new(x_abs_err, y_abs_err, z_abs_err);

        (tp, p_err)
    }

    /// Transform the given point using the given transformation and also return a vector of the
    /// absolute error introduced for each coordinate.
    pub fn transform_vector(&self, v: &Vector3f) -> (Vector3f, Vector3f) {
        let (x, y, z) = (v.x, v.y, v.z);
        let tv = self * v;
        let m = self.m;
        let x_abs_sum = na::abs(&(m[(0, 0)] * x)) + na::abs(&(m[(0, 1)] * y))
            + na::abs(&(m[(0, 2)] * z)) + na::abs(&m[(0, 3)]);
        let y_abs_sum = na::abs(&(m[(1, 0)] * x)) + na::abs(&(m[(1, 1)] * y))
            + na::abs(&(m[(1, 2)] * z)) + na::abs(&m[(1, 3)]);
        let z_abs_sum = na::abs(&(m[(2, 0)] * x)) + na::abs(&(m[(2, 1)] * y))
            + na::abs(&(m[(2, 2)] * z)) + na::abs(&m[(2, 3)]);
        let v_err = gamma(3) * Vector3f::new(x_abs_sum, y_abs_sum, z_abs_sum);

        (tv, v_err)
    }

    pub fn transform_normal(&self, normal: &Vector3f) -> Vector3f {
        let m = self.m_inv.transpose();
        m.fixed_slice::<U3, U3>(0, 0) * *normal
    }
}

impl Default for Transform {
    fn default() -> Self {
        Transform {
            m: na::one(),
            m_inv: na::one(),
        }
    }
}

impl<'a, 'b> Mul<&'a Point3f> for &'b Transform {
    type Output = Point3f;

    fn mul(self, p: &'a Point3f) -> Point3f {
        let x = p.x;
        let y = p.y;
        let z = p.z;
        let xp = self.m[(0, 0)] * x + self.m[(0, 1)] * y + self.m[(0, 2)] * z + self.m[(0, 3)];
        let yp = self.m[(1, 0)] * x + self.m[(1, 1)] * y + self.m[(1, 2)] * z + self.m[(1, 3)];
        let zp = self.m[(2, 0)] * x + self.m[(2, 1)] * y + self.m[(2, 2)] * z + self.m[(2, 3)];
        let wp = self.m[(3, 0)] * x + self.m[(3, 1)] * y + self.m[(3, 2)] * z + self.m[(3, 3)];

        assert!(wp != 0.0);

        if wp == 1.0 {
            Point3f::new(xp, yp, zp)
        } else {
            Point3f::new(xp, yp, zp) / wp
        }
    }
}

impl<'a, 'b> Mul<&'a Vector3f> for &'b Transform {
    type Output = Vector3f;

    fn mul(self, v: &'a Vector3f) -> Vector3f {
        let x = v.x;
        let y = v.y;
        let z = v.z;

        Vector3f::new(
            self.m[(0, 0)] * x + self.m[(0, 1)] * y + self.m[(0, 2)] * z,
            self.m[(1, 0)] * x + self.m[(1, 1)] * y + self.m[(1, 2)] * z,
            self.m[(2, 0)] * x + self.m[(2, 1)] * y + self.m[(2, 2)] * z,
        )
    }
}

impl<'a, 'b> Mul<&'a Transform> for &'b Transform {
    type Output = Transform;

    fn mul(self, t: &'a Transform) -> Transform {
        Transform {
            m: self.m * t.m,
            m_inv: t.m_inv * self.m_inv,
        }
    }
}

#[allow(non_snake_case)]
pub fn solve_linear_system2x2(A: &Matrix2<f32>, B: &Vector2f) -> Option<(f32, f32)> {
    let det = A.determinant();
    if det.abs() < 1e-10 {
        return None;
    }
    let x0 = (A[(1, 1)] * B[0] - A[(0, 1)] * B[1]) / det;
    let x1 = (A[(0, 0)] * B[1] - A[(1, 0)] * B[0]) / det;

    if x0.is_nan() || x1.is_nan() {
        return None;
    }
    return Some((x0, x1));
}

#[test]
fn test_normal_transform() {
    let t = Transform::new(
        Vector3f::new(0.0, 0.0, 0.0),
        Vector3f::new(4.0, 5.0, 6.0),
        4.0,
    );
    let t_inv = t.inverse();

    let v = Vector3f::x();
    let n = Vector3f::y();
    println!("v = {}, n = {}", v, n);
    assert_eq!(v.dot(&n), 0.0);

    let v2 = &t * &v;
    let n2 = t_inv.transform_normal(&n);
    println!("v = {}, n = {}", v2, n2);
    relative_eq!(v2.dot(&n2), 0.0);
}
<commit_msg>Add swaps_handedness() function<commit_after>use std::ops::Mul;
use na::{self, Matrix2, Matrix4, Similarity3, U3};

use {gamma, Point3f, Vector2f, Vector3f};

#[derive(Debug, Clone)]
pub struct Transform {
    pub m: Matrix4<f32>,
    pub m_inv: Matrix4<f32>,
}

impl Transform {
    pub fn new(t: Vector3f, r: Vector3f, s: f32) -> Transform {
        Transform::from_similarity(&Similarity3::new(t, r, s))
    }

    pub fn from_similarity(s: &Similarity3<f32>) -> Transform {
        Transform {
            m: s.to_homogeneous(),
            m_inv: s.inverse().to_homogeneous(),
        }
    }

    pub fn rotate(angle: f32, v: Vector3f) -> Transform {
        Transform::new(na::zero(), v * angle.to_radians(), 1.0)
    }

    pub fn rot_x(angle: f32) -> Transform {
        Transform::new(na::zero(), Vector3f::x() * angle.to_radians(), 1.0)
    }

    pub fn rot_y(angle: f32) -> Transform {
        Transform::new(na::zero(), Vector3f::y() * angle.to_radians(), 1.0)
    }

    pub fn rot_z(angle: f32) -> Transform {
        Transform::new(na::zero(), Vector3f::z() * angle.to_radians(), 1.0)
    }

    pub fn rot(ax: f32, ay: f32, az: f32) -> Transform {
        Transform::new(na::zero(), Vector3f::new(ax, ay, az), 1.0)
    }

    pub fn translate(t: Vector3f) -> Transform {
        Transform::new(t, na::zero(), 1.0)
    }

    pub fn translate_x(t: f32) -> Transform {
        Transform::new(Vector3f::x() * t, na::zero(), 1.0)
    }

    pub fn translate_y(t: f32) -> Transform {
        Transform::new(Vector3f::y() * t, na::zero(), 1.0)
    }

    pub fn translate_z(t: f32) -> Transform {
        Transform::new(Vector3f::z() * t, na::zero(), 1.0)
    }

    pub fn scale_uniform(s: f32) -> Transform {
        Transform::new(na::zero(), na::zero(), s)
    }

    pub fn scale(sx: f32, sy: f32, sz: f32) -> Transform {
        let mat = Matrix4::new_nonuniform_scaling(&Vector3f::new(sx, sy, sz));
        Transform {
            m: mat,
            m_inv: mat.try_inverse().unwrap(),
        }
    }

    pub fn inverse(&self) -> Self {
        Transform {
            m: self.m_inv,
            m_inv: self.m,
        }
    }

    /// Transform the given point using the given transformation and also return a vector of the
    /// absolute error introduced for each coordinate.
    pub fn transform_point(&self, p: &Point3f) -> (Point3f, Vector3f) {
        let (x, y, z) = (p.x, p.y, p.z);
        let tp = self * p;
        let m = self.m;
        let x_abs_sum =
            (m[(0, 0)] * x).abs() + (m[(0, 1)] * y).abs() + (m[(0, 2)] * z).abs() + m[(0, 3)].abs();
        let y_abs_sum =
            (m[(1, 0)] * x).abs() + (m[(1, 1)] * y).abs() + (m[(1, 2)] * z).abs() + m[(1, 3)].abs();
        let z_abs_sum =
            (m[(2, 0)] * x).abs() + (m[(2, 1)] * y).abs() + (m[(2, 2)] * z).abs() + m[(2, 3)].abs();
        let p_err = gamma(3) * Vector3f::new(x_abs_sum, y_abs_sum, z_abs_sum);

        (tp, p_err)
    }

    pub fn transform_point_with_error(
        &self,
        p: &Point3f,
        p_error: &Vector3f,
    ) -> (Point3f, Vector3f) {
        let (x, y, z) = (p.x, p.y, p.z);
        let tp = self * p;
        let m = self.m;
        let x_abs_err = (gamma(3) + 1.0)
            * ((m[(0, 0)] * p_error.x).abs() + (m[(0, 1)] * p_error.y).abs()
                + (m[(0, 2)] * p_error.z).abs())
            + gamma(3)
                * ((m[(0, 0)] * x).abs() + (m[(0, 1)] * y).abs() + (m[(0, 2)] * z).abs()
                    + m[(0, 3)].abs());
        let y_abs_err = (gamma(3) + 1.0)
            * ((m[(1, 0)] * p_error.x).abs() + (m[(1, 1)] * p_error.y).abs()
                + (m[(1, 2)] * p_error.z).abs())
            + gamma(3)
                * ((m[(1, 0)] * x).abs() + (m[(1, 1)] * y).abs() + (m[(1, 2)] * z).abs()
                    + m[(1, 3)].abs());
        let z_abs_err = (gamma(3) + 1.0)
            * ((m[(2, 0)] * p_error.x).abs() + (m[(2, 1)] * p_error.y).abs()
                + (m[(2, 2)] * p_error.z).abs())
            + gamma(3)
                * ((m[(2, 0)] * x).abs() + (m[(2, 1)] * y).abs() + (m[(2, 2)] * z).abs()
                    + m[(2, 3)].abs());
        let p_err = Vector3f::new(x_abs_err, y_abs_err, z_abs_err);

        (tp, p_err)
    }

    /// Transform the given point using the given transformation and also return a vector of the
    /// absolute error introduced for each coordinate.
    pub fn transform_vector(&self, v: &Vector3f) -> (Vector3f, Vector3f) {
        let (x, y, z) = (v.x, v.y, v.z);
        let tv = self * v;
        let m = self.m;
        let x_abs_sum = na::abs(&(m[(0, 0)] * x)) + na::abs(&(m[(0, 1)] * y))
            + na::abs(&(m[(0, 2)] * z)) + na::abs(&m[(0, 3)]);
        let y_abs_sum = na::abs(&(m[(1, 0)] * x)) + na::abs(&(m[(1, 1)] * y))
            + na::abs(&(m[(1, 2)] * z)) + na::abs(&m[(1, 3)]);
        let z_abs_sum = na::abs(&(m[(2, 0)] * x)) + na::abs(&(m[(2, 1)] * y))
            + na::abs(&(m[(2, 2)] * z)) + na::abs(&m[(2, 3)]);
        let v_err = gamma(3) * Vector3f::new(x_abs_sum, y_abs_sum, z_abs_sum);

        (tv, v_err)
    }

    pub fn transform_normal(&self, normal: &Vector3f) -> Vector3f {
        let m = self.m_inv.transpose();
        m.fixed_slice::<U3, U3>(0, 0) * *normal
    }

    pub fn swaps_handedness(&self) -> bool {
        let m = self.m;
        let det = m[(0, 0)] * (m[(1, 1)] * m[(2, 2)] - m[(1, 2)] * m[(2, 1)])
            - m[(0, 1)] * (m[(1, 0)] * m[(2, 2)] - m[(1, 2)] * m[(2, 0)])
            + m[(0, 2)] * (m[(1, 0)] * m[(2, 1)] - m[(1, 1)] * m[(2, 0)]);
        det < 0.0
    }
}

impl Default for Transform {
    fn default() -> Self {
        Transform {
            m: na::one(),
            m_inv: na::one(),
        }
    }
}

impl<'a, 'b> Mul<&'a Point3f> for &'b Transform {
    type Output = Point3f;

    fn mul(self, p: &'a Point3f) -> Point3f {
        let x = p.x;
        let y = p.y;
        let z = p.z;
        let xp = self.m[(0, 0)] * x + self.m[(0, 1)] * y + self.m[(0, 2)] * z + self.m[(0, 3)];
        let yp = self.m[(1, 0)] * x + self.m[(1, 1)] * y + self.m[(1, 2)] * z + self.m[(1, 3)];
        let zp = self.m[(2, 0)] * x + self.m[(2, 1)] * y + self.m[(2, 2)] * z + self.m[(2, 3)];
        let wp = self.m[(3, 0)] * x + self.m[(3, 1)] * y + self.m[(3, 2)] * z + self.m[(3, 3)];

        assert!(wp != 0.0);

        if wp == 1.0 {
            Point3f::new(xp, yp, zp)
        } else {
            Point3f::new(xp, yp, zp) / wp
        }
    }
}

impl<'a, 'b> Mul<&'a Vector3f> for &'b Transform {
    type Output = Vector3f;

    fn mul(self, v: &'a Vector3f) -> Vector3f {
        let x = v.x;
        let y = v.y;
        let z = v.z;

        Vector3f::new(
            self.m[(0, 0)] * x + self.m[(0, 1)] * y + self.m[(0, 2)] * z,
            self.m[(1, 0)] * x + self.m[(1, 1)] * y + self.m[(1, 2)] * z,
            self.m[(2, 0)] * x + self.m[(2, 1)] * y + self.m[(2, 2)] * z,
        )
    }
}

impl<'a, 'b> Mul<&'a Transform> for &'b Transform {
    type Output = Transform;

    fn mul(self, t: &'a Transform) -> Transform {
        Transform {
            m: self.m * t.m,
            m_inv: t.m_inv * self.m_inv,
        }
    }
}

#[allow(non_snake_case)]
pub fn solve_linear_system2x2(A: &Matrix2<f32>, B: &Vector2f) -> Option<(f32, f32)> {
    let det = A.determinant();
    if det.abs() < 1e-10 {
        return None;
    }
    let x0 = (A[(1, 1)] * B[0] - A[(0, 1)] * B[1]) / det;
    let x1 = (A[(0, 0)] * B[1] - A[(1, 0)] * B[0]) / det;

    if x0.is_nan() || x1.is_nan() {
        return None;
    }
    return Some((x0, x1));
}

#[test]
fn test_normal_transform() {
    let t = Transform::new(
        Vector3f::new(0.0, 0.0, 0.0),
        Vector3f::new(4.0, 5.0, 6.0),
        4.0,
    );
    let t_inv = t.inverse();

    let v = Vector3f::x();
    let n = Vector3f::y();
    println!("v = {}, n = {}", v, n);
    assert_eq!(v.dot(&n), 0.0);

    let v2 = &t * &v;
    let n2 = t_inv.transform_normal(&n);
    println!("v = {}, n = {}", v2, n2);
    relative_eq!(v2.dot(&n2), 0.0);
}
<|endoftext|>


<commit_before>extern crate nix;

use nix::unistd;

#[cfg(any(target_os = "linux", target_os = "android"))]
use nix::sys::signalfd::*;

#[cfg(any(target_os = "linux", target_os = "android"))]
fn main() {
    let mut mask = SigSet::empty();
    mask.add(signal::SIGUSR1).unwrap();
    mask.thread_block().unwrap();

    let mut fd = SignalFd::new(&mask).unwrap();

    let pid = unistd::getpid();
    signal::kill(pid, signal::SIGUSR1).unwrap();

    let res = fd.read_signal();
    assert!(res.is_ok());

    let opt = res.ok().unwrap();
    assert!(opt.is_some());

    let info = opt.unwrap();
    assert_eq!(info.ssi_signo as i32, signal::SIGUSR1);
}

#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn main() {}
<commit_msg>Scope test by signalfd<commit_after>#![cfg(feature = "signalfd")]

extern crate nix;

use nix::unistd;

#[cfg(any(target_os = "linux", target_os = "android"))]
use nix::sys::signalfd::*;

#[cfg(any(target_os = "linux", target_os = "android"))]
fn main() {
    let mut mask = SigSet::empty();
    mask.add(signal::SIGUSR1).unwrap();
    mask.thread_block().unwrap();

    let mut fd = SignalFd::new(&mask).unwrap();

    let pid = unistd::getpid();
    signal::kill(pid, signal::SIGUSR1).unwrap();

    let res = fd.read_signal();
    assert!(res.is_ok());

    let opt = res.ok().unwrap();
    assert!(opt.is_some());

    let info = opt.unwrap();
    assert_eq!(info.ssi_signo as i32, signal::SIGUSR1);
}

#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn main() {}
<|endoftext|>


<commit_before>use anyhow::Result;
use reqwest::{blocking::Client, header};

use crate::util::errors::{internal, AppResult};

use std::env;
use std::fs::{self, File};
use std::io::{Cursor, SeekFrom};
use std::path::PathBuf;

use crate::models::Crate;

const CACHE_CONTROL_IMMUTABLE: &str = "public,max-age=31536000,immutable";
const CACHE_CONTROL_README: &str = "public,max-age=604800";
const CACHE_CONTROL_INDEX: &str = "public,max-age=600";

#[derive(Clone, Debug)]
pub enum Uploader {
    /// For production usage, uploads and redirects to s3.
    /// For test usage with `TestApp::with_proxy()`, the recording proxy is used.
    S3 {
        bucket: Box<s3::Bucket>,
        index_bucket: Option<Box<s3::Bucket>>,
        cdn: Option<String>,
    },

    /// For development usage only: "uploads" crate files to `dist` and serves them
    /// from there as well to enable local publishing and download
    Local,
}

pub enum UploadBucket {
    Default,
    Index,
}

impl Uploader {
    /// Returns the URL of an uploaded crate's version archive.
    ///
    /// The function doesn't check for the existence of the file.
    pub fn crate_location(&self, crate_name: &str, version: &str) -> String {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref cdn,
                ..
            } => {
                let host = match *cdn {
                    Some(ref s) => s.clone(),
                    None => bucket.host(),
                };
                let path = Uploader::crate_path(crate_name, version);
                format!("https://{host}/{path}")
            }
            Uploader::Local => format!("/{}", Uploader::crate_path(crate_name, version)),
        }
    }

    /// Returns the URL of an uploaded crate's version readme.
    ///
    /// The function doesn't check for the existence of the file.
    pub fn readme_location(&self, crate_name: &str, version: &str) -> String {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref cdn,
                ..
            } => {
                let host = match *cdn {
                    Some(ref s) => s.clone(),
                    None => bucket.host(),
                };
                let path = Uploader::readme_path(crate_name, version);
                format!("https://{host}/{path}")
            }
            Uploader::Local => format!("/{}", Uploader::readme_path(crate_name, version)),
        }
    }

    /// Returns the internal path of an uploaded crate's version archive.
    fn crate_path(name: &str, version: &str) -> String {
        format!("crates/{name}/{name}-{version}.crate")
    }

    /// Returns the internal path of an uploaded crate's version readme.
    fn readme_path(name: &str, version: &str) -> String {
        format!("readmes/{name}/{name}-{version}.html")
    }

    /// Returns the internal path of an uploaded crate's index file.
    fn index_path(name: &str) -> String {
        cargo_registry_index::Repository::relative_index_file_for_url(name)
    }

    /// Returns the absolute path to the locally uploaded file.
    fn local_uploads_path(path: &str, upload_bucket: UploadBucket) -> PathBuf {
        let path = match upload_bucket {
            UploadBucket::Index => PathBuf::from("index").join(path),
            UploadBucket::Default => PathBuf::from(path),
        };
        env::current_dir().unwrap().join("local_uploads").join(path)
    }

    /// Uploads a file using the configured uploader (either `S3`, `Local`).
    ///
    /// It returns the path of the uploaded file.
    ///
    /// # Panics
    ///
    /// This function can panic on an `Self::Local` during development.
    /// Production and tests use `Self::S3` which should not panic.
    pub fn upload<R: std::io::Read + std::io::Seek + Send + 'static>(
        &self,
        client: &Client,
        path: &str,
        mut content: R,
        content_type: &str,
        extra_headers: header::HeaderMap,
        upload_bucket: UploadBucket,
    ) -> Result<Option<String>> {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref index_bucket,
                ..
            } => {
                let bucket = match upload_bucket {
                    UploadBucket::Default => Some(bucket),
                    UploadBucket::Index => index_bucket.as_ref(),
                };

                if let Some(bucket) = bucket {
                    let content_length = content.seek(SeekFrom::End(0))?;
                    content.seek(SeekFrom::Start(0))?;
                    bucket.put(
                        client,
                        path,
                        content,
                        content_length,
                        content_type,
                        extra_headers,
                    )?;
                }

                Ok(Some(String::from(path)))
            }
            Uploader::Local => {
                let filename = Self::local_uploads_path(path, upload_bucket);
                let dir = filename.parent().unwrap();
                fs::create_dir_all(dir)?;
                let mut file = File::create(&filename)?;
                std::io::copy(&mut content, &mut file)?;
                Ok(filename.to_str().map(String::from))
            }
        }
    }

    /// Deletes a file using the configured uploader (either `S3`, `Local`).
    pub fn delete(&self, client: &Client, path: &str, upload_bucket: UploadBucket) -> Result<()> {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref index_bucket,
                ..
            } => {
                let bucket = match upload_bucket {
                    UploadBucket::Default => Some(bucket),
                    UploadBucket::Index => index_bucket.as_ref(),
                };

                if let Some(bucket) = bucket {
                    bucket.delete(client, path)?;
                }
            }
            Uploader::Local => {
                let filename = Self::local_uploads_path(path, upload_bucket);
                // Ignore errors if the local index file doesn't exist; this can happen if you
                // aren't running the background job worker locally
                let _ = std::fs::remove_file(&filename);
            }
        }
        Ok(())
    }

    /// Uploads a crate and returns the checksum of the uploaded crate file.
    pub fn upload_crate(
        &self,
        http_client: &Client,
        body: Vec<u8>,
        krate: &Crate,
        vers: &semver::Version,
    ) -> AppResult<()> {
        let path = Uploader::crate_path(&krate.name, &vers.to_string());
        let content = Cursor::new(body);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            header::HeaderValue::from_static(CACHE_CONTROL_IMMUTABLE),
        );
        self.upload(
            http_client,
            &path,
            content,
            "application/gzip",
            extra_headers,
            UploadBucket::Default,
        )
        .map_err(|e| internal(&format_args!("failed to upload crate: {}", e)))?;
        Ok(())
    }

    pub(crate) fn upload_readme(
        &self,
        http_client: &Client,
        crate_name: &str,
        vers: &str,
        readme: String,
    ) -> Result<()> {
        let path = Uploader::readme_path(crate_name, vers);
        let content = Cursor::new(readme);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            header::HeaderValue::from_static(CACHE_CONTROL_README),
        );
        self.upload(
            http_client,
            &path,
            content,
            "text/html",
            extra_headers,
            UploadBucket::Default,
        )?;
        Ok(())
    }

    pub(crate) fn upload_index(
        &self,
        http_client: &Client,
        crate_name: &str,
        index: String,
    ) -> Result<()> {
        let path = Uploader::index_path(crate_name);
        let content = Cursor::new(index);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            header::HeaderValue::from_static(CACHE_CONTROL_INDEX),
        );
        self.upload(
            http_client,
            &path,
            content,
            "text/plain",
            extra_headers,
            UploadBucket::Index,
        )?;
        Ok(())
    }

    pub(crate) fn delete_index(&self, http_client: &Client, crate_name: &str) -> Result<()> {
        let path = Uploader::index_path(crate_name);
        self.delete(http_client, &path, UploadBucket::Index)?;
        Ok(())
    }
}
<commit_msg>Uploader: Implement `sync_index()` method<commit_after>use anyhow::Result;
use reqwest::{blocking::Client, header};

use crate::util::errors::{internal, AppResult};

use std::env;
use std::fs::{self, File};
use std::io::{Cursor, SeekFrom};
use std::path::PathBuf;

use crate::models::Crate;

const CACHE_CONTROL_IMMUTABLE: &str = "public,max-age=31536000,immutable";
const CACHE_CONTROL_README: &str = "public,max-age=604800";
const CACHE_CONTROL_INDEX: &str = "public,max-age=600";

#[derive(Clone, Debug)]
pub enum Uploader {
    /// For production usage, uploads and redirects to s3.
    /// For test usage with `TestApp::with_proxy()`, the recording proxy is used.
    S3 {
        bucket: Box<s3::Bucket>,
        index_bucket: Option<Box<s3::Bucket>>,
        cdn: Option<String>,
    },

    /// For development usage only: "uploads" crate files to `dist` and serves them
    /// from there as well to enable local publishing and download
    Local,
}

pub enum UploadBucket {
    Default,
    Index,
}

impl Uploader {
    /// Returns the URL of an uploaded crate's version archive.
    ///
    /// The function doesn't check for the existence of the file.
    pub fn crate_location(&self, crate_name: &str, version: &str) -> String {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref cdn,
                ..
            } => {
                let host = match *cdn {
                    Some(ref s) => s.clone(),
                    None => bucket.host(),
                };
                let path = Uploader::crate_path(crate_name, version);
                format!("https://{host}/{path}")
            }
            Uploader::Local => format!("/{}", Uploader::crate_path(crate_name, version)),
        }
    }

    /// Returns the URL of an uploaded crate's version readme.
    ///
    /// The function doesn't check for the existence of the file.
    pub fn readme_location(&self, crate_name: &str, version: &str) -> String {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref cdn,
                ..
            } => {
                let host = match *cdn {
                    Some(ref s) => s.clone(),
                    None => bucket.host(),
                };
                let path = Uploader::readme_path(crate_name, version);
                format!("https://{host}/{path}")
            }
            Uploader::Local => format!("/{}", Uploader::readme_path(crate_name, version)),
        }
    }

    /// Returns the internal path of an uploaded crate's version archive.
    fn crate_path(name: &str, version: &str) -> String {
        format!("crates/{name}/{name}-{version}.crate")
    }

    /// Returns the internal path of an uploaded crate's version readme.
    fn readme_path(name: &str, version: &str) -> String {
        format!("readmes/{name}/{name}-{version}.html")
    }

    /// Returns the internal path of an uploaded crate's index file.
    fn index_path(name: &str) -> String {
        cargo_registry_index::Repository::relative_index_file_for_url(name)
    }

    /// Returns the absolute path to the locally uploaded file.
    fn local_uploads_path(path: &str, upload_bucket: UploadBucket) -> PathBuf {
        let path = match upload_bucket {
            UploadBucket::Index => PathBuf::from("index").join(path),
            UploadBucket::Default => PathBuf::from(path),
        };
        env::current_dir().unwrap().join("local_uploads").join(path)
    }

    /// Uploads a file using the configured uploader (either `S3`, `Local`).
    ///
    /// It returns the path of the uploaded file.
    ///
    /// # Panics
    ///
    /// This function can panic on an `Self::Local` during development.
    /// Production and tests use `Self::S3` which should not panic.
    pub fn upload<R: std::io::Read + std::io::Seek + Send + 'static>(
        &self,
        client: &Client,
        path: &str,
        mut content: R,
        content_type: &str,
        extra_headers: header::HeaderMap,
        upload_bucket: UploadBucket,
    ) -> Result<Option<String>> {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref index_bucket,
                ..
            } => {
                let bucket = match upload_bucket {
                    UploadBucket::Default => Some(bucket),
                    UploadBucket::Index => index_bucket.as_ref(),
                };

                if let Some(bucket) = bucket {
                    let content_length = content.seek(SeekFrom::End(0))?;
                    content.seek(SeekFrom::Start(0))?;
                    bucket.put(
                        client,
                        path,
                        content,
                        content_length,
                        content_type,
                        extra_headers,
                    )?;
                }

                Ok(Some(String::from(path)))
            }
            Uploader::Local => {
                let filename = Self::local_uploads_path(path, upload_bucket);
                let dir = filename.parent().unwrap();
                fs::create_dir_all(dir)?;
                let mut file = File::create(&filename)?;
                std::io::copy(&mut content, &mut file)?;
                Ok(filename.to_str().map(String::from))
            }
        }
    }

    /// Deletes a file using the configured uploader (either `S3`, `Local`).
    pub fn delete(&self, client: &Client, path: &str, upload_bucket: UploadBucket) -> Result<()> {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref index_bucket,
                ..
            } => {
                let bucket = match upload_bucket {
                    UploadBucket::Default => Some(bucket),
                    UploadBucket::Index => index_bucket.as_ref(),
                };

                if let Some(bucket) = bucket {
                    bucket.delete(client, path)?;
                }
            }
            Uploader::Local => {
                let filename = Self::local_uploads_path(path, upload_bucket);
                // Ignore errors if the local index file doesn't exist; this can happen if you
                // aren't running the background job worker locally
                let _ = std::fs::remove_file(&filename);
            }
        }
        Ok(())
    }

    /// Uploads a crate and returns the checksum of the uploaded crate file.
    pub fn upload_crate(
        &self,
        http_client: &Client,
        body: Vec<u8>,
        krate: &Crate,
        vers: &semver::Version,
    ) -> AppResult<()> {
        let path = Uploader::crate_path(&krate.name, &vers.to_string());
        let content = Cursor::new(body);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            header::HeaderValue::from_static(CACHE_CONTROL_IMMUTABLE),
        );
        self.upload(
            http_client,
            &path,
            content,
            "application/gzip",
            extra_headers,
            UploadBucket::Default,
        )
        .map_err(|e| internal(&format_args!("failed to upload crate: {}", e)))?;
        Ok(())
    }

    pub(crate) fn upload_readme(
        &self,
        http_client: &Client,
        crate_name: &str,
        vers: &str,
        readme: String,
    ) -> Result<()> {
        let path = Uploader::readme_path(crate_name, vers);
        let content = Cursor::new(readme);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            header::HeaderValue::from_static(CACHE_CONTROL_README),
        );
        self.upload(
            http_client,
            &path,
            content,
            "text/html",
            extra_headers,
            UploadBucket::Default,
        )?;
        Ok(())
    }

    pub(crate) fn upload_index(
        &self,
        http_client: &Client,
        crate_name: &str,
        index: String,
    ) -> Result<()> {
        let path = Uploader::index_path(crate_name);
        let content = Cursor::new(index);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            header::HeaderValue::from_static(CACHE_CONTROL_INDEX),
        );
        self.upload(
            http_client,
            &path,
            content,
            "text/plain",
            extra_headers,
            UploadBucket::Index,
        )?;
        Ok(())
    }

    pub(crate) fn delete_index(&self, http_client: &Client, crate_name: &str) -> Result<()> {
        let path = Uploader::index_path(crate_name);
        self.delete(http_client, &path, UploadBucket::Index)?;
        Ok(())
    }

    pub(crate) fn sync_index(
        &self,
        http_client: &Client,
        crate_name: &str,
        index: Option<String>,
    ) -> Result<()> {
        if let Some(index) = index {
            self.upload_index(http_client, crate_name, index)
        } else {
            self.delete_index(http_client, crate_name)
        }
    }
}
<|endoftext|>


<commit_before>/* Copyright 2016 Torbjørn Birch Moltu
 *
 * Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
 * http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
 * http://opensource.org/licenses/MIT>, at your option. This file may not be
 * copied, modified, or distributed except according to those terms.
 */

pub use errors::FromStrError;
use error::{InvalidUtf8Slice,InvalidUtf8Array};
use Utf8Iterator;
use CharExt;
use U8UtfExt;
extern crate std;
use std::{hash,fmt, str};
use std::borrow::Borrow;
use std::ops::Deref;
use std::mem::transmute;

use std::ascii::AsciiExt;
#[cfg(feature="ascii")]
extern crate ascii;
#[cfg(feature="ascii")]
use self::ascii::{AsciiChar,ToAsciiChar,ToAsciiCharError};

// I don't think there is any good default value for char, but char does.
#[derive(Default)]
// char doesn't do anything more advanced than u32 for Eq/Ord, so we shouldn't either.
// The default impl of Ord for arrays works out because longer codepoints
//     start with more ones, so if they're equal, the length is the same,
// breaks down for values above 0x1f_ff_ff but those can only be created by unsafe code.
#[derive(PartialEq,Eq, PartialOrd,Ord)]

#[derive(Clone,Copy)]

/// Store a `char` as UTF-8 so it can be borrowed as a `str`
///
/// Has the same size as `char`, and is a `[u8;4]`
/// with the invariant that the first nth bytes are valid UTF-8, and the remaining are zero.
pub struct Utf8Char {
    bytes: [u8; 4],
}

  /////////////////////
 //conversion traits//
/////////////////////
impl str::FromStr for Utf8Char {
    type Err = FromStrError;
    /// The string must contain exactly one codepoint
    fn from_str(s: &str) -> Result<Self, FromStrError> {
        let mut uc = Utf8Char::default();
        for (src, dst) in s.as_bytes().iter().zip(uc.bytes.iter_mut()) {
            *dst = *src;// happens min(s.len(), 4) times
        }
        if uc.len() == s.len() {Ok(uc)}
        else if s.is_empty()   {Err(FromStrError::Empty)}
        else                   {Err(FromStrError::SeveralCodePoints)}
    }
}
impl From<char> for Utf8Char {
    fn from(c: char) -> Self {
        Utf8Char{ bytes: c.to_utf8_array().0 }
    }
}
impl From<Utf8Char> for char {
    fn from(uc: Utf8Char) -> char {
        unsafe{ char::from_utf8_exact_slice_unchecked(&uc.bytes[..uc.len()]) }
    }
}
impl IntoIterator for Utf8Char {
    type Item=u8;
    type IntoIter=Utf8Iterator;
    /// Iterate over the byte values.
    fn into_iter(self) -> Utf8Iterator {
        Utf8Iterator::from(self)
    }
}

  /////////////////
 //getter traits//
/////////////////
impl AsRef<[u8]> for Utf8Char {
    fn as_ref(&self) -> &[u8] {
        &self.bytes[..self.len()]
    }
}
impl AsRef<str> for Utf8Char {
    fn as_ref(&self) -> &str {
        unsafe{ std::str::from_utf8_unchecked( self.as_ref() ) }
    }
}
impl Borrow<[u8]> for Utf8Char {
    fn borrow(&self) -> &[u8] {
        self.as_ref()
    }
}
impl Borrow<str> for Utf8Char {
    fn borrow(&self) -> &str {
        self.as_ref()
    }
}
impl Deref for Utf8Char {
    type Target = str;
    fn deref(&self) -> &Self::Target {
        self.as_ref()
    }
}

  ////////////////
 //ascii traits//
////////////////
impl AsciiExt for Utf8Char {
    type Owned = Utf8Char;
    fn is_ascii(&self) -> bool {
        self.bytes[0].is_ascii()
    }
    fn eq_ignore_ascii_case(&self,  other: &Self) -> bool {
        self.to_char().eq_ignore_ascii_case(&other.to_char())
    }
    fn to_ascii_uppercase(&self) -> Self::Owned {
        let ascii = self.bytes[0].to_ascii_uppercase();
        if ascii == self.bytes[0] {*self}
        else {Utf8Char{ bytes: [ascii,0,0,0] }}
    }
    fn to_ascii_lowercase(&self) -> Self::Owned {
        let ascii = self.bytes[0].to_ascii_lowercase();
        if ascii == self.bytes[0] {*self}// unchanged
        else {Utf8Char{ bytes: [ascii,0,0,0] }}// is ascii
    }
    fn make_ascii_uppercase(&mut self) {
        *self = self.to_ascii_uppercase()
    }
    fn make_ascii_lowercase(&mut self) {
        *self = self.to_ascii_lowercase();
    }
}

#[cfg(feature="ascii")]
/// Requires feature "ascii".
impl From<AsciiChar> for Utf8Char {
    fn from(ac: AsciiChar) -> Self {
        Utf8Char{ bytes: [ac.as_byte(),0,0,0] }
    }
}
#[cfg(feature="ascii")]
/// Requires feature "asciiChar".
impl ToAsciiChar for Utf8Char {
    fn to_ascii_char(self) -> Result<AsciiChar, ToAsciiCharError> {
        self.bytes[0].to_ascii_char()
    }
    unsafe fn to_ascii_char_unchecked(self) -> AsciiChar {
        self.bytes[0].to_ascii_char_unchecked()
    }
}

  /////////////////////////////////////////////////////////
 //Genaral traits that cannot be derived to emulate char//
/////////////////////////////////////////////////////////
impl hash::Hash for Utf8Char {
    fn hash<H : hash::Hasher>(&self,  state: &mut H) {
        self.to_char().hash(state);
    }
}
impl fmt::Debug for Utf8Char {
    fn fmt(&self,  fmtr: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.to_char(), fmtr)
    }
}

  ///////////////////////////////////////////////////////
 //pub impls that should be together for nicer rustdoc//
///////////////////////////////////////////////////////
impl Utf8Char {
    /// Validate the start of a UTF-8 slice and store it.
    /// Also returns how many bytes were needed.
    ///
    /// If it's a str and you know it contains only one codepoint,
    /// use `.from_str()` to skip the validation.
    pub fn from_slice_start(src: &[u8]) -> Result<(Self,usize),InvalidUtf8Slice> {
        // need to convert to char to check codepoint, but not convert back.
        let len = try!(char::from_utf8_slice(src)).1;
        let mut uc = Self::default();
        for (dst, src) in uc.bytes.iter_mut().zip(src).take(len) {
            *dst = *src;
        }
        Ok((uc,len))
    }
    /// Validate the array and store it.
    pub fn from_array(utf8: [u8;4]) -> Result<Self,InvalidUtf8Array> {
        try!(char::from_utf8_array(utf8));
        let len = Utf8Char{ bytes: utf8 }.len() as u32;
        let mask = u32::from_le(0xff_ff_ff_ff >> 8*(4-len));
        let unused_zeroed = mask  &  unsafe{ transmute::<_,u32>(utf8) };
        Ok(unsafe{ transmute(unused_zeroed) })
    }

    /// Result is 1...4 and identical to `.as_ref().len()` or `.as_char().len_utf8()`.
    /// There is no .is_emty() because it would always return false.
    pub fn len(self) -> usize {
        self.bytes[0].extra_utf8_bytes_unchecked() + 1
    }

    /// Convert from UTF-8 to UTF-32
    pub fn to_char(self) -> char {
        self.into()
    }
    /// Write the internal representation to a slice,
    /// and then returns the number of bytes written.
    ///
    /// # Panics
    /// Will panic the buffer is too small;
    /// You can get the required length from `.len()`,
    /// but a buffer of length four is always large enough.
    pub fn to_slice(self,  dst: &mut[u8]) -> usize {
        if self.len() > dst.len() {
            panic!("The provided buffer is too small.");
        }
        for (dst, src) in dst.iter_mut().zip(self.bytes.iter()) {
            *dst  = *src;
        }
        self.len()
    }
    /// Expose the internal array and the number of used bytes.
    pub fn to_array(self) -> ([u8;4],usize) {
        (self.bytes, self.len())
    }
}
<commit_msg>Fix bug in CharExt::write_utf8() and Utf8Char::to_slice()<commit_after>/* Copyright 2016 Torbjørn Birch Moltu
 *
 * Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
 * http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
 * http://opensource.org/licenses/MIT>, at your option. This file may not be
 * copied, modified, or distributed except according to those terms.
 */

pub use errors::FromStrError;
use error::{InvalidUtf8Slice,InvalidUtf8Array};
use Utf8Iterator;
use CharExt;
use U8UtfExt;
extern crate std;
use std::{hash,fmt, str};
use std::borrow::Borrow;
use std::ops::Deref;
use std::mem::transmute;

use std::ascii::AsciiExt;
#[cfg(feature="ascii")]
extern crate ascii;
#[cfg(feature="ascii")]
use self::ascii::{AsciiChar,ToAsciiChar,ToAsciiCharError};

// I don't think there is any good default value for char, but char does.
#[derive(Default)]
// char doesn't do anything more advanced than u32 for Eq/Ord, so we shouldn't either.
// The default impl of Ord for arrays works out because longer codepoints
//     start with more ones, so if they're equal, the length is the same,
// breaks down for values above 0x1f_ff_ff but those can only be created by unsafe code.
#[derive(PartialEq,Eq, PartialOrd,Ord)]

#[derive(Clone,Copy)]

/// Store a `char` as UTF-8 so it can be borrowed as a `str`
///
/// Has the same size as `char`, and is a `[u8;4]`
/// with the invariant that the first nth bytes are valid UTF-8, and the remaining are zero.
pub struct Utf8Char {
    bytes: [u8; 4],
}

  /////////////////////
 //conversion traits//
/////////////////////
impl str::FromStr for Utf8Char {
    type Err = FromStrError;
    /// The string must contain exactly one codepoint
    fn from_str(s: &str) -> Result<Self, FromStrError> {
        let mut uc = Utf8Char::default();
        for (src, dst) in s.as_bytes().iter().zip(uc.bytes.iter_mut()) {
            *dst = *src;// happens min(s.len(), 4) times
        }
        if uc.len() == s.len() {Ok(uc)}
        else if s.is_empty()   {Err(FromStrError::Empty)}
        else                   {Err(FromStrError::SeveralCodePoints)}
    }
}
impl From<char> for Utf8Char {
    fn from(c: char) -> Self {
        Utf8Char{ bytes: c.to_utf8_array().0 }
    }
}
impl From<Utf8Char> for char {
    fn from(uc: Utf8Char) -> char {
        unsafe{ char::from_utf8_exact_slice_unchecked(&uc.bytes[..uc.len()]) }
    }
}
impl IntoIterator for Utf8Char {
    type Item=u8;
    type IntoIter=Utf8Iterator;
    /// Iterate over the byte values.
    fn into_iter(self) -> Utf8Iterator {
        Utf8Iterator::from(self)
    }
}

  /////////////////
 //getter traits//
/////////////////
impl AsRef<[u8]> for Utf8Char {
    fn as_ref(&self) -> &[u8] {
        &self.bytes[..self.len()]
    }
}
impl AsRef<str> for Utf8Char {
    fn as_ref(&self) -> &str {
        unsafe{ std::str::from_utf8_unchecked( self.as_ref() ) }
    }
}
impl Borrow<[u8]> for Utf8Char {
    fn borrow(&self) -> &[u8] {
        self.as_ref()
    }
}
impl Borrow<str> for Utf8Char {
    fn borrow(&self) -> &str {
        self.as_ref()
    }
}
impl Deref for Utf8Char {
    type Target = str;
    fn deref(&self) -> &Self::Target {
        self.as_ref()
    }
}

  ////////////////
 //ascii traits//
////////////////
impl AsciiExt for Utf8Char {
    type Owned = Utf8Char;
    fn is_ascii(&self) -> bool {
        self.bytes[0].is_ascii()
    }
    fn eq_ignore_ascii_case(&self,  other: &Self) -> bool {
        self.to_char().eq_ignore_ascii_case(&other.to_char())
    }
    fn to_ascii_uppercase(&self) -> Self::Owned {
        let ascii = self.bytes[0].to_ascii_uppercase();
        if ascii == self.bytes[0] {*self}
        else {Utf8Char{ bytes: [ascii,0,0,0] }}
    }
    fn to_ascii_lowercase(&self) -> Self::Owned {
        let ascii = self.bytes[0].to_ascii_lowercase();
        if ascii == self.bytes[0] {*self}// unchanged
        else {Utf8Char{ bytes: [ascii,0,0,0] }}// is ascii
    }
    fn make_ascii_uppercase(&mut self) {
        *self = self.to_ascii_uppercase()
    }
    fn make_ascii_lowercase(&mut self) {
        *self = self.to_ascii_lowercase();
    }
}

#[cfg(feature="ascii")]
/// Requires feature "ascii".
impl From<AsciiChar> for Utf8Char {
    fn from(ac: AsciiChar) -> Self {
        Utf8Char{ bytes: [ac.as_byte(),0,0,0] }
    }
}
#[cfg(feature="ascii")]
/// Requires feature "asciiChar".
impl ToAsciiChar for Utf8Char {
    fn to_ascii_char(self) -> Result<AsciiChar, ToAsciiCharError> {
        self.bytes[0].to_ascii_char()
    }
    unsafe fn to_ascii_char_unchecked(self) -> AsciiChar {
        self.bytes[0].to_ascii_char_unchecked()
    }
}

  /////////////////////////////////////////////////////////
 //Genaral traits that cannot be derived to emulate char//
/////////////////////////////////////////////////////////
impl hash::Hash for Utf8Char {
    fn hash<H : hash::Hasher>(&self,  state: &mut H) {
        self.to_char().hash(state);
    }
}
impl fmt::Debug for Utf8Char {
    fn fmt(&self,  fmtr: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.to_char(), fmtr)
    }
}

  ///////////////////////////////////////////////////////
 //pub impls that should be together for nicer rustdoc//
///////////////////////////////////////////////////////
impl Utf8Char {
    /// Validate the start of a UTF-8 slice and store it.
    /// Also returns how many bytes were needed.
    ///
    /// If it's a str and you know it contains only one codepoint,
    /// use `.from_str()` to skip the validation.
    pub fn from_slice_start(src: &[u8]) -> Result<(Self,usize),InvalidUtf8Slice> {
        // need to convert to char to check codepoint, but not convert back.
        let len = try!(char::from_utf8_slice(src)).1;
        let mut uc = Self::default();
        for (dst, src) in uc.bytes.iter_mut().zip(src).take(len) {
            *dst = *src;
        }
        Ok((uc,len))
    }
    /// Validate the array and store it.
    pub fn from_array(utf8: [u8;4]) -> Result<Self,InvalidUtf8Array> {
        try!(char::from_utf8_array(utf8));
        let len = Utf8Char{ bytes: utf8 }.len() as u32;
        let mask = u32::from_le(0xff_ff_ff_ff >> 8*(4-len));
        let unused_zeroed = mask  &  unsafe{ transmute::<_,u32>(utf8) };
        Ok(unsafe{ transmute(unused_zeroed) })
    }

    /// Result is 1...4 and identical to `.as_ref().len()` or `.as_char().len_utf8()`.
    /// There is no .is_emty() because it would always return false.
    pub fn len(self) -> usize {
        self.bytes[0].extra_utf8_bytes_unchecked() + 1
    }

    /// Convert from UTF-8 to UTF-32
    pub fn to_char(self) -> char {
        self.into()
    }
    /// Write the internal representation to a slice,
    /// and then returns the number of bytes written.
    ///
    /// # Panics
    /// Will panic the buffer is too small;
    /// You can get the required length from `.len()`,
    /// but a buffer of length four is always large enough.
    pub fn to_slice(self,  dst: &mut[u8]) -> usize {
        if self.len() > dst.len() {
            panic!("The provided buffer is too small.");
        }
        dst[..self.len()].copy_from_slice(&self.bytes[..self.len()]);
        self.len()
    }
    /// Expose the internal array and the number of used bytes.
    pub fn to_array(self) -> ([u8;4],usize) {
        (self.bytes, self.len())
    }
}
<|endoftext|>


<commit_before>use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

// http://json.schemastore.org/swagger-2.0

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Scheme {
    Http,
    Https,
    Ws,
    Wss,
}

impl Default for Scheme {
    fn default() -> Self {
        Scheme::Http
    }
}

/// top level document
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct Spec {
    /// The Swagger version of this document.
    pub swagger: String,
    pub info: Info,
    /// The host (name or ip) of the API. Example: 'swagger.io'
    /// ^[^{}/ :\\\\]+(?::\\d+)?$
    #[serde(skip_serializing_if = "Option::is_none")]
    pub host: Option<String>,
    /// The base path to the API. Example: '/api'.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "basePath")]
    pub base_path: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub schemes: Option<Vec<Scheme>>,
    /// A list of MIME types accepted by the API.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub consumes: Option<Vec<String>>,
    /// A list of MIME types the API can produce.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub produces: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tags: Option<Vec<Tag>>,
    /// Relative paths to the individual endpoints. They must be relative
    /// to the 'basePath'.
    pub paths: BTreeMap<String, PathItem>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub definitions: Option<BTreeMap<String, Schema>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parameters: Option<BTreeMap<String, Parameter>>,
    /// mappings to http response codes or "default"
    #[serde(skip_serializing_if = "Option::is_none")]
    pub responses: Option<BTreeMap<String, Response>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub security_definitions: Option<BTreeMap<String, Security>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub security: Option<Vec<BTreeMap<String, Vec<String>>>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub external_docs: Option<Vec<ExternalDoc>>,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub struct Tag {
    pub name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub external_docs: Option<Vec<ExternalDoc>>,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct ExternalDoc {
    pub url: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
}

/// General information about the API.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub struct Info {
    /// A unique and precise title of the API.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    /// A semantic version number of the API.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub terms_of_service: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub contact: Option<Contact>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub license: Option<License>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct Contact {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    // TODO: Make sure the url is a valid URL
    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,
    // TODO: Make sure the email is a valid email
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email: Option<String>,
}

/// todo x-* properties
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct License {
    /// The name of the license type. It's encouraged to use an OSI
    /// compatible license.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    /// The URL pointing to the license.
    // TODO: Make sure the url is a valid URL
    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,
}

/// todo support x-* properties
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct PathItem {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub get: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub post: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub put: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub patch: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub delete: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub options: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub head: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parameters: Option<Vec<ParameterOrRef>>,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub struct Operation {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub summary: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub consumes: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub produces: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub schemes: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tags: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub operation_id: Option<String>,
    pub responses: BTreeMap<String, Response>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parameters: Option<Vec<ParameterOrRef>>,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct Parameter {
    pub name: String,
    #[serde(rename = "in")]
    pub location: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub required: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub schema: Option<Schema>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub unique_items: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "type")]
    pub param_type: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub format: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct Response {
    pub description: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub schema: Option<Schema>,
}

// todo: support x-* fields
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(untagged)]
pub enum ParameterOrRef {
    /// both bodyParameter and nonBodyParameter in one for now
    #[derive(Default)]
    Parameter {
        /// The name of the parameter.
        name: String,
        /// values depend on parameter type
        /// may be `header`, `query`, 'path`, `formData`
        #[serde(rename = "in")]
        location: String,
        #[serde(skip_serializing_if = "Option::is_none")]
        required: Option<bool>,
        #[serde(skip_serializing_if = "Option::is_none")]
        schema: Option<Schema>,
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(rename = "uniqueItems")]
        unique_items: Option<bool>,
        /// string, number, boolean, integer, array, file ( only for formData )
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(rename = "type")]
        param_type: Option<String>,
        #[serde(skip_serializing_if = "Option::is_none")]
        format: Option<String>,
        /// A brief description of the parameter. This could contain examples
        /// of use.  GitHub Flavored Markdown is allowed.
        #[serde(skip_serializing_if = "Option::is_none")]
        description: Option<String>,
        // collectionFormat: ???
        // default: ???
        // maximum ?
        // exclusiveMaximum ??
        // minimum ??
        // exclusiveMinimum ??
        // maxLength ??
        // minLength ??
        // pattern ??
        // maxItems ??
        // minItems ??
        // enum ??
        // multipleOf ??
        // allowEmptyValue ( for query / body params )
    },
    Ref {
        #[serde(rename = "$ref")]
        ref_path: String,
    },
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(tag = "type")]
pub enum Security {
    #[serde(rename = "apiKey")]
    ApiKey {
        name: String,
        #[serde(rename = "in")]
        location: String,
    },
    #[serde(rename = "oauth2")]
    Oauth2 {
        flow: String,
        #[serde(rename = "authorizationUrl")]
        authorization_url: String,
        #[serde(rename = "tokenUrl")]
        #[serde(skip_serializing_if = "Option::is_none")]
        token_url: Option<String>,
        scopes: BTreeMap<String, String>,
    },
    #[serde(rename = "basic")]
    Basic,
}

/// A [JSON schema](http://json-schema.org/) definition describing
/// the shape and properties of an object.
///
/// This may also contain a `$ref` to another definition
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct Schema {
    #[serde(skip_serializing_if = "Option::is_none")]
    /// [JSON reference](https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03)
    /// path to another defintion
    #[serde(rename = "$ref")]
    pub ref_path: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "type")]
    pub schema_type: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub format: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "enum")]
    pub enum_values: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub required: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub items: Option<Box<Schema>>,
    // implies object
    #[serde(skip_serializing_if = "Option::is_none")]
    pub properties: Option<BTreeMap<String, Schema>>,
}

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

    #[test]
    fn security_api_deserializes() {
        let json = r#"{"type":"apiKey", "name":"foo", "in": "query"}"#;
        assert_eq!(
            serde_yaml::from_str::<Security>(&json).unwrap(),
            Security::ApiKey {
                name: "foo".into(),
                location: "query".into(),
            }
        );
    }

    #[test]
    fn security_api_serializes() {
        let json = r#"{"type":"apiKey","name":"foo","in":"query"}"#;
        assert_eq!(
            serde_json::to_string(&Security::ApiKey {
                name: "foo".into(),
                location: "query".into(),
            })
            .unwrap(),
            json
        );
    }

    #[test]
    fn security_basic_deserializes() {
        let json = r#"{"type":"basic"}"#;
        assert_eq!(
            serde_yaml::from_str::<Security>(&json).unwrap(),
            Security::Basic
        );
    }

    #[test]
    fn security_basic_serializes() {
        let json = r#"{"type":"basic"}"#;
        assert_eq!(json, serde_json::to_string(&Security::Basic).unwrap());
    }

    #[test]
    fn security_oauth_deserializes() {
        let json = r#"{"type":"oauth2","flow":"implicit","authorizationUrl":"foo/bar","scopes":{"foo":"bar"}}"#;
        let mut scopes = BTreeMap::new();
        scopes.insert("foo".into(), "bar".into());
        assert_eq!(
            serde_yaml::from_str::<Security>(&json).unwrap(),
            Security::Oauth2 {
                flow: "implicit".into(),
                authorization_url: "foo/bar".into(),
                token_url: None,
                scopes: scopes,
            }
        );
    }

    #[test]
    fn security_oauth_serializes() {
        let json = r#"{"type":"oauth2","flow":"implicit","authorizationUrl":"foo/bar","scopes":{"foo":"bar"}}"#;
        let mut scopes = BTreeMap::new();
        scopes.insert("foo".into(), "bar".into());
        assert_eq!(
            json,
            serde_json::to_string(&Security::Oauth2 {
                flow: "implicit".into(),
                authorization_url: "foo/bar".into(),
                token_url: None,
                scopes: scopes,
            })
            .unwrap()
        );
    }

    #[test]
    fn parameter_or_ref_deserializes_ref() {
        let json = r#"{"$ref":"foo/bar"}"#;
        assert_eq!(
            serde_yaml::from_str::<ParameterOrRef>(&json).unwrap(),
            ParameterOrRef::Ref {
                ref_path: "foo/bar".into()
            }
        );
    }

    #[test]
    fn parameter_or_ref_serializes_pref() {
        let json = r#"{"$ref":"foo/bar"}"#;
        assert_eq!(
            json,
            serde_json::to_string(&ParameterOrRef::Ref {
                ref_path: "foo/bar".into()
            },)
            .unwrap()
        );
    }
}
<commit_msg>add a few missing fields that showed up in tests<commit_after>use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

// http://json.schemastore.org/swagger-2.0

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Scheme {
    Http,
    Https,
    Ws,
    Wss,
}

impl Default for Scheme {
    fn default() -> Self {
        Scheme::Http
    }
}

/// top level document
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct Spec {
    /// The Swagger version of this document.
    pub swagger: String,
    pub info: Info,
    /// The host (name or ip) of the API. Example: 'swagger.io'
    /// ^[^{}/ :\\\\]+(?::\\d+)?$
    #[serde(skip_serializing_if = "Option::is_none")]
    pub host: Option<String>,
    /// The base path to the API. Example: '/api'.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "basePath")]
    pub base_path: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub schemes: Option<Vec<Scheme>>,
    /// A list of MIME types accepted by the API.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub consumes: Option<Vec<String>>,
    /// A list of MIME types the API can produce.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub produces: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tags: Option<Vec<Tag>>,
    /// Relative paths to the individual endpoints. They must be relative
    /// to the 'basePath'.
    pub paths: BTreeMap<String, PathItem>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub definitions: Option<BTreeMap<String, Schema>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parameters: Option<BTreeMap<String, Parameter>>,
    /// mappings to http response codes or "default"
    #[serde(skip_serializing_if = "Option::is_none")]
    pub responses: Option<BTreeMap<String, Response>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub security_definitions: Option<BTreeMap<String, Security>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub security: Option<Vec<BTreeMap<String, Vec<String>>>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub external_docs: Option<Vec<ExternalDoc>>,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub struct Tag {
    pub name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub external_docs: Option<Vec<ExternalDoc>>,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct ExternalDoc {
    pub url: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
}

/// General information about the API.
///
/// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#info-object
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub struct Info {
    /// A unique and precise title of the API.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    /// A semantic version number of the API.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(rename = "termsOfService", skip_serializing_if = "Option::is_none")]
    pub terms_of_service: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub contact: Option<Contact>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub license: Option<License>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct Contact {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    // TODO: Make sure the url is a valid URL
    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,
    // TODO: Make sure the email is a valid email
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email: Option<String>,
}

/// todo x-* properties
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct License {
    /// The name of the license type. It's encouraged to use an OSI
    /// compatible license.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    /// The URL pointing to the license.
    // TODO: Make sure the url is a valid URL
    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,
}

/// todo support x-* properties
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct PathItem {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub get: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub post: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub put: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub patch: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub delete: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub options: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub head: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parameters: Option<Vec<ParameterOrRef>>,
}

/// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#operation-object
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub struct Operation {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub summary: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub consumes: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub produces: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub schemes: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tags: Option<Vec<String>>,
    #[serde(rename="operationId", skip_serializing_if = "Option::is_none")]
    pub operation_id: Option<String>,
    pub responses: BTreeMap<String, Response>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parameters: Option<Vec<ParameterOrRef>>,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct Parameter {
    pub name: String,
    #[serde(rename = "in")]
    pub location: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub required: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub schema: Option<Schema>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub unique_items: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "type")]
    pub param_type: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub format: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct Response {
    pub description: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub schema: Option<Schema>,
}

// todo: support x-* fields
/// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#parameter-object
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(untagged)]
pub enum ParameterOrRef {
    /// both bodyParameter and nonBodyParameter in one for now
    #[derive(Default)]
    Parameter {
        /// The name of the parameter.
        name: String,
        /// values depend on parameter type
        /// may be `header`, `query`, 'path`, `formData`
        #[serde(rename = "in")]
        location: String,
        #[serde(skip_serializing_if = "Option::is_none")]
        required: Option<bool>,
        #[serde(skip_serializing_if = "Option::is_none")]
        schema: Option<Schema>,
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(rename = "uniqueItems")]
        unique_items: Option<bool>,
        /// string, number, boolean, integer, array, file ( only for formData )
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(rename = "type")]
        param_type: Option<String>,
        #[serde(skip_serializing_if = "Option::is_none")]
        format: Option<String>,
        /// A brief description of the parameter. This could contain examples
        /// of use.  GitHub Flavored Markdown is allowed.
        #[serde(skip_serializing_if = "Option::is_none")]
        description: Option<String>,
        #[serde(rename = "collectionFormat", skip_serializing_if = "Option::is_none")]
        collection_format: Option<String>
        // default: ???
        // maximum ?
        // exclusiveMaximum ??
        // minimum ??
        // exclusiveMinimum ??
        // maxLength ??
        // minLength ??
        // pattern ??
        // maxItems ??
        // minItems ??
        // enum ??
        // multipleOf ??
        // allowEmptyValue ( for query / body params )
    },
    Ref {
        #[serde(rename = "$ref")]
        ref_path: String,
    },
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(tag = "type")]
pub enum Security {
    #[serde(rename = "apiKey")]
    ApiKey {
        name: String,
        #[serde(rename = "in")]
        location: String,
    },
    #[serde(rename = "oauth2")]
    Oauth2 {
        flow: String,
        #[serde(rename = "authorizationUrl")]
        authorization_url: String,
        #[serde(rename = "tokenUrl")]
        #[serde(skip_serializing_if = "Option::is_none")]
        token_url: Option<String>,
        scopes: BTreeMap<String, String>,
    },
    #[serde(rename = "basic")]
    Basic,
}

/// A [JSON schema](http://json-schema.org/) definition describing
/// the shape and properties of an object.
///
/// This may also contain a `$ref` to another definition
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct Schema {
    #[serde(skip_serializing_if = "Option::is_none")]
    /// [JSON reference](https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03)
    /// path to another defintion
    #[serde(rename = "$ref")]
    pub ref_path: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "type")]
    pub schema_type: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub format: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "enum")]
    pub enum_values: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub required: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub items: Option<Box<Schema>>,
    // implies object
    #[serde(skip_serializing_if = "Option::is_none")]
    pub properties: Option<BTreeMap<String, Schema>>,
}

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

    #[test]
    fn security_api_deserializes() {
        let json = r#"{"type":"apiKey", "name":"foo", "in": "query"}"#;
        assert_eq!(
            serde_yaml::from_str::<Security>(&json).unwrap(),
            Security::ApiKey {
                name: "foo".into(),
                location: "query".into(),
            }
        );
    }

    #[test]
    fn security_api_serializes() {
        let json = r#"{"type":"apiKey","name":"foo","in":"query"}"#;
        assert_eq!(
            serde_json::to_string(&Security::ApiKey {
                name: "foo".into(),
                location: "query".into(),
            })
            .unwrap(),
            json
        );
    }

    #[test]
    fn security_basic_deserializes() {
        let json = r#"{"type":"basic"}"#;
        assert_eq!(
            serde_yaml::from_str::<Security>(&json).unwrap(),
            Security::Basic
        );
    }

    #[test]
    fn security_basic_serializes() {
        let json = r#"{"type":"basic"}"#;
        assert_eq!(json, serde_json::to_string(&Security::Basic).unwrap());
    }

    #[test]
    fn security_oauth_deserializes() {
        let json = r#"{"type":"oauth2","flow":"implicit","authorizationUrl":"foo/bar","scopes":{"foo":"bar"}}"#;
        let mut scopes = BTreeMap::new();
        scopes.insert("foo".into(), "bar".into());
        assert_eq!(
            serde_yaml::from_str::<Security>(&json).unwrap(),
            Security::Oauth2 {
                flow: "implicit".into(),
                authorization_url: "foo/bar".into(),
                token_url: None,
                scopes: scopes,
            }
        );
    }

    #[test]
    fn security_oauth_serializes() {
        let json = r#"{"type":"oauth2","flow":"implicit","authorizationUrl":"foo/bar","scopes":{"foo":"bar"}}"#;
        let mut scopes = BTreeMap::new();
        scopes.insert("foo".into(), "bar".into());
        assert_eq!(
            json,
            serde_json::to_string(&Security::Oauth2 {
                flow: "implicit".into(),
                authorization_url: "foo/bar".into(),
                token_url: None,
                scopes: scopes,
            })
            .unwrap()
        );
    }

    #[test]
    fn parameter_or_ref_deserializes_ref() {
        let json = r#"{"$ref":"foo/bar"}"#;
        assert_eq!(
            serde_yaml::from_str::<ParameterOrRef>(&json).unwrap(),
            ParameterOrRef::Ref {
                ref_path: "foo/bar".into()
            }
        );
    }

    #[test]
    fn parameter_or_ref_serializes_pref() {
        let json = r#"{"$ref":"foo/bar"}"#;
        assert_eq!(
            json,
            serde_json::to_string(&ParameterOrRef::Ref {
                ref_path: "foo/bar".into()
            },)
            .unwrap()
        );
    }
}
<|endoftext|>


<commit_before>use std::str::Chars;
use std::str::from_char;
use std::iter::Iterator;
use std::iter::Enumerate;

static LITERAL_EXPRESSION : &'static str = "LiteralExpression";
static DOT_EXPRESSION : &'static str = "DotExpression";
static CHAR_CLASS_EXPRESSION : &'static str = "CharClassExpression";
static NOT_EXPRESSION : &'static str = "NotExpression";

#[deriving(Eq)]
enum NodeContents {
  Text( ~str ),
  Children( ~[ ~Node ] ),
  Empty
}

#[deriving(Eq)]
struct Node {
  name: &'static str,
  start: uint,
  end: uint,
  contents: NodeContents
}

impl Node {
  fn matchedText( &self ) -> ~str {
    match self.contents {
      Empty => ~"",
      Text( ref x ) => x.to_owned(),

      // TODO: recurse through children and collect all text
      Children( _ ) => ~"foo"
    }
  }

  fn predicate( name: &'static str ) -> Node {
    Node { name: name, start: 0, end: 0, contents: Empty }
  }
}

type ParseState<'a> = Enumerate< Chars<'a> >;

struct ParseResult<'a> {
  node: Node,
  parse_state: ParseState<'a>
}

trait Expression {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) -> Option< ParseResult<'a> >;
}

struct LiteralExpression {
  text: &'static str
}

impl LiteralExpression {
  fn new( text: &'static str ) -> LiteralExpression {
    LiteralExpression { text: text }
  }
}

impl Expression for LiteralExpression {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    let indices_and_chars =
      parse_state.take( self.text.len() ).collect::< ~[ ( uint, char ) ] >();
    let chars : ~str =
      indices_and_chars.iter().map( | &( _, ch ) | ch ).collect();

    if self.text == chars {
      Some( ParseResult { parse_state: MoveForward( parse_state.clone(),
                                                    self.text.len() ),
                          node: Node {
                            name: LITERAL_EXPRESSION,
                            start: indices_and_chars.head().unwrap().val0(),
                            end: indices_and_chars.last().unwrap().val0() + 1,
                            contents: Text( self.text.to_owned() ) } } )
    } else {
      None
    }
  }
}

struct DotExpression;
impl Expression for DotExpression {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) -> Option< ParseResult<'a> > {
    let mut new_parse_state = parse_state.clone();
    match new_parse_state.next() {
      Some( ( index, character ) ) => Some(
        ParseResult { parse_state: new_parse_state,
                      node: Node {
                        name: DOT_EXPRESSION,
                        start: index,
                        end: index + 1,
                        contents: Text( from_char( character ) ) } } ),
      _ => None
    }
  }
}

struct CharClassExpression {
  // All the single chars in the char class
  single_chars: ~[ char ],

  // Sequence of [from, to] (inclusive bounds) char ranges
  ranges: ~[ ( char, char ) ]
}

impl CharClassExpression {
  // Takes the inner content of square brackets, so for [a-z], send "a-z".
  fn new( contents: &str ) -> CharClassExpression {
    fn rangeAtIndex( index: uint, chars: &[char] ) -> Option<( char, char )> {
      match ( chars.get( index ),
              chars.get( index + 1 ),
              chars.get( index + 2 ) ) {
        ( Some( char1 ), Some( char2 ), Some( char3 ) ) if *char2 == '-' =>
            Some( ( *char1, *char3 ) ),
        _ => None
      }
    }

    let chars: ~[ char ] = contents.chars().collect();
    let mut char_class = CharClassExpression { single_chars: ~[],
                                               ranges: ~[] };
    let mut index = 0;
    loop {
      match rangeAtIndex( index, chars ) {
        Some( range ) => {
          char_class.ranges.push( range );
          index += 3;
        },
        _ => {
          match chars.get( index ) {
            Some( character ) => char_class.single_chars.push( *character ),
            _ => break
          };
          index += 1;
        }
      };
    }

    char_class
  }

  fn matches( &self, character: char ) -> bool {
    return self.single_chars.contains( &character ) ||
      self.ranges.iter().any(
        | &( from, to ) | character >= from && character <= to );
  }
}

impl Expression for CharClassExpression {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    let mut new_parse_state = parse_state.clone();
    match new_parse_state.next() {
      Some( ( index, ch ) ) if self.matches( ch ) => {
        Some( ParseResult { parse_state: new_parse_state,
                            node: Node {
                               name: CHAR_CLASS_EXPRESSION,
                               start: index,
                               end: index + 1,
                               contents: Text( from_char( ch ) ) } } )
      }
      _ => None
    }
  }
}

struct NotExpression {
  expr: ~Expression
}

impl NotExpression {
  fn new( expr: ~Expression ) -> NotExpression {
    NotExpression { expr: expr }
  }
}

impl Expression for NotExpression {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    match self.expr.apply( parse_state ) {
      Some( _ ) => None,
      _ => Some(
        ParseResult { parse_state: *parse_state,
                      node: Node::predicate( NOT_EXPRESSION ) } )
    }
  }
}

fn MoveForward<B, T: Iterator<B> >( mut iter: T, steps: uint ) -> T {
  for i in range( 0, steps ) {
    iter.next();
  }
  iter
}

pub fn parseString( input: &str ) -> Node {
  Node { name: LITERAL_EXPRESSION,
         start: 0,
         end: 3,
         contents: Text( input.to_owned() ) }
}

#[cfg(test)]
mod parser_tests;
<commit_msg>whitespace<commit_after>use std::str::Chars;
use std::str::from_char;
use std::iter::Iterator;
use std::iter::Enumerate;

static LITERAL_EXPRESSION : &'static str = "LiteralExpression";
static DOT_EXPRESSION : &'static str = "DotExpression";
static CHAR_CLASS_EXPRESSION : &'static str = "CharClassExpression";
static NOT_EXPRESSION : &'static str = "NotExpression";

#[deriving(Eq)]
enum NodeContents {
  Text( ~str ),
  Children( ~[ ~Node ] ),
  Empty
}

#[deriving(Eq)]
struct Node {
  name: &'static str,
  start: uint,
  end: uint,
  contents: NodeContents
}

impl Node {
  fn matchedText( &self ) -> ~str {
    match self.contents {
      Empty => ~"",
      Text( ref x ) => x.to_owned(),

      // TODO: recurse through children and collect all text
      Children( _ ) => ~"foo"
    }
  }

  fn predicate( name: &'static str ) -> Node {
    Node { name: name, start: 0, end: 0, contents: Empty }
  }
}

type ParseState<'a> = Enumerate< Chars<'a> >;

struct ParseResult<'a> {
  node: Node,
  parse_state: ParseState<'a>
}

trait Expression {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) -> Option< ParseResult<'a> >;
}

struct LiteralExpression {
  text: &'static str
}

impl LiteralExpression {
  fn new( text: &'static str ) -> LiteralExpression {
    LiteralExpression { text: text }
  }
}

impl Expression for LiteralExpression {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    let indices_and_chars =
      parse_state.take( self.text.len() ).collect::< ~[ ( uint, char ) ] >();
    let chars : ~str =
      indices_and_chars.iter().map( | &( _, ch ) | ch ).collect();

    if self.text == chars {
      Some( ParseResult { parse_state: MoveForward( parse_state.clone(),
                                                    self.text.len() ),
                          node: Node {
                            name: LITERAL_EXPRESSION,
                            start: indices_and_chars.head().unwrap().val0(),
                            end: indices_and_chars.last().unwrap().val0() + 1,
                            contents: Text( self.text.to_owned() ) } } )
    } else {
      None
    }
  }
}

struct DotExpression;
impl Expression for DotExpression {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) -> Option< ParseResult<'a> > {
    let mut new_parse_state = parse_state.clone();
    match new_parse_state.next() {
      Some( ( index, character ) ) => Some(
        ParseResult { parse_state: new_parse_state,
                      node: Node {
                        name: DOT_EXPRESSION,
                        start: index,
                        end: index + 1,
                        contents: Text( from_char( character ) ) } } ),
      _ => None
    }
  }
}

struct CharClassExpression {
  // All the single chars in the char class
  single_chars: ~[ char ],

  // Sequence of [from, to] (inclusive bounds) char ranges
  ranges: ~[ ( char, char ) ]
}

impl CharClassExpression {
  // Takes the inner content of square brackets, so for [a-z], send "a-z".
  fn new( contents: &str ) -> CharClassExpression {
    fn rangeAtIndex( index: uint, chars: &[char] ) -> Option<( char, char )> {
      match ( chars.get( index ),
              chars.get( index + 1 ),
              chars.get( index + 2 ) ) {
        ( Some( char1 ), Some( char2 ), Some( char3 ) ) if *char2 == '-' =>
            Some( ( *char1, *char3 ) ),
        _ => None
      }
    }

    let chars: ~[ char ] = contents.chars().collect();
    let mut char_class = CharClassExpression { single_chars: ~[],
                                               ranges: ~[] };
    let mut index = 0;
    loop {
      match rangeAtIndex( index, chars ) {
        Some( range ) => {
          char_class.ranges.push( range );
          index += 3;
        },
        _ => {
          match chars.get( index ) {
            Some( character ) => char_class.single_chars.push( *character ),
            _ => break
          };
          index += 1;
        }
      };
    }

    char_class
  }

  fn matches( &self, character: char ) -> bool {
    return self.single_chars.contains( &character ) ||
      self.ranges.iter().any(
        | &( from, to ) | character >= from && character <= to );
  }
}

impl Expression for CharClassExpression {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    let mut new_parse_state = parse_state.clone();
    match new_parse_state.next() {
      Some( ( index, ch ) ) if self.matches( ch ) => {
        Some( ParseResult { parse_state: new_parse_state,
                            node: Node {
                               name: CHAR_CLASS_EXPRESSION,
                               start: index,
                               end: index + 1,
                               contents: Text( from_char( ch ) ) } } )
      }
      _ => None
    }
  }
}

struct NotExpression {
  expr: ~Expression
}

impl NotExpression {
  fn new( expr: ~Expression ) -> NotExpression {
    NotExpression { expr: expr }
  }
}

impl Expression for NotExpression {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    match self.expr.apply( parse_state ) {
      Some( _ ) => None,
      _ => Some( ParseResult { parse_state: *parse_state,
                               node: Node::predicate( NOT_EXPRESSION ) } )
    }
  }
}

fn MoveForward<B, T: Iterator<B> >( mut iter: T, steps: uint ) -> T {
  for i in range( 0, steps ) {
    iter.next();
  }
  iter
}

pub fn parseString( input: &str ) -> Node {
  Node { name: LITERAL_EXPRESSION,
         start: 0,
         end: 3,
         contents: Text( input.to_owned() ) }
}

#[cfg(test)]
mod parser_tests;
<|endoftext|>