<commit_before>//! Repository interface

use self::super::Github;
use deployments::Deployments;
use keys::Keys;
use issues::{IssueRef, Issues};
use labels::Labels;
use pullrequests::PullRequests;
use releases::Releases;
use statuses::Statuses;

pub struct Repositories<'a> {
    github: &'a Github<'a>
}

impl <'a> Repositories<'a> {
    pub fn new(
        github: &'a Github<'a>) -> Repositories<'a> {
        Repositories {
            github: github
        }
    }
}

pub struct UserRepositories<'a> {
    github: &'a Github<'a>,
    owner: &'static str
}

impl <'a> UserRepositories<'a> {
    pub fn new(
        github: &'a Github<'a>,
        owner: &'static str) -> UserRepositories<'a> {
        UserRepositories {
            github: github,
            owner: owner
        }
    }
}

pub struct Repository<'a> {
  github: &'a Github<'a>,
  owner: &'static str,
  repo: &'static str
}

impl<'a> Repository<'a> {
  pub fn new(
    github: &'a Github<'a>, owner: &'static str, repo: &'static str) -> Repository<'a> {
    Repository {
      github: github,
      owner: owner,
      repo: repo
    }
  }

  /// get a reference to deployments associated with this repository ref
  pub fn deployments(&self) -> Deployments {
    Deployments::new(self.github, self.owner, self.repo)
  }

  /// get a reference to a specific github issue associated with this repoistory ref
  pub fn issue(&self, number: i64) -> IssueRef {
    IssueRef::new(self.github, self.owner, self.repo, number)
  }

  /// get a reference to github issues associated with this repoistory ref
  pub fn issues(&self) -> Issues {
    Issues::new(self.github, self.owner, self.repo)
  }

  /// get a reference deploy keys associated with this repository ref
  pub fn keys(&self) -> Keys {
    Keys::new(self.github, self.owner, self.repo)
  }

  /// get a list of labels associated with this repository ref
  pub fn labels(&self) -> Labels {
    Labels::new(self.github, self.owner, self.repo)
  }

  /// get a list of pulls associated with this repository ref
  pub fn pulls(&self) -> PullRequests {
    PullRequests::new(self.github, self.owner, self.repo)
  }

  /// get a reference to releases associated with this repository ref
  pub fn releases(&self) -> Releases {
    Releases::new(self.github, self.owner, self.repo)
  }

  /// get a references to statuses associated with this reposoitory ref
  pub fn statuses(&self) -> Statuses {
    Statuses::new(self.github, self.owner, self.repo)
  }
}
<commit_msg>impl repo lists<commit_after>//! Repository interface

use self::super::{Github, Result};
use deployments::Deployments;
use keys::Keys;
use issues::{IssueRef, Issues};
use labels::Labels;
use pullrequests::PullRequests;
use releases::Releases;
use rep::Repo;
use rustc_serialize::json;
use statuses::Statuses;

pub struct Repositories<'a> {
    github: &'a Github<'a>
}

impl <'a> Repositories<'a> {
    pub fn new(
        github: &'a Github<'a>) -> Repositories<'a> {
        Repositories {
            github: github
        }
    }

    fn path(&self, more: &str) -> String {
        format!("/user/repos{}", more)
    }

    /// list the authenticated users repositories
    /// https://developer.github.com/v3/repos/#list-your-repositories
    // todo: params
    pub fn list(&self) -> Result<Vec<Repo>> {
        let body = try!(
            self.github.get(
                &self.path("")
            )
         );
         Ok(json::decode::<Vec<Repo>>(&body).unwrap())
    }
}

pub struct UserRepositories<'a> {
    github: &'a Github<'a>,
    owner: &'static str
}

impl <'a> UserRepositories<'a> {
    pub fn new(
        github: &'a Github<'a>,
        owner: &'static str) -> UserRepositories<'a> {
        UserRepositories {
            github: github,
            owner: owner
        }
    }

    fn path(&self, more: &str) -> String {
        format!("/users/{}/repos{}", self.owner, more)
    }

    pub fn list(&self) -> Result<Vec<Repo>> {
        let body = try!(
            self.github.get(
                &self.path("")
            )
         );
        Ok(json::decode::<Vec<Repo>>(&body).unwrap())
    }
}

pub struct Repository<'a> {
  github: &'a Github<'a>,
  owner: &'static str,
  repo: &'static str
}

impl<'a> Repository<'a> {
  pub fn new(
    github: &'a Github<'a>, owner: &'static str, repo: &'static str) -> Repository<'a> {
    Repository {
      github: github,
      owner: owner,
      repo: repo
    }
  }

  /// get a reference to deployments associated with this repository ref
  pub fn deployments(&self) -> Deployments {
    Deployments::new(self.github, self.owner, self.repo)
  }

  /// get a reference to a specific github issue associated with this repoistory ref
  pub fn issue(&self, number: i64) -> IssueRef {
    IssueRef::new(self.github, self.owner, self.repo, number)
  }

  /// get a reference to github issues associated with this repoistory ref
  pub fn issues(&self) -> Issues {
    Issues::new(self.github, self.owner, self.repo)
  }

  /// get a reference deploy keys associated with this repository ref
  pub fn keys(&self) -> Keys {
    Keys::new(self.github, self.owner, self.repo)
  }

  /// get a list of labels associated with this repository ref
  pub fn labels(&self) -> Labels {
    Labels::new(self.github, self.owner, self.repo)
  }

  /// get a list of pulls associated with this repository ref
  pub fn pulls(&self) -> PullRequests {
    PullRequests::new(self.github, self.owner, self.repo)
  }

  /// get a reference to releases associated with this repository ref
  pub fn releases(&self) -> Releases {
    Releases::new(self.github, self.owner, self.repo)
  }

  /// get a references to statuses associated with this reposoitory ref
  pub fn statuses(&self) -> Statuses {
    Statuses::new(self.github, self.owner, self.repo)
  }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.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.
//! A type-checked scaling factor between units.

use std::num::{cast, One};

/// A scaling factor between two different units of measurement.
///
/// This is effectively a type-safe float, intended to be used in combination with other types like
/// `length::Length` to enforce conversion between systems of measurement at compile time.
///
/// `Src` and `Dst` represent the units before and after multiplying a value by a ScaleFactor.  They
/// may be types without values, such as empty enums.  For example:
///
/// ```rust
/// enum Mm {};
/// enum Inch {};
///
/// let mm_per_inch: ScaleFactor<Inch, Mm> = ScaleFactor(25.4);
///
/// let one_foot: Length<Inch, f32> = Length(12.0);
/// let one_foot_in_mm: Length<Mm, f32> = one_foot * mm_per_inch;
/// ```
#[deriving(Decodable, Encodable, Show)]
pub struct ScaleFactor<Src, Dst, T>(pub T);

impl<Src, Dst, T: Clone> ScaleFactor<Src, Dst, T> {
    pub fn get(&self) -> T {
        match *self {
            ScaleFactor(ref x) => x.clone()
        }
    }
}

impl<Src, Dst, T: Clone + One + Div<T, T>> ScaleFactor<Src, Dst, T> {
    /// The inverse ScaleFactor (1.0 / self).
    pub fn inv(&self) -> ScaleFactor<Dst, Src, T> {
        let one: T = One::one();
        ScaleFactor(one / self.get())
    }
}

// scale0 * scale1
impl<A, B, C, T: Clone + Mul<T,T>>
Mul<ScaleFactor<B, C, T>, ScaleFactor<A, C, T>> for ScaleFactor<A, B, T> {
    #[inline]
    fn mul(&self, other: &ScaleFactor<B, C, T>) -> ScaleFactor<A, C, T> {
        ScaleFactor(self.get() * other.get())
    }
}

// scale0 + scale1
impl<Src, Dst, T: Clone + Add<T,T>>
Add<ScaleFactor<Src, Dst, T>, ScaleFactor<Src, Dst, T>> for ScaleFactor<Src, Dst, T> {
    #[inline]
    fn add(&self, other: &ScaleFactor<Src, Dst, T>) -> ScaleFactor<Src, Dst, T> {
        ScaleFactor(self.get() + other.get())
    }
}

// scale0 - scale1
impl<Src, Dst, T: Clone + Sub<T,T>>
Sub<ScaleFactor<Src, Dst, T>, ScaleFactor<Src, Dst, T>> for ScaleFactor<Src, Dst, T> {
    #[inline]
    fn sub(&self, other: &ScaleFactor<Src, Dst, T>) -> ScaleFactor<Src, Dst, T> {
        ScaleFactor(self.get() - other.get())
    }
}

impl<Src, Dst, T0: NumCast + Clone, T1: NumCast + Clone> ScaleFactor<Src, Dst, T0> {
    /// Cast from one numeric representation to another, preserving the units.
    pub fn cast(&self) -> Option<ScaleFactor<Src, Dst, T1>> {
        cast(self.get()).map(|x| ScaleFactor(x))
    }
}

// FIXME: Switch to `deriving(PartialEq, Clone)` after this Rust issue is fixed:
// https://github.com/mozilla/rust/issues/7671

impl<Src, Dst, T: Clone + PartialEq> PartialEq for ScaleFactor<Src, Dst, T> {
    fn eq(&self, other: &ScaleFactor<Src, Dst, T>) -> bool {
        self.get().eq(&other.get())
    }
}

impl<Src, Dst, T: Clone> Clone for ScaleFactor<Src, Dst, T> {
    fn clone(&self) -> ScaleFactor<Src, Dst, T> {
        ScaleFactor(self.get())
    }
}

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

    #[deriving(Show)]
    enum Inch {}
    #[deriving(Show)]
    enum Cm {}
    #[deriving(Show)]
    enum Mm {}

    #[test]
    fn test_scale_factor() {
        let mm_per_inch: ScaleFactor<Inch, Mm, f32> = ScaleFactor(25.4);
        let cm_per_mm: ScaleFactor<Mm, Cm, f32> = ScaleFactor(0.1);

        let mm_per_cm: ScaleFactor<Cm, Mm, f32> = cm_per_mm.inv();
        assert_eq!(mm_per_cm.get(), 10.0);

        let cm_per_inch: ScaleFactor<Inch, Cm, f32> = mm_per_inch * cm_per_mm;
        assert_eq!(cm_per_inch, ScaleFactor(2.54));

        let a: ScaleFactor<Inch, Inch, int> = ScaleFactor(2);
        let b: ScaleFactor<Inch, Inch, int> = ScaleFactor(3);
        assert!(a != b);
        assert_eq!(a, a.clone());
        assert_eq!(a + b, ScaleFactor(5));
        assert_eq!(a - b, ScaleFactor(-1));
    }
}
<commit_msg>Fix doc test failures after ScaleFactor changes<commit_after>// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.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.
//! A type-checked scaling factor between units.

use std::num::{cast, One};

/// A scaling factor between two different units of measurement.
///
/// This is effectively a type-safe float, intended to be used in combination with other types like
/// `length::Length` to enforce conversion between systems of measurement at compile time.
///
/// `Src` and `Dst` represent the units before and after multiplying a value by a ScaleFactor.  They
/// may be types without values, such as empty enums.  For example:
///
/// ```rust
/// use geom::scale_factor::ScaleFactor;
/// use geom::length::Length;
/// enum Mm {};
/// enum Inch {};
///
/// let mm_per_inch: ScaleFactor<Inch, Mm, f32> = ScaleFactor(25.4);
///
/// let one_foot: Length<Inch, f32> = Length(12.0);
/// let one_foot_in_mm: Length<Mm, f32> = one_foot * mm_per_inch;
/// ```
#[deriving(Decodable, Encodable, Show)]
pub struct ScaleFactor<Src, Dst, T>(pub T);

impl<Src, Dst, T: Clone> ScaleFactor<Src, Dst, T> {
    pub fn get(&self) -> T {
        match *self {
            ScaleFactor(ref x) => x.clone()
        }
    }
}

impl<Src, Dst, T: Clone + One + Div<T, T>> ScaleFactor<Src, Dst, T> {
    /// The inverse ScaleFactor (1.0 / self).
    pub fn inv(&self) -> ScaleFactor<Dst, Src, T> {
        let one: T = One::one();
        ScaleFactor(one / self.get())
    }
}

// scale0 * scale1
impl<A, B, C, T: Clone + Mul<T,T>>
Mul<ScaleFactor<B, C, T>, ScaleFactor<A, C, T>> for ScaleFactor<A, B, T> {
    #[inline]
    fn mul(&self, other: &ScaleFactor<B, C, T>) -> ScaleFactor<A, C, T> {
        ScaleFactor(self.get() * other.get())
    }
}

// scale0 + scale1
impl<Src, Dst, T: Clone + Add<T,T>>
Add<ScaleFactor<Src, Dst, T>, ScaleFactor<Src, Dst, T>> for ScaleFactor<Src, Dst, T> {
    #[inline]
    fn add(&self, other: &ScaleFactor<Src, Dst, T>) -> ScaleFactor<Src, Dst, T> {
        ScaleFactor(self.get() + other.get())
    }
}

// scale0 - scale1
impl<Src, Dst, T: Clone + Sub<T,T>>
Sub<ScaleFactor<Src, Dst, T>, ScaleFactor<Src, Dst, T>> for ScaleFactor<Src, Dst, T> {
    #[inline]
    fn sub(&self, other: &ScaleFactor<Src, Dst, T>) -> ScaleFactor<Src, Dst, T> {
        ScaleFactor(self.get() - other.get())
    }
}

impl<Src, Dst, T0: NumCast + Clone, T1: NumCast + Clone> ScaleFactor<Src, Dst, T0> {
    /// Cast from one numeric representation to another, preserving the units.
    pub fn cast(&self) -> Option<ScaleFactor<Src, Dst, T1>> {
        cast(self.get()).map(|x| ScaleFactor(x))
    }
}

// FIXME: Switch to `deriving(PartialEq, Clone)` after this Rust issue is fixed:
// https://github.com/mozilla/rust/issues/7671

impl<Src, Dst, T: Clone + PartialEq> PartialEq for ScaleFactor<Src, Dst, T> {
    fn eq(&self, other: &ScaleFactor<Src, Dst, T>) -> bool {
        self.get().eq(&other.get())
    }
}

impl<Src, Dst, T: Clone> Clone for ScaleFactor<Src, Dst, T> {
    fn clone(&self) -> ScaleFactor<Src, Dst, T> {
        ScaleFactor(self.get())
    }
}

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

    #[deriving(Show)]
    enum Inch {}
    #[deriving(Show)]
    enum Cm {}
    #[deriving(Show)]
    enum Mm {}

    #[test]
    fn test_scale_factor() {
        let mm_per_inch: ScaleFactor<Inch, Mm, f32> = ScaleFactor(25.4);
        let cm_per_mm: ScaleFactor<Mm, Cm, f32> = ScaleFactor(0.1);

        let mm_per_cm: ScaleFactor<Cm, Mm, f32> = cm_per_mm.inv();
        assert_eq!(mm_per_cm.get(), 10.0);

        let cm_per_inch: ScaleFactor<Inch, Cm, f32> = mm_per_inch * cm_per_mm;
        assert_eq!(cm_per_inch, ScaleFactor(2.54));

        let a: ScaleFactor<Inch, Inch, int> = ScaleFactor(2);
        let b: ScaleFactor<Inch, Inch, int> = ScaleFactor(3);
        assert!(a != b);
        assert_eq!(a, a.clone());
        assert_eq!(a + b, ScaleFactor(5));
        assert_eq!(a - b, ScaleFactor(-1));
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use super::generate_artifacts::Artifact;
use crate::config::{Config, ProjectConfig};
use crate::errors::BuildProjectError;
use relay_codegen::Printer;
use schema::Schema;
use std::fs::{create_dir_all, File};
use std::io;
use std::io::prelude::*;
use std::path::PathBuf;

pub fn write_artifacts(
    config: &Config,
    project_config: &ProjectConfig,
    artifacts: &[Artifact<'_>],
    schema: &Schema,
) -> Result<(), BuildProjectError> {
    let mut printer = Printer::default();
    for artifact in artifacts {
        let generated_path = &config.root_dir.join(&artifact.path);
        let content = artifact
            .content
            .as_bytes(config, project_config, &mut printer, schema);
        write_file(generated_path, &content).map_err(|error| {
            BuildProjectError::WriteFileError {
                file: generated_path.clone(),
                source: error,
            }
        })?;
    }
    Ok(())
}

fn ensure_file_directory_exists(file_path: &PathBuf) -> io::Result<()> {
    if file_path.exists() {
        return Ok(());
    }
    let file_directory = file_path.parent();
    if let Some(file_directory) = file_directory {
        if !file_directory.exists() {
            create_dir_all(file_directory)?;
        }
    }

    Ok(())
}

fn write_file(path: &PathBuf, content: &[u8]) -> io::Result<()> {
    ensure_file_directory_exists(path)?;
    let mut file = File::create(path)?;
    file.write_all(&content)?;
    Ok(())
}
<commit_msg>only write if file content has changed<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use super::generate_artifacts::Artifact;
use crate::config::{Config, ProjectConfig};
use crate::errors::BuildProjectError;
use relay_codegen::Printer;
use schema::Schema;
use std::fs::{create_dir_all, File};
use std::io;
use std::io::prelude::*;
use std::path::PathBuf;

pub fn write_artifacts(
    config: &Config,
    project_config: &ProjectConfig,
    artifacts: &[Artifact<'_>],
    schema: &Schema,
) -> Result<(), BuildProjectError> {
    let mut printer = Printer::default();
    for artifact in artifacts {
        let generated_path = &config.root_dir.join(&artifact.path);
        let content = artifact
            .content
            .as_bytes(config, project_config, &mut printer, schema);
        write_file(generated_path, &content).map_err(|error| {
            BuildProjectError::WriteFileError {
                file: generated_path.clone(),
                source: error,
            }
        })?;
    }
    Ok(())
}

fn ensure_file_directory_exists(file_path: &PathBuf) -> io::Result<()> {
    if let Some(file_directory) = file_path.parent() {
        if !file_directory.exists() {
            create_dir_all(file_directory)?;
        }
    }

    Ok(())
}

fn write_file(path: &PathBuf, content: &[u8]) -> io::Result<()> {
    if path.exists() {
        let existing_content = std::fs::read(path)?;
        if existing_content == content {
            return Ok(());
        }
    } else {
        ensure_file_directory_exists(path)?;
    }

    let mut file = File::create(path)?;
    file.write_all(&content)?;
    Ok(())
}
<|endoftext|>


<commit_before>use serde_derive::{Deserialize, Serialize};
use std::{char, convert::From, marker::PhantomData, ops::Deref, u32};

use crate::{
    ast::{InputValue, Selection, ToInputValue},
    executor::{Executor, Registry},
    parser::{LexerError, ParseError, ScalarToken, Token},
    schema::meta::MetaType,
    types::base::GraphQLType,
    value::{ParseScalarResult, ScalarRefValue, ScalarValue, Value},
};

/// An ID as defined by the GraphQL specification
///
/// Represented as a string, but can be converted _to_ from an integer as well.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct ID(String);

impl From<String> for ID {
    fn from(s: String) -> ID {
        ID(s)
    }
}

impl ID {
    /// Construct a new ID from anything implementing `Into<String>`
    pub fn new<S: Into<String>>(value: S) -> Self {
        ID(value.into())
    }
}

impl Deref for ID {
    type Target = str;

    fn deref(&self) -> &str {
        &self.0
    }
}

graphql_scalar!(ID as "ID" where Scalar = <S>{
    resolve(&self) -> Value {
        Value::scalar(self.0.clone())
    }

    from_input_value(v: &InputValue) -> Option<ID> {
        match *v {
            InputValue::Scalar(ref s) => {
                s.as_string().or_else(|| s.as_int().map(|i| i.to_string()))
                    .map(ID)
            }
            _ => None
        }
    }

    from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
        match value {
            ScalarToken::String(value) | ScalarToken::Int(value) => {
                Ok(S::from(value.to_owned()))
            }
            _ => Err(ParseError::UnexpectedToken(Token::Scalar(value))),
        }
    }
});

graphql_scalar!(String as "String" where Scalar = <S>{
    resolve(&self) -> Value {
        Value::scalar(self.clone())
    }

    from_input_value(v: &InputValue) -> Option<String> {
        match *v {
            InputValue::Scalar(ref s) => s.as_string(),
            _ => None,
        }
    }

    from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
        if let ScalarToken::String(value) = value {
            let mut ret = String::with_capacity(value.len());
            let mut char_iter = value.chars();
            while let Some(ch) = char_iter.next() {
                match ch {
                    '\\' => {
                        match char_iter.next() {
                            Some('"') => {ret.push('"');}
                            Some('/') => {ret.push('/');}
                            Some('n') => {ret.push('\n');}
                            Some('r') => {ret.push('\r');}
                            Some('t') => {ret.push('\t');}
                            Some('\\') => {ret.push('\\');}
                            Some('f') => {ret.push('\u{000c}');}
                            Some('b') => {ret.push('\u{0008}');}
                            Some('u') => {
                                ret.push(parse_unicode_codepoint(&mut char_iter)?);
                            }
                            Some(s) => return Err(ParseError::LexerError(LexerError::UnknownEscapeSequence(format!("\\{}", s)))),
                            None => return Err(ParseError::LexerError(LexerError::UnterminatedString)),
                        }
                    },
                    ch => {ret.push(ch);}
                }
            }
            Ok(ret.into())
        } else {
            Err(ParseError::UnexpectedToken(Token::Scalar(value)))
        }
    }
});

fn parse_unicode_codepoint<'a, I>(char_iter: &mut I) -> Result<char, ParseError<'a>>
where
    I: Iterator<Item = char>,
{
    let escaped_code_point = char_iter
        .next()
        .ok_or_else(|| {
            ParseError::LexerError(LexerError::UnknownEscapeSequence(String::from("\\u")))
        })
        .and_then(|c1| {
            char_iter
                .next()
                .map(|c2| format!("{}{}", c1, c2))
                .ok_or_else(|| {
                    ParseError::LexerError(LexerError::UnknownEscapeSequence(format!("\\u{}", c1)))
                })
        })
        .and_then(|mut s| {
            char_iter
                .next()
                .ok_or_else(|| {
                    ParseError::LexerError(LexerError::UnknownEscapeSequence(format!(
                        "\\u{}",
                        s.clone()
                    )))
                })
                .map(|c2| {
                    s.push(c2);
                    s
                })
        })
        .and_then(|mut s| {
            char_iter
                .next()
                .ok_or_else(|| {
                    ParseError::LexerError(LexerError::UnknownEscapeSequence(format!(
                        "\\u{}",
                        s.clone()
                    )))
                })
                .map(|c2| {
                    s.push(c2);
                    s
                })
        })?;
    let code_point = u32::from_str_radix(&escaped_code_point, 16).map_err(|_| {
        ParseError::LexerError(LexerError::UnknownEscapeSequence(format!(
            "\\u{}",
            escaped_code_point
        )))
    })?;
    char::from_u32(code_point).ok_or_else(|| {
        ParseError::LexerError(LexerError::UnknownEscapeSequence(format!(
            "\\u{}",
            escaped_code_point
        )))
    })
}

impl<'a, S> GraphQLType<S> for &'a str
where
    S: ScalarValue,
    for<'b> &'b S: ScalarRefValue<'b>,
{
    type Context = ();
    type TypeInfo = ();

    fn name(_: &()) -> Option<&str> {
        Some("String")
    }

    fn meta<'r>(_: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
    where
        S: 'r,
        for<'b> &'b S: ScalarRefValue<'b>,
    {
        registry.build_scalar_type::<String>(&()).into_meta()
    }

    fn resolve(
        &self,
        _: &(),
        _: Option<&[Selection<S>]>,
        _: &Executor<Self::Context, S>,
    ) -> Value<S> {
        Value::scalar(String::from(*self))
    }
}

impl<'a, S> ToInputValue<S> for &'a str
where
    S: ScalarValue,
{
    fn to_input_value(&self) -> InputValue<S> {
        InputValue::scalar(String::from(*self))
    }
}

graphql_scalar!(bool as "Boolean" where Scalar = <S>{
    resolve(&self) -> Value {
        Value::scalar(*self)
    }

    from_input_value(v: &InputValue) -> Option<bool> {
        match *v {
            InputValue::Scalar(ref b) => b.as_boolean(),
            _ => None,
        }
    }

    from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S > {
        // Bools are parsed on it's own. This should not hit this code path
        Err(ParseError::UnexpectedToken(Token::Scalar(value)))
    }
});

graphql_scalar!(i32 as "Int" where Scalar = <S>{
    resolve(&self) -> Value {
        Value::scalar(*self)
    }

    from_input_value(v: &InputValue) -> Option<i32> {
        match *v {
            InputValue::Scalar(ref i) => i.as_int(),
            _ => None,
        }
    }

    from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
        if let ScalarToken::Int(v) = value {
            v.parse()
             .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value)))
             .map(|s: i32| s.into())
        } else {
            Err(ParseError::UnexpectedToken(Token::Scalar(value)))
        }
    }
});

graphql_scalar!(f64 as "Float" where Scalar = <S>{
    resolve(&self) -> Value {
        Value::scalar(*self)
    }

    from_input_value(v: &InputValue) -> Option<f64> {
        match *v {
            InputValue::Scalar(ref s) => s.as_float(),
            _ => None,
        }
    }

    from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
        match value {
            ScalarToken::Int(v) | ScalarToken::Float(v) => {
                v.parse()
                 .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value)))
                 .map(|s: f64| s.into())
            }
            ScalarToken::String(_) => {
                Err(ParseError::UnexpectedToken(Token::Scalar(value)))
            }
        }
    }
});

/// Utillity type to define read-only schemas
///
/// If you instantiate `RootNode` with this as the mutation, no mutation will be
/// generated for the schema.
#[derive(Debug)]
pub struct EmptyMutation<T> {
    phantom: PhantomData<T>,
}

impl<T> EmptyMutation<T> {
    /// Construct a new empty mutation
    pub fn new() -> EmptyMutation<T> {
        EmptyMutation {
            phantom: PhantomData,
        }
    }
}

impl<S, T> GraphQLType<S> for EmptyMutation<T>
where
    S: ScalarValue,
    for<'b> &'b S: ScalarRefValue<'b>,
{
    type Context = T;
    type TypeInfo = ();

    fn name(_: &()) -> Option<&str> {
        Some("_EmptyMutation")
    }

    fn meta<'r>(_: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
    where
        S: 'r,
        for<'b> &'b S: ScalarRefValue<'b>,
    {
        registry.build_object_type::<Self>(&(), &[]).into_meta()
    }
}

#[cfg(test)]
mod tests {
    use super::ID;
    use crate::{
        parser::ScalarToken,
        value::{DefaultScalarValue, ParseScalarValue},
    };

    #[test]
    fn test_id_from_string() {
        let actual = ID::from(String::from("foo"));
        let expected = ID(String::from("foo"));
        assert_eq!(actual, expected);
    }

    #[test]
    fn test_id_new() {
        let actual = ID::new("foo");
        let expected = ID(String::from("foo"));
        assert_eq!(actual, expected);
    }

    #[test]
    fn test_id_deref() {
        let id = ID(String::from("foo"));
        assert_eq!(id.len(), 3);
    }

    #[test]
    fn parse_strings() {
        fn parse_string(s: &str, expected: &str) {
            let s =
                <String as ParseScalarValue<DefaultScalarValue>>::from_str(ScalarToken::String(s));
            assert!(s.is_ok(), "A parsing error occurred: {:?}", s);
            let s: Option<String> = s.unwrap().into();
            assert!(s.is_some(), "No string returned");
            assert_eq!(s.unwrap(), expected);
        }

        parse_string("simple", "simple");
        parse_string(" white space ", " white space ");
        parse_string(r#"quote \""#, "quote \"");
        parse_string(r#"escaped \n\r\b\t\f"#, "escaped \n\r\u{0008}\t\u{000c}");
        parse_string(r#"slashes \\ \/"#, "slashes \\ /");
        parse_string(
            r#"unicode \u1234\u5678\u90AB\uCDEF"#,
            "unicode \u{1234}\u{5678}\u{90ab}\u{cdef}",
        );
    }
}
<commit_msg>Make EmptyMutation `Send`<commit_after>use serde_derive::{Deserialize, Serialize};
use std::{char, convert::From, marker::PhantomData, ops::Deref, u32};

use crate::{
    ast::{InputValue, Selection, ToInputValue},
    executor::{Executor, Registry},
    parser::{LexerError, ParseError, ScalarToken, Token},
    schema::meta::MetaType,
    types::base::GraphQLType,
    value::{ParseScalarResult, ScalarRefValue, ScalarValue, Value},
};

/// An ID as defined by the GraphQL specification
///
/// Represented as a string, but can be converted _to_ from an integer as well.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct ID(String);

impl From<String> for ID {
    fn from(s: String) -> ID {
        ID(s)
    }
}

impl ID {
    /// Construct a new ID from anything implementing `Into<String>`
    pub fn new<S: Into<String>>(value: S) -> Self {
        ID(value.into())
    }
}

impl Deref for ID {
    type Target = str;

    fn deref(&self) -> &str {
        &self.0
    }
}

graphql_scalar!(ID as "ID" where Scalar = <S>{
    resolve(&self) -> Value {
        Value::scalar(self.0.clone())
    }

    from_input_value(v: &InputValue) -> Option<ID> {
        match *v {
            InputValue::Scalar(ref s) => {
                s.as_string().or_else(|| s.as_int().map(|i| i.to_string()))
                    .map(ID)
            }
            _ => None
        }
    }

    from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
        match value {
            ScalarToken::String(value) | ScalarToken::Int(value) => {
                Ok(S::from(value.to_owned()))
            }
            _ => Err(ParseError::UnexpectedToken(Token::Scalar(value))),
        }
    }
});

graphql_scalar!(String as "String" where Scalar = <S>{
    resolve(&self) -> Value {
        Value::scalar(self.clone())
    }

    from_input_value(v: &InputValue) -> Option<String> {
        match *v {
            InputValue::Scalar(ref s) => s.as_string(),
            _ => None,
        }
    }

    from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
        if let ScalarToken::String(value) = value {
            let mut ret = String::with_capacity(value.len());
            let mut char_iter = value.chars();
            while let Some(ch) = char_iter.next() {
                match ch {
                    '\\' => {
                        match char_iter.next() {
                            Some('"') => {ret.push('"');}
                            Some('/') => {ret.push('/');}
                            Some('n') => {ret.push('\n');}
                            Some('r') => {ret.push('\r');}
                            Some('t') => {ret.push('\t');}
                            Some('\\') => {ret.push('\\');}
                            Some('f') => {ret.push('\u{000c}');}
                            Some('b') => {ret.push('\u{0008}');}
                            Some('u') => {
                                ret.push(parse_unicode_codepoint(&mut char_iter)?);
                            }
                            Some(s) => return Err(ParseError::LexerError(LexerError::UnknownEscapeSequence(format!("\\{}", s)))),
                            None => return Err(ParseError::LexerError(LexerError::UnterminatedString)),
                        }
                    },
                    ch => {ret.push(ch);}
                }
            }
            Ok(ret.into())
        } else {
            Err(ParseError::UnexpectedToken(Token::Scalar(value)))
        }
    }
});

fn parse_unicode_codepoint<'a, I>(char_iter: &mut I) -> Result<char, ParseError<'a>>
where
    I: Iterator<Item = char>,
{
    let escaped_code_point = char_iter
        .next()
        .ok_or_else(|| {
            ParseError::LexerError(LexerError::UnknownEscapeSequence(String::from("\\u")))
        })
        .and_then(|c1| {
            char_iter
                .next()
                .map(|c2| format!("{}{}", c1, c2))
                .ok_or_else(|| {
                    ParseError::LexerError(LexerError::UnknownEscapeSequence(format!("\\u{}", c1)))
                })
        })
        .and_then(|mut s| {
            char_iter
                .next()
                .ok_or_else(|| {
                    ParseError::LexerError(LexerError::UnknownEscapeSequence(format!(
                        "\\u{}",
                        s.clone()
                    )))
                })
                .map(|c2| {
                    s.push(c2);
                    s
                })
        })
        .and_then(|mut s| {
            char_iter
                .next()
                .ok_or_else(|| {
                    ParseError::LexerError(LexerError::UnknownEscapeSequence(format!(
                        "\\u{}",
                        s.clone()
                    )))
                })
                .map(|c2| {
                    s.push(c2);
                    s
                })
        })?;
    let code_point = u32::from_str_radix(&escaped_code_point, 16).map_err(|_| {
        ParseError::LexerError(LexerError::UnknownEscapeSequence(format!(
            "\\u{}",
            escaped_code_point
        )))
    })?;
    char::from_u32(code_point).ok_or_else(|| {
        ParseError::LexerError(LexerError::UnknownEscapeSequence(format!(
            "\\u{}",
            escaped_code_point
        )))
    })
}

impl<'a, S> GraphQLType<S> for &'a str
where
    S: ScalarValue,
    for<'b> &'b S: ScalarRefValue<'b>,
{
    type Context = ();
    type TypeInfo = ();

    fn name(_: &()) -> Option<&str> {
        Some("String")
    }

    fn meta<'r>(_: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
    where
        S: 'r,
        for<'b> &'b S: ScalarRefValue<'b>,
    {
        registry.build_scalar_type::<String>(&()).into_meta()
    }

    fn resolve(
        &self,
        _: &(),
        _: Option<&[Selection<S>]>,
        _: &Executor<Self::Context, S>,
    ) -> Value<S> {
        Value::scalar(String::from(*self))
    }
}

impl<'a, S> ToInputValue<S> for &'a str
where
    S: ScalarValue,
{
    fn to_input_value(&self) -> InputValue<S> {
        InputValue::scalar(String::from(*self))
    }
}

graphql_scalar!(bool as "Boolean" where Scalar = <S>{
    resolve(&self) -> Value {
        Value::scalar(*self)
    }

    from_input_value(v: &InputValue) -> Option<bool> {
        match *v {
            InputValue::Scalar(ref b) => b.as_boolean(),
            _ => None,
        }
    }

    from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S > {
        // Bools are parsed on it's own. This should not hit this code path
        Err(ParseError::UnexpectedToken(Token::Scalar(value)))
    }
});

graphql_scalar!(i32 as "Int" where Scalar = <S>{
    resolve(&self) -> Value {
        Value::scalar(*self)
    }

    from_input_value(v: &InputValue) -> Option<i32> {
        match *v {
            InputValue::Scalar(ref i) => i.as_int(),
            _ => None,
        }
    }

    from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
        if let ScalarToken::Int(v) = value {
            v.parse()
             .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value)))
             .map(|s: i32| s.into())
        } else {
            Err(ParseError::UnexpectedToken(Token::Scalar(value)))
        }
    }
});

graphql_scalar!(f64 as "Float" where Scalar = <S>{
    resolve(&self) -> Value {
        Value::scalar(*self)
    }

    from_input_value(v: &InputValue) -> Option<f64> {
        match *v {
            InputValue::Scalar(ref s) => s.as_float(),
            _ => None,
        }
    }

    from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
        match value {
            ScalarToken::Int(v) | ScalarToken::Float(v) => {
                v.parse()
                 .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value)))
                 .map(|s: f64| s.into())
            }
            ScalarToken::String(_) => {
                Err(ParseError::UnexpectedToken(Token::Scalar(value)))
            }
        }
    }
});

/// Utillity type to define read-only schemas
///
/// If you instantiate `RootNode` with this as the mutation, no mutation will be
/// generated for the schema.
#[derive(Debug)]
pub struct EmptyMutation<T> {
    phantom: PhantomData<T>,
}

impl<T> EmptyMutation<T> {
    /// Construct a new empty mutation
    pub fn new() -> EmptyMutation<T> {
        EmptyMutation {
            phantom: PhantomData,
        }
    }
}

// This is safe due to never using `T`.
unsafe impl<T> Send for EmptyMutation<T> {}

impl<S, T> GraphQLType<S> for EmptyMutation<T>
where
    S: ScalarValue,
    for<'b> &'b S: ScalarRefValue<'b>,
{
    type Context = T;
    type TypeInfo = ();

    fn name(_: &()) -> Option<&str> {
        Some("_EmptyMutation")
    }

    fn meta<'r>(_: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
    where
        S: 'r,
        for<'b> &'b S: ScalarRefValue<'b>,
    {
        registry.build_object_type::<Self>(&(), &[]).into_meta()
    }
}

#[cfg(test)]
mod tests {
    use super::{EmptyMutation, ID};
    use crate::{
        parser::ScalarToken,
        value::{DefaultScalarValue, ParseScalarValue},
    };

    #[test]
    fn test_id_from_string() {
        let actual = ID::from(String::from("foo"));
        let expected = ID(String::from("foo"));
        assert_eq!(actual, expected);
    }

    #[test]
    fn test_id_new() {
        let actual = ID::new("foo");
        let expected = ID(String::from("foo"));
        assert_eq!(actual, expected);
    }

    #[test]
    fn test_id_deref() {
        let id = ID(String::from("foo"));
        assert_eq!(id.len(), 3);
    }

    #[test]
    fn parse_strings() {
        fn parse_string(s: &str, expected: &str) {
            let s =
                <String as ParseScalarValue<DefaultScalarValue>>::from_str(ScalarToken::String(s));
            assert!(s.is_ok(), "A parsing error occurred: {:?}", s);
            let s: Option<String> = s.unwrap().into();
            assert!(s.is_some(), "No string returned");
            assert_eq!(s.unwrap(), expected);
        }

        parse_string("simple", "simple");
        parse_string(" white space ", " white space ");
        parse_string(r#"quote \""#, "quote \"");
        parse_string(r#"escaped \n\r\b\t\f"#, "escaped \n\r\u{0008}\t\u{000c}");
        parse_string(r#"slashes \\ \/"#, "slashes \\ /");
        parse_string(
            r#"unicode \u1234\u5678\u90AB\uCDEF"#,
            "unicode \u{1234}\u{5678}\u{90ab}\u{cdef}",
        );
    }

    #[test]
    fn empty_mutation_is_send() {
        fn check_if_send<T: Send>() {}
        check_if_send::<EmptyMutation<()>>();
    }
}
<|endoftext|>


<commit_before>#![warn(clippy::pedantic)]

use crate::cacache::Cache;
use anyhow::anyhow;
use rayon::prelude::*;
use serde::Deserialize;
use std::{
    collections::HashMap,
    env, fmt, fs,
    path::Path,
    process::{self, Command},
};
use tempfile::tempdir;
use url::Url;

mod cacache;

#[derive(Deserialize)]
struct PackageLock {
    #[serde(rename = "lockfileVersion")]
    version: u8,
    dependencies: Option<HashMap<String, OldPackage>>,
    packages: Option<HashMap<String, Package>>,
}

#[derive(Deserialize)]
struct OldPackage {
    version: UrlOrString,
    resolved: Option<UrlOrString>,
    integrity: Option<String>,
    dependencies: Option<HashMap<String, OldPackage>>,
}

#[derive(Deserialize)]
struct Package {
    resolved: Option<UrlOrString>,
    integrity: Option<String>,
}

#[derive(Debug, Deserialize, PartialEq, Eq)]
#[serde(untagged)]
enum UrlOrString {
    Url(Url),
    String(String),
}

impl fmt::Display for UrlOrString {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            UrlOrString::Url(url) => url.fmt(f),
            UrlOrString::String(string) => string.fmt(f),
        }
    }
}

fn to_new_packages(
    old_packages: HashMap<String, OldPackage>,
) -> anyhow::Result<HashMap<String, Package>> {
    let mut new = HashMap::new();

    for (name, package) in old_packages {
        new.insert(
            format!("{name}-{}", package.version),
            Package {
                resolved: if matches!(package.version, UrlOrString::Url(_)) {
                    Some(package.version)
                } else {
                    package.resolved
                },
                integrity: package.integrity,
            },
        );

        if let Some(dependencies) = package.dependencies {
            new.extend(to_new_packages(dependencies)?);
        }
    }

    Ok(new)
}

#[allow(clippy::case_sensitive_file_extension_comparisons)]
fn get_hosted_git_url(url: &Url) -> Option<Url> {
    if ["git", "http", "git+ssh", "git+https", "ssh", "https"].contains(&url.scheme()) {
        let mut s = url.path_segments()?;

        match url.host_str()? {
            "github.com" => {
                let user = s.next()?;
                let mut project = s.next()?;
                let typ = s.next();
                let mut commit = s.next();

                if typ.is_none() {
                    commit = url.fragment();
                } else if typ.is_some() && typ != Some("tree") {
                    return None;
                }

                if project.ends_with(".git") {
                    project = project.strip_suffix(".git")?;
                }

                let commit = commit.unwrap();

                Some(
                    Url::parse(&format!(
                        "https://codeload.github.com/{user}/{project}/tar.gz/{commit}"
                    ))
                    .ok()?,
                )
            }
            "bitbucket.org" => {
                let user = s.next()?;
                let mut project = s.next()?;
                let aux = s.next();

                if aux == Some("get") {
                    return None;
                }

                if project.ends_with(".git") {
                    project = project.strip_suffix(".git")?;
                }

                let commit = url.fragment()?;

                Some(
                    Url::parse(&format!(
                        "https://bitbucket.org/{user}/{project}/get/{commit}.tar.gz"
                    ))
                    .ok()?,
                )
            }
            "gitlab.com" => {
                let path = &url.path()[1..];

                if path.contains("/~/") || path.contains("/archive.tar.gz") {
                    return None;
                }

                let user = s.next()?;
                let mut project = s.next()?;

                if project.ends_with(".git") {
                    project = project.strip_suffix(".git")?;
                }

                let commit = url.fragment()?;

                Some(
                    Url::parse(&format!(
                    "https://gitlab.com/{user}/{project}/repository/archive.tar.gz?ref={commit}"
                ))
                    .ok()?,
                )
            }
            "git.sr.ht" => {
                let user = s.next()?;
                let mut project = s.next()?;
                let aux = s.next();

                if aux == Some("archive") {
                    return None;
                }

                if project.ends_with(".git") {
                    project = project.strip_suffix(".git")?;
                }

                let commit = url.fragment()?;

                Some(
                    Url::parse(&format!(
                        "https://git.sr.ht/{user}/{project}/archive/{commit}.tar.gz"
                    ))
                    .ok()?,
                )
            }
            _ => None,
        }
    } else {
        None
    }
}

fn get_ideal_hash(integrity: &str) -> anyhow::Result<&str> {
    let split: Vec<_> = integrity.split_ascii_whitespace().collect();

    if split.len() == 1 {
        Ok(split[0])
    } else {
        for hash in ["sha512-", "sha1-"] {
            if let Some(h) = split.iter().find(|s| s.starts_with(hash)) {
                return Ok(h);
            }
        }

        Err(anyhow!("not sure which hash to select out of {split:?}"))
    }
}

fn main() -> anyhow::Result<()> {
    let args = env::args().collect::<Vec<_>>();

    if args.len() < 2 {
        println!("usage: {} <path/to/package-lock.json>", args[0]);
        println!();
        println!("Prefetches npm dependencies for usage by fetchNpmDeps.");

        process::exit(1);
    }

    let lock_content = fs::read_to_string(&args[1])?;
    let lock: PackageLock = serde_json::from_str(&lock_content)?;

    let out_tempdir;

    let (out, print_hash) = if let Some(path) = args.get(2) {
        (Path::new(path), false)
    } else {
        out_tempdir = tempdir()?;

        (out_tempdir.path(), true)
    };

    let agent = ureq::agent();

    eprintln!("lockfile version: {}", lock.version);

    let packages = match lock.version {
        1 => lock.dependencies.map(to_new_packages).transpose()?,
        2 | 3 => lock.packages,
        _ => panic!(
            "We don't support lockfile version {}, please file an issue.",
            lock.version
        ),
    };

    if packages.is_none() {
        return Ok(());
    }

    let cache = Cache::new(out.join("_cacache"));

    packages
        .unwrap()
        .into_par_iter()
        .filter(|(dep, _)| !dep.is_empty())
        .filter(|(_, package)| matches!(package.resolved, Some(UrlOrString::Url(_))))
        .try_for_each(|(dep, package)| {
            eprintln!("{dep}");

            let mut resolved = match package.resolved {
                Some(UrlOrString::Url(url)) => url,
                _ => unreachable!(),
            };

            if let Some(hosted_git_url) = get_hosted_git_url(&resolved) {
                resolved = hosted_git_url;
            }

            let mut data = Vec::new();

            agent
                .get(resolved.as_str())
                .call()?
                .into_reader()
                .read_to_end(&mut data)?;

            cache
                .put(
                    format!("make-fetch-happen:request-cache:{resolved}"),
                    resolved,
                    &data,
                    package
                        .integrity
                        .map(|i| Ok::<String, anyhow::Error>(get_ideal_hash(&i)?.to_string()))
                        .transpose()?,
                )
                .map_err(|e| anyhow!("couldn't insert cache entry for {dep}: {e:?}"))?;

            Ok::<_, anyhow::Error>(())
        })?;

    fs::write(out.join("package-lock.json"), lock_content)?;

    if print_hash {
        Command::new("nix")
            .args(["--experimental-features", "nix-command", "hash", "path"])
            .arg(out.as_os_str())
            .status()?;
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::{get_hosted_git_url, get_ideal_hash};
    use url::Url;

    #[test]
    fn hosted_git_urls() {
        for (input, expected) in [
            (
                "git+ssh://git@github.com/castlabs/electron-releases.git#fc5f78d046e8d7cdeb66345a2633c383ab41f525",
                Some("https://codeload.github.com/castlabs/electron-releases/tar.gz/fc5f78d046e8d7cdeb66345a2633c383ab41f525"),
            ),
            (
                "https://user@github.com/foo/bar#fix/bug",
                Some("https://codeload.github.com/foo/bar/tar.gz/fix/bug")
            ),
            (
                "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz",
                None
            ),
            (
                "git+ssh://bitbucket.org/foo/bar#branch",
                Some("https://bitbucket.org/foo/bar/get/branch.tar.gz")
            ),
            (
                "ssh://git@gitlab.com/foo/bar.git#fix/bug",
                Some("https://gitlab.com/foo/bar/repository/archive.tar.gz?ref=fix/bug")
            ),
            (
                "git+ssh://git.sr.ht/~foo/bar#branch",
                Some("https://git.sr.ht/~foo/bar/archive/branch.tar.gz")
            ),
        ] {
            assert_eq!(
                get_hosted_git_url(&Url::parse(input).unwrap()),
                expected.map(|u| Url::parse(u).unwrap())
            );
        }
    }

    #[test]
    fn ideal_hashes() {
        for (input, expected) in [
            ("sha512-foo sha1-bar", Some("sha512-foo")),
            ("sha1-bar md5-foo", Some("sha1-bar")),
            ("sha1-bar", Some("sha1-bar")),
            ("sha512-foo", Some("sha512-foo")),
            ("foo-bar sha1-bar", Some("sha1-bar")),
            ("foo-bar baz-foo", None),
        ] {
            assert_eq!(get_ideal_hash(input).ok(), expected);
        }
    }
}
<commit_msg>prefetch-npm-deps: add support for hosted git shorthands<commit_after>#![warn(clippy::pedantic)]

use crate::cacache::Cache;
use anyhow::{anyhow, Context};
use rayon::prelude::*;
use serde::Deserialize;
use std::{
    collections::HashMap,
    env, fmt, fs,
    path::Path,
    process::{self, Command},
};
use tempfile::tempdir;
use url::Url;

mod cacache;

#[derive(Deserialize)]
struct PackageLock {
    #[serde(rename = "lockfileVersion")]
    version: u8,
    dependencies: Option<HashMap<String, OldPackage>>,
    packages: Option<HashMap<String, Package>>,
}

#[derive(Deserialize)]
struct OldPackage {
    version: UrlOrString,
    resolved: Option<UrlOrString>,
    integrity: Option<String>,
    dependencies: Option<HashMap<String, OldPackage>>,
}

#[derive(Debug, Deserialize, PartialEq, Eq)]
struct Package {
    resolved: Option<UrlOrString>,
    integrity: Option<String>,
}

#[derive(Debug, Deserialize, PartialEq, Eq)]
#[serde(untagged)]
enum UrlOrString {
    Url(Url),
    String(String),
}

impl fmt::Display for UrlOrString {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            UrlOrString::Url(url) => url.fmt(f),
            UrlOrString::String(string) => string.fmt(f),
        }
    }
}

#[allow(clippy::case_sensitive_file_extension_comparisons)]
fn to_new_packages(
    old_packages: HashMap<String, OldPackage>,
    initial_url: &Url,
) -> anyhow::Result<HashMap<String, Package>> {
    let mut new = HashMap::new();

    for (name, mut package) in old_packages {
        if let UrlOrString::Url(v) = &package.version {
            for (scheme, host) in [
                ("github", "github.com"),
                ("bitbucket", "bitbucket.org"),
                ("gitlab", "gitlab.com"),
            ] {
                if v.scheme() == scheme {
                    package.version = {
                        let mut new_url = initial_url.clone();

                        new_url.set_host(Some(host))?;

                        if v.path().ends_with(".git") {
                            new_url.set_path(v.path());
                        } else {
                            new_url.set_path(&format!("{}.git", v.path()));
                        }

                        new_url.set_fragment(v.fragment());

                        UrlOrString::Url(new_url)
                    };

                    break;
                }
            }
        }

        new.insert(
            format!("{name}-{}", package.version),
            Package {
                resolved: if matches!(package.version, UrlOrString::Url(_)) {
                    Some(package.version)
                } else {
                    package.resolved
                },
                integrity: package.integrity,
            },
        );

        if let Some(dependencies) = package.dependencies {
            new.extend(to_new_packages(dependencies, initial_url)?);
        }
    }

    Ok(new)
}

#[allow(clippy::case_sensitive_file_extension_comparisons)]
fn get_hosted_git_url(url: &Url) -> Option<Url> {
    if ["git", "http", "git+ssh", "git+https", "ssh", "https"].contains(&url.scheme()) {
        let mut s = url.path_segments()?;

        match url.host_str()? {
            "github.com" => {
                let user = s.next()?;
                let mut project = s.next()?;
                let typ = s.next();
                let mut commit = s.next();

                if typ.is_none() {
                    commit = url.fragment();
                } else if typ.is_some() && typ != Some("tree") {
                    return None;
                }

                if project.ends_with(".git") {
                    project = project.strip_suffix(".git")?;
                }

                let commit = commit.unwrap();

                Some(
                    Url::parse(&format!(
                        "https://codeload.github.com/{user}/{project}/tar.gz/{commit}"
                    ))
                    .ok()?,
                )
            }
            "bitbucket.org" => {
                let user = s.next()?;
                let mut project = s.next()?;
                let aux = s.next();

                if aux == Some("get") {
                    return None;
                }

                if project.ends_with(".git") {
                    project = project.strip_suffix(".git")?;
                }

                let commit = url.fragment()?;

                Some(
                    Url::parse(&format!(
                        "https://bitbucket.org/{user}/{project}/get/{commit}.tar.gz"
                    ))
                    .ok()?,
                )
            }
            "gitlab.com" => {
                let path = &url.path()[1..];

                if path.contains("/~/") || path.contains("/archive.tar.gz") {
                    return None;
                }

                let user = s.next()?;
                let mut project = s.next()?;

                if project.ends_with(".git") {
                    project = project.strip_suffix(".git")?;
                }

                let commit = url.fragment()?;

                Some(
                    Url::parse(&format!(
                    "https://gitlab.com/{user}/{project}/repository/archive.tar.gz?ref={commit}"
                ))
                    .ok()?,
                )
            }
            "git.sr.ht" => {
                let user = s.next()?;
                let mut project = s.next()?;
                let aux = s.next();

                if aux == Some("archive") {
                    return None;
                }

                if project.ends_with(".git") {
                    project = project.strip_suffix(".git")?;
                }

                let commit = url.fragment()?;

                Some(
                    Url::parse(&format!(
                        "https://git.sr.ht/{user}/{project}/archive/{commit}.tar.gz"
                    ))
                    .ok()?,
                )
            }
            _ => None,
        }
    } else {
        None
    }
}

fn get_ideal_hash(integrity: &str) -> anyhow::Result<&str> {
    let split: Vec<_> = integrity.split_ascii_whitespace().collect();

    if split.len() == 1 {
        Ok(split[0])
    } else {
        for hash in ["sha512-", "sha1-"] {
            if let Some(h) = split.iter().find(|s| s.starts_with(hash)) {
                return Ok(h);
            }
        }

        Err(anyhow!("not sure which hash to select out of {split:?}"))
    }
}

fn get_initial_url() -> anyhow::Result<Url> {
    Url::parse("git+ssh://git@a.b").context("initial url should be valid")
}

fn main() -> anyhow::Result<()> {
    let args = env::args().collect::<Vec<_>>();

    if args.len() < 2 {
        println!("usage: {} <path/to/package-lock.json>", args[0]);
        println!();
        println!("Prefetches npm dependencies for usage by fetchNpmDeps.");

        process::exit(1);
    }

    let lock_content = fs::read_to_string(&args[1])?;
    let lock: PackageLock = serde_json::from_str(&lock_content)?;

    let out_tempdir;

    let (out, print_hash) = if let Some(path) = args.get(2) {
        (Path::new(path), false)
    } else {
        out_tempdir = tempdir()?;

        (out_tempdir.path(), true)
    };

    let agent = ureq::agent();

    eprintln!("lockfile version: {}", lock.version);

    let packages = match lock.version {
        1 => {
            let initial_url = get_initial_url()?;

            lock.dependencies
                .map(|p| to_new_packages(p, &initial_url))
                .transpose()?
        }
        2 | 3 => lock.packages,
        _ => panic!(
            "We don't support lockfile version {}, please file an issue.",
            lock.version
        ),
    };

    if packages.is_none() {
        return Ok(());
    }

    let cache = Cache::new(out.join("_cacache"));

    packages
        .unwrap()
        .into_par_iter()
        .filter(|(dep, _)| !dep.is_empty())
        .filter(|(_, package)| matches!(package.resolved, Some(UrlOrString::Url(_))))
        .try_for_each(|(dep, package)| {
            eprintln!("{dep}");

            let mut resolved = match package.resolved {
                Some(UrlOrString::Url(url)) => url,
                _ => unreachable!(),
            };

            if let Some(hosted_git_url) = get_hosted_git_url(&resolved) {
                resolved = hosted_git_url;
            }

            let mut data = Vec::new();

            agent
                .get(resolved.as_str())
                .call()?
                .into_reader()
                .read_to_end(&mut data)?;

            cache
                .put(
                    format!("make-fetch-happen:request-cache:{resolved}"),
                    resolved,
                    &data,
                    package
                        .integrity
                        .map(|i| Ok::<String, anyhow::Error>(get_ideal_hash(&i)?.to_string()))
                        .transpose()?,
                )
                .map_err(|e| anyhow!("couldn't insert cache entry for {dep}: {e:?}"))?;

            Ok::<_, anyhow::Error>(())
        })?;

    fs::write(out.join("package-lock.json"), lock_content)?;

    if print_hash {
        Command::new("nix")
            .args(["--experimental-features", "nix-command", "hash", "path"])
            .arg(out.as_os_str())
            .status()?;
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::{
        get_hosted_git_url, get_ideal_hash, get_initial_url, to_new_packages, OldPackage, Package,
        UrlOrString,
    };
    use std::collections::HashMap;
    use url::Url;

    #[test]
    fn hosted_git_urls() {
        for (input, expected) in [
            (
                "git+ssh://git@github.com/castlabs/electron-releases.git#fc5f78d046e8d7cdeb66345a2633c383ab41f525",
                Some("https://codeload.github.com/castlabs/electron-releases/tar.gz/fc5f78d046e8d7cdeb66345a2633c383ab41f525"),
            ),
            (
                "https://user@github.com/foo/bar#fix/bug",
                Some("https://codeload.github.com/foo/bar/tar.gz/fix/bug")
            ),
            (
                "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz",
                None
            ),
            (
                "git+ssh://bitbucket.org/foo/bar#branch",
                Some("https://bitbucket.org/foo/bar/get/branch.tar.gz")
            ),
            (
                "ssh://git@gitlab.com/foo/bar.git#fix/bug",
                Some("https://gitlab.com/foo/bar/repository/archive.tar.gz?ref=fix/bug")
            ),
            (
                "git+ssh://git.sr.ht/~foo/bar#branch",
                Some("https://git.sr.ht/~foo/bar/archive/branch.tar.gz")
            ),
        ] {
            assert_eq!(
                get_hosted_git_url(&Url::parse(input).unwrap()),
                expected.map(|u| Url::parse(u).unwrap())
            );
        }
    }

    #[test]
    fn ideal_hashes() {
        for (input, expected) in [
            ("sha512-foo sha1-bar", Some("sha512-foo")),
            ("sha1-bar md5-foo", Some("sha1-bar")),
            ("sha1-bar", Some("sha1-bar")),
            ("sha512-foo", Some("sha512-foo")),
            ("foo-bar sha1-bar", Some("sha1-bar")),
            ("foo-bar baz-foo", None),
        ] {
            assert_eq!(get_ideal_hash(input).ok(), expected);
        }
    }

    #[test]
    fn git_shorthand_v1() -> anyhow::Result<()> {
        let old =
            {
                let mut o = HashMap::new();
                o.insert(
                String::from("sqlite3"),
                OldPackage {
                    version: UrlOrString::Url(Url::parse(
                        "github:mapbox/node-sqlite3#593c9d498be2510d286349134537e3bf89401c4a",
                    ).unwrap()),
                    resolved: None,
                    integrity: None,
                    dependencies: None,
                },
            );
                o
            };

        let initial_url = get_initial_url()?;

        let new = to_new_packages(old, &initial_url)?;

        assert_eq!(new.len(), 1, "new packages map should contain 1 value");
        assert_eq!(new.into_values().next().unwrap(), Package {
            resolved: Some(UrlOrString::Url(Url::parse("git+ssh://git@github.com/mapbox/node-sqlite3.git#593c9d498be2510d286349134537e3bf89401c4a").unwrap())),
            integrity: None
        });

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std;
use std::str;
use std::ptr;
use std::ffi::OsStr;
use std::ops::Deref;
use std::borrow::Borrow;
use std::iter::{FromIterator, IntoIterator};
use smallvec;
use smallvec::{Array, SmallVec};

#[derive(Clone, Default)]
pub struct SmallString<B: Array<Item = u8>> {
    buffer: SmallVec<B>,
}

impl<B: Array<Item = u8>> SmallString<B> {
    pub fn new() -> Self {
        SmallString { buffer: SmallVec::new() }
    }

    pub fn from_str(s: &str) -> Self {
        SmallString { buffer: s.as_bytes().into_iter().cloned().collect() }
    }

    unsafe fn insert_bytes(&mut self, idx: usize, bytes: &[u8]) {
        let len = self.len();
        let amt = bytes.len();
        self.buffer.reserve(amt);

        ptr::copy(
            self.buffer.as_ptr().offset(idx as isize),
            self.buffer.as_mut_ptr().offset((idx + amt) as isize),
            len - idx,
        );
        ptr::copy(
            bytes.as_ptr(),
            self.buffer.as_mut_ptr().offset(idx as isize),
            amt,
        );
        self.buffer.set_len(len + amt);
    }

    #[inline]
    pub fn insert_str(&mut self, idx: usize, string: &str) {
        assert!(self.is_char_boundary(idx));

        unsafe {
            self.insert_bytes(idx, string.as_bytes());
        }
    }

    #[inline]
    pub unsafe fn as_mut_smallvec(&mut self) -> &mut SmallVec<B> {
        &mut self.buffer
    }

    #[inline]
    pub fn split_off(&mut self, at: usize) -> SmallString<B> {
        assert!(self.is_char_boundary(at));
        let len = self.len();
        let mut other = SmallString::new();
        unsafe {
            ptr::copy_nonoverlapping(
                self.buffer.as_ptr().offset(at as isize),
                other.buffer.as_mut_ptr().offset(0),
                len - at,
            );
            self.buffer.set_len(at);
            other.buffer.set_len(len - at);
        }
        other
    }

    #[inline]
    pub fn shrink_to_fit(&mut self) {
        self.buffer.shrink_to_fit();
    }
}

impl<B: Array<Item = u8>> std::hash::Hash for SmallString<B> {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        let s: &str = self;
        s.hash(state)
    }
}

impl<B: Array<Item = u8>> std::cmp::PartialEq for SmallString<B> {
    fn eq(&self, other: &Self) -> bool {
        let (s1, s2): (&str, &str) = (self, other);
        s1 == s2
    }
}

impl<B: Array<Item = u8>> std::cmp::Eq for SmallString<B> {}

impl<'a, B: Array<Item = u8>> PartialEq<SmallString<B>> for &'a str {
    fn eq(&self, other: &SmallString<B>) -> bool {
        *self == (other as &str)
    }
}

impl<B: Array<Item = u8>> std::fmt::Display for SmallString<B> {
    fn fmt(&self, fm: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        let s: &str = SmallString::deref(self);
        s.fmt(fm)
    }
}

impl<B: Array<Item = u8>> std::fmt::Debug for SmallString<B> {
    fn fmt(&self, fm: &mut std::fmt::Formatter) -> std::fmt::Result {
        let s: &str = SmallString::deref(self);
        s.fmt(fm)
    }
}

impl<'a, B: Array<Item = u8>> From<&'a str> for SmallString<B> {
    fn from(s: &str) -> Self {
        Self::from_str(s)
    }
}

impl<B: Array<Item = u8>> Deref for SmallString<B> {
    type Target = str;

    fn deref(&self) -> &str {
        // We only allow `buffer` to be created from an existing valid string,
        // so this is safe.
        unsafe { str::from_utf8_unchecked(self.buffer.as_ref()) }
    }
}

impl<B: Array<Item = u8>> AsRef<str> for SmallString<B> {
    fn as_ref(&self) -> &str {
        // We only allow `buffer` to be created from an existing valid string,
        // so this is safe.
        unsafe { str::from_utf8_unchecked(self.buffer.as_ref()) }
    }
}

struct Utf8Iterator<I>(I, Option<smallvec::IntoIter<[u8; 4]>>);

impl<I: Iterator<Item = char>> Utf8Iterator<I> {
    pub fn new<In: IntoIterator<IntoIter = I, Item = char>>(into: In) -> Self {
        Utf8Iterator(into.into_iter(), None)
    }
}

impl<I: Iterator<Item = char>> Iterator for Utf8Iterator<I> {
    type Item = u8;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(mut into) = self.1.take() {
            if let Some(n) = into.next() {
                self.1 = Some(into);
                return Some(n);
            }
        }

        let out = self.0.next();

        out.and_then(|chr| {
            let mut dest = [0u8; 4];
            let outstr = chr.encode_utf8(&mut dest);

            self.1 = Some(
                outstr
                    .as_bytes()
                    .into_iter()
                    .cloned()
                    .collect::<SmallVec<[u8; 4]>>()
                    .into_iter(),
            );

            self.1.as_mut().and_then(|i| i.next())
        })
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let hint = self.0.size_hint();

        (hint.0, hint.1.map(|x| x * 4))
    }
}

impl<B: Array<Item = u8>> FromIterator<char> for SmallString<B> {
    fn from_iter<T: IntoIterator<Item = char>>(into_iter: T) -> Self {
        // We're a shell so we mostly work with ASCII data - optimise for this
        // case since we have to optimise for _some_ fixed size of char.
        let utf8 = Utf8Iterator::new(into_iter);

        SmallString { buffer: utf8.collect() }
    }
}

#[cfg(feature = "as-mut")]
impl<B: Array<Item = u8>> AsMut<str> for SmallString<B> {
    fn as_mut(&mut self) -> &mut str {
        // We only allow `buffer` to be created from an existing valid string,
        // so this is safe.
        unsafe { str::from_utf8_unchecked_mut(self.buffer.as_mut()) }
    }
}

impl<B: Array<Item = u8>> AsRef<OsStr> for SmallString<B> {
    fn as_ref(&self) -> &OsStr {
        let s: &str = self.as_ref();
        s.as_ref()
    }
}

impl<B: Array<Item = u8>> Borrow<str> for SmallString<B> {
    fn borrow(&self) -> &str {
        // We only allow `buffer` to be created from an existing valid string,
        // so this is safe.
        unsafe { str::from_utf8_unchecked(self.buffer.as_ref()) }
    }
}

impl<B: Array<Item = u8>> From<String> for SmallString<B> {
    fn from(s: String) -> SmallString<B> {
        SmallString { buffer: SmallVec::from_vec(s.into_bytes()) }
    }
}

impl<B: Array<Item = u8>> From<SmallString<B>> for String {
    fn from(s: SmallString<B>) -> String {
        unsafe { String::from_utf8_unchecked(s.buffer.into_vec()) }
    }
}
<commit_msg>Fixed a nasty memory safety issue in SmallString:split_off().<commit_after>use std;
use std::str;
use std::ptr;
use std::ffi::OsStr;
use std::ops::Deref;
use std::borrow::Borrow;
use std::iter::{FromIterator, IntoIterator};
use smallvec;
use smallvec::{Array, SmallVec};

#[derive(Clone, Default)]
pub struct SmallString<B: Array<Item = u8>> {
    buffer: SmallVec<B>,
}

impl<B: Array<Item = u8>> SmallString<B> {
    pub fn new() -> Self {
        SmallString { buffer: SmallVec::new() }
    }

    pub fn with_capacity(capacity: usize) -> Self {
        SmallString { buffer: SmallVec::with_capacity(capacity) }
    }

    pub fn from_str(s: &str) -> Self {
        SmallString { buffer: s.as_bytes().into_iter().cloned().collect() }
    }

    unsafe fn insert_bytes(&mut self, idx: usize, bytes: &[u8]) {
        debug_assert!(idx <= self.len());
        let len = self.len();
        let amt = bytes.len();
        self.buffer.reserve(amt);

        ptr::copy(
            self.buffer.as_ptr().offset(idx as isize),
            self.buffer.as_mut_ptr().offset((idx + amt) as isize),
            len - idx,
        );
        ptr::copy(
            bytes.as_ptr(),
            self.buffer.as_mut_ptr().offset(idx as isize),
            amt,
        );
        self.buffer.set_len(len + amt);
    }

    unsafe fn remove_bytes(&mut self, start: usize, end: usize) {
        debug_assert!(end >= start);
        debug_assert!(start > 0);
        debug_assert!(end <= self.len());
        let len = self.len();
        let amt = end - start;
        ptr::copy(
            self.buffer.as_ptr().offset(end as isize),
            self.buffer.as_mut_ptr().offset(start as isize),
            len - end,
        );
        self.buffer.set_len(len - amt);
    }

    #[inline]
    pub fn insert_str(&mut self, idx: usize, string: &str) {
        assert!(self.is_char_boundary(idx));

        unsafe {
            self.insert_bytes(idx, string.as_bytes());
        }
    }

    #[inline]
    pub fn push_str(&mut self, string: &str) {
        let len = self.len();
        unsafe {
            self.insert_bytes(len, string.as_bytes());
        }
    }

    #[inline]
    pub unsafe fn as_mut_smallvec(&mut self) -> &mut SmallVec<B> {
        &mut self.buffer
    }

    #[inline]
    pub fn split_off(&mut self, at: usize) -> SmallString<B> {
        assert!(self.is_char_boundary(at));
        let len = self.len();
        let mut other = SmallString::with_capacity(len - at);
        unsafe {
            ptr::copy_nonoverlapping(
                self.buffer.as_ptr().offset(at as isize),
                other.buffer.as_mut_ptr().offset(0),
                len - at,
            );
            self.buffer.set_len(at);
            other.buffer.set_len(len - at);
        }
        other
    }

    #[inline]
    pub fn truncate(&mut self, size: usize) {
        assert!(self.is_char_boundary(size));
        self.buffer.truncate(size);
    }

    #[inline]
    pub fn truncate_front(&mut self, size: usize) {
        assert!(self.is_char_boundary(size));
        unsafe {
            self.remove_bytes(0, size);
        }
    }

    #[inline]
    pub fn shrink_to_fit(&mut self) {
        self.buffer.shrink_to_fit();
    }
}

impl<B: Array<Item = u8>> std::hash::Hash for SmallString<B> {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        let s: &str = self;
        s.hash(state)
    }
}

impl<B: Array<Item = u8>> std::cmp::PartialEq for SmallString<B> {
    fn eq(&self, other: &Self) -> bool {
        let (s1, s2): (&str, &str) = (self, other);
        s1 == s2
    }
}

impl<B: Array<Item = u8>> std::cmp::Eq for SmallString<B> {}

impl<'a, B: Array<Item = u8>> PartialEq<SmallString<B>> for &'a str {
    fn eq(&self, other: &SmallString<B>) -> bool {
        *self == (other as &str)
    }
}

impl<B: Array<Item = u8>> std::fmt::Display for SmallString<B> {
    fn fmt(&self, fm: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        let s: &str = SmallString::deref(self);
        s.fmt(fm)
    }
}

impl<B: Array<Item = u8>> std::fmt::Debug for SmallString<B> {
    fn fmt(&self, fm: &mut std::fmt::Formatter) -> std::fmt::Result {
        let s: &str = SmallString::deref(self);
        s.fmt(fm)
    }
}

impl<'a, B: Array<Item = u8>> From<&'a str> for SmallString<B> {
    fn from(s: &str) -> Self {
        Self::from_str(s)
    }
}

impl<B: Array<Item = u8>> Deref for SmallString<B> {
    type Target = str;

    fn deref(&self) -> &str {
        // We only allow `buffer` to be created from an existing valid string,
        // so this is safe.
        unsafe { str::from_utf8_unchecked(self.buffer.as_ref()) }
    }
}

impl<B: Array<Item = u8>> AsRef<str> for SmallString<B> {
    fn as_ref(&self) -> &str {
        // We only allow `buffer` to be created from an existing valid string,
        // so this is safe.
        unsafe { str::from_utf8_unchecked(self.buffer.as_ref()) }
    }
}

struct Utf8Iterator<I>(I, Option<smallvec::IntoIter<[u8; 4]>>);

impl<I: Iterator<Item = char>> Utf8Iterator<I> {
    pub fn new<In: IntoIterator<IntoIter = I, Item = char>>(into: In) -> Self {
        Utf8Iterator(into.into_iter(), None)
    }
}

impl<I: Iterator<Item = char>> Iterator for Utf8Iterator<I> {
    type Item = u8;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(mut into) = self.1.take() {
            if let Some(n) = into.next() {
                self.1 = Some(into);
                return Some(n);
            }
        }

        let out = self.0.next();

        out.and_then(|chr| {
            let mut dest = [0u8; 4];
            let outstr = chr.encode_utf8(&mut dest);

            self.1 = Some(
                outstr
                    .as_bytes()
                    .into_iter()
                    .cloned()
                    .collect::<SmallVec<[u8; 4]>>()
                    .into_iter(),
            );

            self.1.as_mut().and_then(|i| i.next())
        })
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let hint = self.0.size_hint();

        (hint.0, hint.1.map(|x| x * 4))
    }
}

impl<B: Array<Item = u8>> FromIterator<char> for SmallString<B> {
    fn from_iter<T: IntoIterator<Item = char>>(into_iter: T) -> Self {
        // We're a shell so we mostly work with ASCII data - optimise for this
        // case since we have to optimise for _some_ fixed size of char.
        let utf8 = Utf8Iterator::new(into_iter);

        SmallString { buffer: utf8.collect() }
    }
}

#[cfg(feature = "as-mut")]
impl<B: Array<Item = u8>> AsMut<str> for SmallString<B> {
    fn as_mut(&mut self) -> &mut str {
        // We only allow `buffer` to be created from an existing valid string,
        // so this is safe.
        unsafe { str::from_utf8_unchecked_mut(self.buffer.as_mut()) }
    }
}

impl<B: Array<Item = u8>> AsRef<OsStr> for SmallString<B> {
    fn as_ref(&self) -> &OsStr {
        let s: &str = self.as_ref();
        s.as_ref()
    }
}

impl<B: Array<Item = u8>> Borrow<str> for SmallString<B> {
    fn borrow(&self) -> &str {
        // We only allow `buffer` to be created from an existing valid string,
        // so this is safe.
        unsafe { str::from_utf8_unchecked(self.buffer.as_ref()) }
    }
}

impl<B: Array<Item = u8>> From<String> for SmallString<B> {
    fn from(s: String) -> SmallString<B> {
        SmallString { buffer: SmallVec::from_vec(s.into_bytes()) }
    }
}

impl<B: Array<Item = u8>> From<SmallString<B>> for String {
    fn from(s: SmallString<B>) -> String {
        unsafe { String::from_utf8_unchecked(s.buffer.into_vec()) }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use rope::BackingArray;
    type SS = SmallString<BackingArray>;

    #[test]
    fn remove_bytes_01() {
        let mut s = SS::new();
        s.push_str("Hello there, everyone!  How's it going?");
        unsafe {
            s.remove_bytes(11, 21);
        }
        assert_eq!("Hello there!  How's it going?", s);
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 The Rust Project Developers.
// Copyright 2018 The Uuid Project Developers.
//
// See the COPYRIGHT file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.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.

use byteorder;
use prelude::*;

impl Uuid {
    /// Creates a new [`Uuid`] from a `u128` value.
    ///
    /// To create a [`Uuid`] from `u128`s, you need `u128` feature enabled for this crate.
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    #[inline]
    pub fn from_u128(quad: u128) -> Self {
        Uuid::from(quad)
    }
}

impl From<u128> for Uuid {
    fn from(f: u128) -> Self {
        let mut bytes = [0; 16];

        {
            use byteorder::ByteOrder;

            byteorder::NativeEndian::write_u128(&mut bytes[..], f);
        }

        Uuid {
            bytes
        }
    }
}<commit_msg>run fmt<commit_after>// Copyright 2013-2014 The Rust Project Developers.
// Copyright 2018 The Uuid Project Developers.
//
// See the COPYRIGHT file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.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.

use byteorder;
use prelude::*;

impl Uuid {
    /// Creates a new [`Uuid`] from a `u128` value.
    ///
    /// To create a [`Uuid`] from `u128`s, you need `u128` feature enabled for this crate.
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    #[inline]
    pub fn from_u128(quad: u128) -> Self {
        Uuid::from(quad)
    }
}

impl From<u128> for Uuid {
    fn from(f: u128) -> Self {
        let mut bytes = [0; 16];

        {
            use byteorder::ByteOrder;

            byteorder::NativeEndian::write_u128(&mut bytes[..], f);
        }

        Uuid { bytes }
    }
}
<|endoftext|>


<commit_before>use std::slice;
use std::mem;

pub enum UniformPrimitiveType {
    Float,
    Int
}

pub enum UniformDatumType {
    Vec1(UniformPrimitiveType),
    Vec2(UniformPrimitiveType),
    Vec3(UniformPrimitiveType),
    Vec4(UniformPrimitiveType),
    Matrix2x2,
    Matrix3x3,
    Matrix4x4
}

pub trait UniformData {
    fn uniform_datum_type() -> UniformDatumType;
    fn uniform_bytes(&self) -> &[u8];
    fn uniform_elements(&self) -> usize;
}

pub trait UniformDatum {
    fn uniform_datum_type() -> UniformDatumType;
}

pub trait UniformPrimitive {
    fn uniform_primitive_type() -> UniformPrimitiveType;
}
<commit_msg>Add impls of `UniformPrimitive` for `f32` and `i32`<commit_after>use std::slice;
use std::mem;

pub enum UniformPrimitiveType {
    Float,
    Int
}

pub enum UniformDatumType {
    Vec1(UniformPrimitiveType),
    Vec2(UniformPrimitiveType),
    Vec3(UniformPrimitiveType),
    Vec4(UniformPrimitiveType),
    Matrix2x2,
    Matrix3x3,
    Matrix4x4
}

pub trait UniformData {
    fn uniform_datum_type() -> UniformDatumType;
    fn uniform_bytes(&self) -> &[u8];
    fn uniform_elements(&self) -> usize;
}

pub trait UniformDatum {
    fn uniform_datum_type() -> UniformDatumType;
}

pub trait UniformPrimitive {
    fn uniform_primitive_type() -> UniformPrimitiveType;
}

impl UniformPrimitive for f32 {
    fn uniform_primitive_type() -> UniformPrimitiveType {
        UniformPrimitiveType::Float
    }
}

impl UniformPrimitive for i32 {
    fn uniform_primitive_type() -> UniformPrimitiveType {
        UniformPrimitiveType::Int
    }
}
<|endoftext|>