<commit_before>//! Rows.

use crate::row::sealed::{AsName, Sealed};
use crate::statement::Column;
use crate::types::{FromSql, Type, WrongType};
use crate::{Error, Statement};
use fallible_iterator::FallibleIterator;
use postgres_protocol::message::backend::DataRowBody;
use std::fmt;
use std::ops::Range;
use std::str;
use std::sync::Arc;

mod sealed {
    pub trait Sealed {}

    pub trait AsName {
        fn as_name(&self) -> &str;
    }
}

impl AsName for Column {
    fn as_name(&self) -> &str {
        self.name()
    }
}

impl AsName for String {
    fn as_name(&self) -> &str {
        self
    }
}

/// A trait implemented by types that can index into columns of a row.
///
/// This cannot be implemented outside of this crate.
pub trait RowIndex: Sealed {
    #[doc(hidden)]
    fn __idx<T>(&self, columns: &[T]) -> Option<usize>
    where
        T: AsName;
}

impl Sealed for usize {}

impl RowIndex for usize {
    #[inline]
    fn __idx<T>(&self, columns: &[T]) -> Option<usize>
    where
        T: AsName,
    {
        if *self >= columns.len() {
            None
        } else {
            Some(*self)
        }
    }
}

impl Sealed for str {}

impl RowIndex for str {
    #[inline]
    fn __idx<T>(&self, columns: &[T]) -> Option<usize>
    where
        T: AsName,
    {
        if let Some(idx) = columns.iter().position(|d| d.as_name() == self) {
            return Some(idx);
        };

        // FIXME ASCII-only case insensitivity isn't really the right thing to
        // do. Postgres itself uses a dubious wrapper around tolower and JDBC
        // uses the US locale.
        columns
            .iter()
            .position(|d| d.as_name().eq_ignore_ascii_case(self))
    }
}

impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {}

impl<'a, T> RowIndex for &'a T
where
    T: ?Sized + RowIndex,
{
    #[inline]
    fn __idx<U>(&self, columns: &[U]) -> Option<usize>
    where
        U: AsName,
    {
        T::__idx(*self, columns)
    }
}

/// A row of data returned from the database by a query.
pub struct Row {
    statement: Statement,
    body: DataRowBody,
    ranges: Vec<Option<Range<usize>>>,
}

/// A macro to map pg types to rust types, for debug display.
macro_rules! debug_row_type {
    ($this:expr; $map:expr; $idx:expr; $name:expr; $type:expr; $($pg_ty:tt => $ty:ty),*) => {
        match $type {
            $(
                &Type::$pg_ty => match <$ty as FromSql>::from_sql_nullable(
                    &Type::$pg_ty,
                    $this.0.col_buffer($idx),
                ) {
                    Ok(val) => $map.entry(&$name, &val),
                    Err(_) => $map.entry(&$name, &"<error>"),
                },
            )*
            _ => $map.entry(&$name, &"<opaque type>"),
        }
    }
}

impl fmt::Debug for Row {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Provides debug impl for row contents.
        struct RowData<'a>(&'a Row);

        impl fmt::Debug for RowData<'_> {
            #[allow(clippy::match_ref_pats)]
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                let mut map = f.debug_map();
                for (idx, col) in self.0.columns().iter().enumerate() {
                    debug_row_type!(self; map; idx; col.name(); col.type_();
                        BOOL => bool,
                        INT2 => i16,
                        INT4 => i32,
                        INT8 => i64,
                        FLOAT4 => f32,
                        FLOAT8 => f64,
                        VARCHAR => String,
                        BPCHAR => String,
                        TEXT => String,
                        JSON => String,
                        XML => String,
                        TIMESTAMPTZ => std::time::SystemTime,
                        TIMESTAMP => std::time::SystemTime,
                        BYTEA => Vec<u8>
                        // More types could be added here.
                    );
                }
                map.finish()
            }
        }

        f.debug_struct("Row")
            .field("columns", &self.columns())
            .field("data", &RowData(self))
            .finish()
    }
}

impl Row {
    pub(crate) fn new(statement: Statement, body: DataRowBody) -> Result<Row, Error> {
        let ranges = body.ranges().collect().map_err(Error::parse)?;
        Ok(Row {
            statement,
            body,
            ranges,
        })
    }

    /// Returns information about the columns of data in the row.
    pub fn columns(&self) -> &[Column] {
        self.statement.columns()
    }

    /// Determines if the row contains no values.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns the number of values in the row.
    pub fn len(&self) -> usize {
        self.columns().len()
    }

    /// Deserializes a value from the row.
    ///
    /// The value can be specified either by its numeric index in the row, or by its column name.
    ///
    /// # Panics
    ///
    /// Panics if the index is out of bounds or if the value cannot be converted to the specified type.
    pub fn get<'a, I, T>(&'a self, idx: I) -> T
    where
        I: RowIndex + fmt::Display,
        T: FromSql<'a>,
    {
        match self.get_inner(&idx) {
            Ok(ok) => ok,
            Err(err) => panic!("error retrieving column {}: {}", idx, err),
        }
    }

    /// Like `Row::get`, but returns a `Result` rather than panicking.
    pub fn try_get<'a, I, T>(&'a self, idx: I) -> Result<T, Error>
    where
        I: RowIndex + fmt::Display,
        T: FromSql<'a>,
    {
        self.get_inner(&idx)
    }

    fn get_inner<'a, I, T>(&'a self, idx: &I) -> Result<T, Error>
    where
        I: RowIndex + fmt::Display,
        T: FromSql<'a>,
    {
        let idx = match idx.__idx(self.columns()) {
            Some(idx) => idx,
            None => return Err(Error::column(idx.to_string())),
        };

        let ty = self.columns()[idx].type_();
        if !T::accepts(ty) {
            return Err(Error::from_sql(
                Box::new(WrongType::new::<T>(ty.clone())),
                idx,
            ));
        }

        FromSql::from_sql_nullable(ty, self.col_buffer(idx)).map_err(|e| Error::from_sql(e, idx))
    }

    /// Get the raw bytes for the column at the given index.
    fn col_buffer(&self, idx: usize) -> Option<&[u8]> {
        let range = self.ranges[idx].to_owned()?;
        Some(&self.body.buffer()[range])
    }
}

/// A row of data returned from the database by a simple query.
pub struct SimpleQueryRow {
    columns: Arc<[String]>,
    body: DataRowBody,
    ranges: Vec<Option<Range<usize>>>,
}

impl SimpleQueryRow {
    #[allow(clippy::new_ret_no_self)]
    pub(crate) fn new(columns: Arc<[String]>, body: DataRowBody) -> Result<SimpleQueryRow, Error> {
        let ranges = body.ranges().collect().map_err(Error::parse)?;
        Ok(SimpleQueryRow {
            columns,
            body,
            ranges,
        })
    }

    /// Determines if the row contains no values.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns the number of values in the row.
    pub fn len(&self) -> usize {
        self.columns.len()
    }

    /// Returns a value from the row.
    ///
    /// The value can be specified either by its numeric index in the row, or by its column name.
    ///
    /// # Panics
    ///
    /// Panics if the index is out of bounds or if the value cannot be converted to the specified type.
    pub fn get<I>(&self, idx: I) -> Option<&str>
    where
        I: RowIndex + fmt::Display,
    {
        match self.get_inner(&idx) {
            Ok(ok) => ok,
            Err(err) => panic!("error retrieving column {}: {}", idx, err),
        }
    }

    /// Like `SimpleQueryRow::get`, but returns a `Result` rather than panicking.
    pub fn try_get<I>(&self, idx: I) -> Result<Option<&str>, Error>
    where
        I: RowIndex + fmt::Display,
    {
        self.get_inner(&idx)
    }

    fn get_inner<I>(&self, idx: &I) -> Result<Option<&str>, Error>
    where
        I: RowIndex + fmt::Display,
    {
        let idx = match idx.__idx(&self.columns) {
            Some(idx) => idx,
            None => return Err(Error::column(idx.to_string())),
        };

        let buf = self.ranges[idx].clone().map(|r| &self.body.buffer()[r]);
        FromSql::from_sql_nullable(&Type::TEXT, buf).map_err(|e| Error::from_sql(e, idx))
    }
}
<commit_msg>Make requested changes<commit_after>//! Rows.

use crate::row::sealed::{AsName, Sealed};
use crate::statement::Column;
use crate::types::{FromSql, Type, WrongType};
use crate::{Error, Statement};
use fallible_iterator::FallibleIterator;
use postgres_protocol::message::backend::DataRowBody;
use std::fmt;
use std::ops::Range;
use std::str;
use std::sync::Arc;

mod sealed {
    pub trait Sealed {}

    pub trait AsName {
        fn as_name(&self) -> &str;
    }
}

impl AsName for Column {
    fn as_name(&self) -> &str {
        self.name()
    }
}

impl AsName for String {
    fn as_name(&self) -> &str {
        self
    }
}

/// A trait implemented by types that can index into columns of a row.
///
/// This cannot be implemented outside of this crate.
pub trait RowIndex: Sealed {
    #[doc(hidden)]
    fn __idx<T>(&self, columns: &[T]) -> Option<usize>
    where
        T: AsName;
}

impl Sealed for usize {}

impl RowIndex for usize {
    #[inline]
    fn __idx<T>(&self, columns: &[T]) -> Option<usize>
    where
        T: AsName,
    {
        if *self >= columns.len() {
            None
        } else {
            Some(*self)
        }
    }
}

impl Sealed for str {}

impl RowIndex for str {
    #[inline]
    fn __idx<T>(&self, columns: &[T]) -> Option<usize>
    where
        T: AsName,
    {
        if let Some(idx) = columns.iter().position(|d| d.as_name() == self) {
            return Some(idx);
        };

        // FIXME ASCII-only case insensitivity isn't really the right thing to
        // do. Postgres itself uses a dubious wrapper around tolower and JDBC
        // uses the US locale.
        columns
            .iter()
            .position(|d| d.as_name().eq_ignore_ascii_case(self))
    }
}

impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {}

impl<'a, T> RowIndex for &'a T
where
    T: ?Sized + RowIndex,
{
    #[inline]
    fn __idx<U>(&self, columns: &[U]) -> Option<usize>
    where
        U: AsName,
    {
        T::__idx(*self, columns)
    }
}

/// A row of data returned from the database by a query.
pub struct Row {
    statement: Statement,
    body: DataRowBody,
    ranges: Vec<Option<Range<usize>>>,
}

impl fmt::Debug for Row {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Row")
            .field("columns", &self.columns())
            .finish()
    }
}

impl Row {
    pub(crate) fn new(statement: Statement, body: DataRowBody) -> Result<Row, Error> {
        let ranges = body.ranges().collect().map_err(Error::parse)?;
        Ok(Row {
            statement,
            body,
            ranges,
        })
    }

    /// Returns information about the columns of data in the row.
    pub fn columns(&self) -> &[Column] {
        self.statement.columns()
    }

    /// Determines if the row contains no values.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns the number of values in the row.
    pub fn len(&self) -> usize {
        self.columns().len()
    }

    /// Deserializes a value from the row.
    ///
    /// The value can be specified either by its numeric index in the row, or by its column name.
    ///
    /// # Panics
    ///
    /// Panics if the index is out of bounds or if the value cannot be converted to the specified type.
    pub fn get<'a, I, T>(&'a self, idx: I) -> T
    where
        I: RowIndex + fmt::Display,
        T: FromSql<'a>,
    {
        match self.get_inner(&idx) {
            Ok(ok) => ok,
            Err(err) => panic!("error retrieving column {}: {}", idx, err),
        }
    }

    /// Like `Row::get`, but returns a `Result` rather than panicking.
    pub fn try_get<'a, I, T>(&'a self, idx: I) -> Result<T, Error>
    where
        I: RowIndex + fmt::Display,
        T: FromSql<'a>,
    {
        self.get_inner(&idx)
    }

    fn get_inner<'a, I, T>(&'a self, idx: &I) -> Result<T, Error>
    where
        I: RowIndex + fmt::Display,
        T: FromSql<'a>,
    {
        let idx = match idx.__idx(self.columns()) {
            Some(idx) => idx,
            None => return Err(Error::column(idx.to_string())),
        };

        let ty = self.columns()[idx].type_();
        if !T::accepts(ty) {
            return Err(Error::from_sql(
                Box::new(WrongType::new::<T>(ty.clone())),
                idx,
            ));
        }

        FromSql::from_sql_nullable(ty, self.col_buffer(idx)).map_err(|e| Error::from_sql(e, idx))
    }

    /// Get the raw bytes for the column at the given index.
    fn col_buffer(&self, idx: usize) -> Option<&[u8]> {
        let range = self.ranges[idx].to_owned()?;
        Some(&self.body.buffer()[range])
    }
}

/// A row of data returned from the database by a simple query.
pub struct SimpleQueryRow {
    columns: Arc<[String]>,
    body: DataRowBody,
    ranges: Vec<Option<Range<usize>>>,
}

impl SimpleQueryRow {
    #[allow(clippy::new_ret_no_self)]
    pub(crate) fn new(columns: Arc<[String]>, body: DataRowBody) -> Result<SimpleQueryRow, Error> {
        let ranges = body.ranges().collect().map_err(Error::parse)?;
        Ok(SimpleQueryRow {
            columns,
            body,
            ranges,
        })
    }

    /// Determines if the row contains no values.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns the number of values in the row.
    pub fn len(&self) -> usize {
        self.columns.len()
    }

    /// Returns a value from the row.
    ///
    /// The value can be specified either by its numeric index in the row, or by its column name.
    ///
    /// # Panics
    ///
    /// Panics if the index is out of bounds or if the value cannot be converted to the specified type.
    pub fn get<I>(&self, idx: I) -> Option<&str>
    where
        I: RowIndex + fmt::Display,
    {
        match self.get_inner(&idx) {
            Ok(ok) => ok,
            Err(err) => panic!("error retrieving column {}: {}", idx, err),
        }
    }

    /// Like `SimpleQueryRow::get`, but returns a `Result` rather than panicking.
    pub fn try_get<I>(&self, idx: I) -> Result<Option<&str>, Error>
    where
        I: RowIndex + fmt::Display,
    {
        self.get_inner(&idx)
    }

    fn get_inner<I>(&self, idx: &I) -> Result<Option<&str>, Error>
    where
        I: RowIndex + fmt::Display,
    {
        let idx = match idx.__idx(&self.columns) {
            Some(idx) => idx,
            None => return Err(Error::column(idx.to_string())),
        };

        let buf = self.ranges[idx].clone().map(|r| &self.body.buffer()[r]);
        FromSql::from_sql_nullable(&Type::TEXT, buf).map_err(|e| Error::from_sql(e, idx))
    }
}
<|endoftext|>


<commit_before>use std::fmt;

use bytes::Bytes;
use js_sys::Uint8Array;
use http::{HeaderMap, StatusCode};
use url::Url;

#[cfg(feature = "json")]
use serde::de::DeserializeOwned;

/// A Response to a submitted `Request`.
pub struct Response {
    http: http::Response<web_sys::Response>,
    // Boxed to save space (11 words to 1 word), and it's not accessed
    // frequently internally.
    url: Box<Url>,
}

impl Response {
    pub(super) fn new(
        res: http::Response<web_sys::Response>,
        url: Url,
    ) -> Response {
        Response {
            http: res,
            url: Box::new(url),
        }
    }

    /// Get the `StatusCode` of this `Response`.
    #[inline]
    pub fn status(&self) -> StatusCode {
        self.http.status()
    }

    /// Get the `Headers` of this `Response`.
    #[inline]
    pub fn headers(&self) -> &HeaderMap {
        self.http.headers()
    }

    /// Get a mutable reference to the `Headers` of this `Response`.
    #[inline]
    pub fn headers_mut(&mut self) -> &mut HeaderMap {
        self.http.headers_mut()
    }

    /// Get the final `Url` of this `Response`.
    #[inline]
    pub fn url(&self) -> &Url {
        &self.url
    }

    /* It might not be possible to detect this in JS?
    /// Get the HTTP `Version` of this `Response`.
    #[inline]
    pub fn version(&self) -> Version {
        self.http.version()
    }
    */

    /// Try to deserialize the response body as JSON.
    #[cfg(feature = "json")]
    pub async fn json<T: DeserializeOwned>(self) -> crate::Result<T> {
        let full = self.bytes().await?;

        serde_json::from_slice(&full).map_err(crate::error::decode)
    }

    /// Get the response text.
    pub async fn text(self) -> crate::Result<String> {
        let p = self.http.body().text()
            .map_err(crate::error::wasm)
            .map_err(crate::error::decode)?;
        let js_val = super::promise::<wasm_bindgen::JsValue>(p)
            .await
            .map_err(crate::error::decode)?;
        if let Some(s) = js_val.as_string() {
            Ok(s)
        } else {
            Err(crate::error::decode("response.text isn't string"))
        }
    }

    /// Get the response as bytes
    pub async fn bytes(self) -> crate::Result<Bytes> {
        let p = self.http.body().array_buffer()
            .map_err(crate::error::wasm)
            .map_err(crate::error::decode)?;

        let buf_js = super::promise::<wasm_bindgen::JsValue>(p)
            .await
            .map_err(crate::error::decode)?;

        let buffer = Uint8Array::new(&buf_js);
        let mut bytes = vec![0; buffer.length() as usize];
        buffer.copy_to(&mut bytes);
        Ok(bytes.into())
    }

    // util methods

    /// Turn a response into an error if the server returned an error.
    pub fn error_for_status(self) -> crate::Result<Self> {
        let status = self.status();
        if status.is_client_error() || status.is_server_error() {
            Err(crate::error::status_code(*self.url, status))
        } else {
            Ok(self)
        }
    }

    /// Turn a reference to a response into an error if the server returned an error.
    pub fn error_for_status_ref(&self) -> crate::Result<&Self> {
        let status = self.status();
        if status.is_client_error() || status.is_server_error() {
            Err(crate::error::status_code(*self.url.clone(), status))
        } else {
            Ok(self)
        }
    }
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Response")
            //.field("url", self.url())
            .field("status", &self.status())
            .field("headers", self.headers())
            .finish()
    }
}
<commit_msg>Implement `Response::content_length` for `wasm32` (#863)<commit_after>use std::fmt;

use bytes::Bytes;
use js_sys::Uint8Array;
use http::{HeaderMap, StatusCode};
use url::Url;

#[cfg(feature = "json")]
use serde::de::DeserializeOwned;

/// A Response to a submitted `Request`.
pub struct Response {
    http: http::Response<web_sys::Response>,
    // Boxed to save space (11 words to 1 word), and it's not accessed
    // frequently internally.
    url: Box<Url>,
}

impl Response {
    pub(super) fn new(
        res: http::Response<web_sys::Response>,
        url: Url,
    ) -> Response {
        Response {
            http: res,
            url: Box::new(url),
        }
    }

    /// Get the `StatusCode` of this `Response`.
    #[inline]
    pub fn status(&self) -> StatusCode {
        self.http.status()
    }

    /// Get the `Headers` of this `Response`.
    #[inline]
    pub fn headers(&self) -> &HeaderMap {
        self.http.headers()
    }

    /// Get a mutable reference to the `Headers` of this `Response`.
    #[inline]
    pub fn headers_mut(&mut self) -> &mut HeaderMap {
        self.http.headers_mut()
    }

    /// Get the content-length of this response, if known.
    ///
    /// Reasons it may not be known:
    ///
    /// - The server didn't send a `content-length` header.
    /// - The response is compressed and automatically decoded (thus changing
    //   the actual decoded length).
    pub fn content_length(&self) -> Option<u64> {
        self.headers()
            .get(http::header::CONTENT_LENGTH)?
            .to_str()
            .ok()?
            .parse()
            .ok()
    }

    /// Get the final `Url` of this `Response`.
    #[inline]
    pub fn url(&self) -> &Url {
        &self.url
    }

    /* It might not be possible to detect this in JS?
    /// Get the HTTP `Version` of this `Response`.
    #[inline]
    pub fn version(&self) -> Version {
        self.http.version()
    }
    */

    /// Try to deserialize the response body as JSON.
    #[cfg(feature = "json")]
    pub async fn json<T: DeserializeOwned>(self) -> crate::Result<T> {
        let full = self.bytes().await?;

        serde_json::from_slice(&full).map_err(crate::error::decode)
    }

    /// Get the response text.
    pub async fn text(self) -> crate::Result<String> {
        let p = self.http.body().text()
            .map_err(crate::error::wasm)
            .map_err(crate::error::decode)?;
        let js_val = super::promise::<wasm_bindgen::JsValue>(p)
            .await
            .map_err(crate::error::decode)?;
        if let Some(s) = js_val.as_string() {
            Ok(s)
        } else {
            Err(crate::error::decode("response.text isn't string"))
        }
    }

    /// Get the response as bytes
    pub async fn bytes(self) -> crate::Result<Bytes> {
        let p = self.http.body().array_buffer()
            .map_err(crate::error::wasm)
            .map_err(crate::error::decode)?;

        let buf_js = super::promise::<wasm_bindgen::JsValue>(p)
            .await
            .map_err(crate::error::decode)?;

        let buffer = Uint8Array::new(&buf_js);
        let mut bytes = vec![0; buffer.length() as usize];
        buffer.copy_to(&mut bytes);
        Ok(bytes.into())
    }

    // util methods

    /// Turn a response into an error if the server returned an error.
    pub fn error_for_status(self) -> crate::Result<Self> {
        let status = self.status();
        if status.is_client_error() || status.is_server_error() {
            Err(crate::error::status_code(*self.url, status))
        } else {
            Ok(self)
        }
    }

    /// Turn a reference to a response into an error if the server returned an error.
    pub fn error_for_status_ref(&self) -> crate::Result<&Self> {
        let status = self.status();
        if status.is_client_error() || status.is_server_error() {
            Err(crate::error::status_code(*self.url.clone(), status))
        } else {
            Ok(self)
        }
    }
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Response")
            //.field("url", self.url())
            .field("status", &self.status())
            .field("headers", self.headers())
            .finish()
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 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.

use std::libc::{c_int, c_void, size_t};

// errors.h

pub type Error = c_int;
pub static OK: c_int = 0;
pub static REPROCESS: c_int = 1;
pub static ENCODINGCHANGE: c_int = 2;
pub static PAUSED: c_int = 3;
pub static NOMEM: c_int = 5;
pub static BADPARM: c_int = 6;
pub static INVALID: c_int = 7;
pub static FILENOTFOUND: c_int = 8;
pub static NEEDDATA: c_int = 9;
pub static BADENCODING: c_int = 10;
pub static UNKNOWN: c_int = 11;

#[cfg(target_os = "macos")]
#[nolink]
#[link_args="-lhubbub -lparserutils -liconv"]
extern { }

#[cfg(target_os = "linux")]
#[nolink]
#[link_args="-lhubbub -lparserutils"]
extern { }

#[nolink]
extern {
    pub fn hubbub_error_to_string(error: Error) -> *u8;
}

// parser.h

pub type Parser = c_void;

pub type ParserOptType = c_int;
pub static PARSER_TOKEN_HANDLER: c_int = 0;
pub static PARSER_ERROR_HANDLER: c_int = 1;
pub static PARSER_CONTENT_MODEL: c_int = 2;
pub static PARSER_TREE_HANDLER: c_int = 3;
pub static PARSER_DOCUMENT_NODE: c_int = 4;
pub static PARSER_ENABLE_SCRIPTING: c_int = 5;
pub static PARSER_PAUSE: c_int = 6;
pub static PARSER_ENABLE_STYLING: c_int = 7;

pub struct ParserOptParamsTokenHandler {
    handler: *u8,
    pw: *c_void
}

pub struct ParserOptParamsErrorHandler {
    handler: *u8,
    pw: *c_void
}

pub struct ParserOptParamsContentModel {
    content_model: ContentModel
}

pub mod parser {
    use std::libc::{c_void, size_t, c_char};
    use super::{Parser, Error, ParserOptType, CharsetSource};

    #[nolink]
    extern {
        pub fn hubbub_parser_create(enc: *u8,
                                    fix_enc: bool,
                                    alloc: *u8,
                                    pw: *c_void,
                                    parser: **Parser)
            -> Error;
        pub fn hubbub_parser_destroy(parser: *Parser) -> Error;
        pub fn hubbub_parser_setopt(parser: *Parser, opt_type: ParserOptType, params: *c_void)
            -> Error;
        pub fn hubbub_parser_parse_chunk(parser: *Parser, data: *u8, len: size_t) -> Error;
        pub fn hubbub_parser_insert_chunk(parser: *Parser, data: *u8, len: size_t) -> Error;
        pub fn hubbub_parser_completed(parser: *Parser) -> Error;
        pub fn hubbub_parser_read_charset(parser: *Parser, source: *CharsetSource) -> *c_char;
    }
}

// tree.h

pub struct TreeHandler {
    create_comment: *u8,
    create_doctype: *u8,
    create_element: *u8,
    create_text: *u8,
    ref_node: *u8,
    unref_node: *u8,
    append_child: *u8,
    insert_before: *u8,
    remove_child: *u8,
    clone_node: *u8,
    reparent_children: *u8,
    get_parent: *u8,
    has_children: *u8,
    form_associate: *u8,
    add_attributes: *u8,
    set_quirks_mode: *u8,
    encoding_change: *u8,
    complete_script: *u8,
    complete_style: *u8,
    ctx: *c_void,
}

// types.h

// Source of charset information, in order of importance.
// A client-dictated charset will override all others.
// A document-specified charset will override autodetection or the default.
pub type CharsetSource = c_int;
pub static CHARSET_UNKNOWN: c_int = 0;
pub static CHARSET_TENTATIVE: c_int = 1;
pub static CHARSET_CONFIDENT: c_int = 2;

// Content model flag
pub type ContentModel = c_int;
pub static CONTENT_MODEL_PCDATA: c_int = 0;
pub static CONTENT_MODEL_RCDATA: c_int = 1;
pub static CONTENT_MODEL_CDATA: c_int = 2;
pub static CONTENT_MODEL_PLAINTEXT: c_int = 3;

// Quirks mode flag
pub type QuirksMode = c_int;
pub static QUIRKS_MODE_NONE: c_int = 0;
pub static QUIRKS_MODE_LIMITED: c_int = 1;
pub static QUIRKS_MODE_FULL: c_int = 2;

pub type TokenType = c_int;
pub static TOKEN_DOCTYPE: c_int = 0;
pub static TOKEN_START_TAG: c_int = 1;
pub static TOKEN_END_TAG: c_int = 2;
pub static TOKEN_COMMENT: c_int = 3;
pub static TOKEN_CHARACTER: c_int = 4;
pub static TOKEN_EOF: c_int = 5;

pub type NS = c_int;
pub static NS_NULL: c_int = 0;
pub static NS_HTML: c_int = 1;
pub static NS_MATHML: c_int = 2;
pub static NS_SVG: c_int = 3;
pub static NS_XLINK: c_int = 4;
pub static NS_XML: c_int = 5;
pub static NS_XMLNS: c_int = 6;

pub struct String {
    ptr: *u8,
    len: size_t
}

pub struct Attribute {
    ns: NS,
    name: String,
    value: String,
}

pub struct Doctype {
    name: String,
    public_missing: bool,
    public_id: String,
    system_missing: bool,
    system_id: String,
    force_quirks: bool,
}

pub struct Tag {
    ns: NS,
    name: String,
    n_attributes: u32,
    attributes: *Attribute,
    self_closing: bool,
}

// Token data
pub struct Token {
    token_type: TokenType,
    data: u8,   // union: one of Doctype, Tag, Comment (string), or Character (string)
}

<commit_msg>support android build<commit_after>// Copyright 2013 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.

use std::libc::{c_int, c_void, size_t};

// errors.h

pub type Error = c_int;
pub static OK: c_int = 0;
pub static REPROCESS: c_int = 1;
pub static ENCODINGCHANGE: c_int = 2;
pub static PAUSED: c_int = 3;
pub static NOMEM: c_int = 5;
pub static BADPARM: c_int = 6;
pub static INVALID: c_int = 7;
pub static FILENOTFOUND: c_int = 8;
pub static NEEDDATA: c_int = 9;
pub static BADENCODING: c_int = 10;
pub static UNKNOWN: c_int = 11;

#[cfg(target_os = "macos")]
#[nolink]
#[link_args="-lhubbub -lparserutils -liconv"]
extern { }

#[cfg(target_os = "linux")]
#[cfg(target_os = "android")]
#[nolink]
#[link_args="-lhubbub -lparserutils"]
extern { }

#[nolink]
extern {
    pub fn hubbub_error_to_string(error: Error) -> *u8;
}

// parser.h

pub type Parser = c_void;

pub type ParserOptType = c_int;
pub static PARSER_TOKEN_HANDLER: c_int = 0;
pub static PARSER_ERROR_HANDLER: c_int = 1;
pub static PARSER_CONTENT_MODEL: c_int = 2;
pub static PARSER_TREE_HANDLER: c_int = 3;
pub static PARSER_DOCUMENT_NODE: c_int = 4;
pub static PARSER_ENABLE_SCRIPTING: c_int = 5;
pub static PARSER_PAUSE: c_int = 6;
pub static PARSER_ENABLE_STYLING: c_int = 7;

pub struct ParserOptParamsTokenHandler {
    handler: *u8,
    pw: *c_void
}

pub struct ParserOptParamsErrorHandler {
    handler: *u8,
    pw: *c_void
}

pub struct ParserOptParamsContentModel {
    content_model: ContentModel
}

pub mod parser {
    use std::libc::{c_void, size_t, c_char};
    use super::{Parser, Error, ParserOptType, CharsetSource};

    #[nolink]
    extern {
        pub fn hubbub_parser_create(enc: *u8,
                                    fix_enc: bool,
                                    alloc: *u8,
                                    pw: *c_void,
                                    parser: **Parser)
            -> Error;
        pub fn hubbub_parser_destroy(parser: *Parser) -> Error;
        pub fn hubbub_parser_setopt(parser: *Parser, opt_type: ParserOptType, params: *c_void)
            -> Error;
        pub fn hubbub_parser_parse_chunk(parser: *Parser, data: *u8, len: size_t) -> Error;
        pub fn hubbub_parser_insert_chunk(parser: *Parser, data: *u8, len: size_t) -> Error;
        pub fn hubbub_parser_completed(parser: *Parser) -> Error;
        pub fn hubbub_parser_read_charset(parser: *Parser, source: *CharsetSource) -> *c_char;
    }
}

// tree.h

pub struct TreeHandler {
    create_comment: *u8,
    create_doctype: *u8,
    create_element: *u8,
    create_text: *u8,
    ref_node: *u8,
    unref_node: *u8,
    append_child: *u8,
    insert_before: *u8,
    remove_child: *u8,
    clone_node: *u8,
    reparent_children: *u8,
    get_parent: *u8,
    has_children: *u8,
    form_associate: *u8,
    add_attributes: *u8,
    set_quirks_mode: *u8,
    encoding_change: *u8,
    complete_script: *u8,
    complete_style: *u8,
    ctx: *c_void,
}

// types.h

// Source of charset information, in order of importance.
// A client-dictated charset will override all others.
// A document-specified charset will override autodetection or the default.
pub type CharsetSource = c_int;
pub static CHARSET_UNKNOWN: c_int = 0;
pub static CHARSET_TENTATIVE: c_int = 1;
pub static CHARSET_CONFIDENT: c_int = 2;

// Content model flag
pub type ContentModel = c_int;
pub static CONTENT_MODEL_PCDATA: c_int = 0;
pub static CONTENT_MODEL_RCDATA: c_int = 1;
pub static CONTENT_MODEL_CDATA: c_int = 2;
pub static CONTENT_MODEL_PLAINTEXT: c_int = 3;

// Quirks mode flag
pub type QuirksMode = c_int;
pub static QUIRKS_MODE_NONE: c_int = 0;
pub static QUIRKS_MODE_LIMITED: c_int = 1;
pub static QUIRKS_MODE_FULL: c_int = 2;

pub type TokenType = c_int;
pub static TOKEN_DOCTYPE: c_int = 0;
pub static TOKEN_START_TAG: c_int = 1;
pub static TOKEN_END_TAG: c_int = 2;
pub static TOKEN_COMMENT: c_int = 3;
pub static TOKEN_CHARACTER: c_int = 4;
pub static TOKEN_EOF: c_int = 5;

pub type NS = c_int;
pub static NS_NULL: c_int = 0;
pub static NS_HTML: c_int = 1;
pub static NS_MATHML: c_int = 2;
pub static NS_SVG: c_int = 3;
pub static NS_XLINK: c_int = 4;
pub static NS_XML: c_int = 5;
pub static NS_XMLNS: c_int = 6;

pub struct String {
    ptr: *u8,
    len: size_t
}

pub struct Attribute {
    ns: NS,
    name: String,
    value: String,
}

pub struct Doctype {
    name: String,
    public_missing: bool,
    public_id: String,
    system_missing: bool,
    system_id: String,
    force_quirks: bool,
}

pub struct Tag {
    ns: NS,
    name: String,
    n_attributes: u32,
    attributes: *Attribute,
    self_closing: bool,
}

// Token data
pub struct Token {
    token_type: TokenType,
    data: u8,   // union: one of Doctype, Tag, Comment (string), or Character (string)
}

<|endoftext|>


<commit_before><commit_msg>Improve the naming of variables, add comments<commit_after><|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// GPIO direction.
pub enum GPIODirection {
  In,
  Out,
}

/// Logic levels.
#[deriving(PartialEq)]
pub enum GPIOLevel {
  Low,
  High,
}

/// General Purpose I/O
pub trait GPIO {
  /// Set to logic high
  fn set_high(&self);

  /// Set to logic low
  fn set_low(&self);

  /// Read current logic level
  fn level(&self) -> GPIOLevel;

  /// Set directio to `Low` or `High`
  fn set_direction(&self, new_mode: GPIODirection);
}
<commit_msg>Document each variant of GPIOLevel and GPIODirection<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// GPIO direction.
pub enum GPIODirection {
  /// Input mode
  In,
  /// Output mode
  Out,
}

/// Logic levels.
#[deriving(PartialEq)]
pub enum GPIOLevel {
  /// Logic low
  Low,
  /// Logic high
  High,
}

/// General Purpose I/O
pub trait GPIO {
  /// Set to logic high
  fn set_high(&self);

  /// Set to logic low
  fn set_low(&self);

  /// Read current logic level
  fn level(&self) -> GPIOLevel;

  /// Set direction mode to `In` or `Out`,
  /// for reading or writing respectively
  fn set_direction(&self, new_mode: GPIODirection);
}
<|endoftext|>


<commit_before>use std::ffi::CStr;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::os::raw::{c_char, c_int};
use std::slice;
use std::str;

use c_ares_sys;
use c_types;
use itertools::Itertools;

use types::AddressFamily;
use utils::address_family;

#[derive(Debug)]
pub struct HostentOwned {
    inner: *mut c_types::hostent,
    phantom: PhantomData<c_types::hostent>,
}

impl HostentOwned {
    pub fn new(hostent: *mut c_types::hostent) -> HostentOwned {
        HostentOwned {
            inner: hostent,
            phantom: PhantomData,
        }
    }
}

impl Drop for HostentOwned {
    fn drop(&mut self) {
        unsafe {
            c_ares_sys::ares_free_hostent(self.inner);
        }
    }
}

#[derive(Clone, Copy)]
pub struct HostentBorrowed<'a> {
    inner: &'a c_types::hostent,
}

impl<'a> HostentBorrowed<'a> {
    pub fn new(hostent: &'a c_types::hostent) -> HostentBorrowed<'a> {
        HostentBorrowed { inner: hostent }
    }
}

pub trait HasHostent {
    fn hostent(&self) -> &c_types::hostent;

    fn hostname(&self) -> &str {
        unsafe {
            let c_str = CStr::from_ptr(self.hostent().h_name);
            str::from_utf8_unchecked(c_str.to_bytes())
        }
    }

    fn addresses(&self) -> HostAddressResultsIter {
        // h_addrtype is `c_short` on windows, `c_int` on unix.  Tell clippy to
        // allow the identity conversion in the latter case.
        #[cfg_attr(feature = "cargo-clippy", allow(identity_conversion))]
        let addrtype = c_int::from(self.hostent().h_addrtype);
        HostAddressResultsIter {
            family: address_family(addrtype),
            next: unsafe { &*(self.hostent().h_addr_list as *const _) },
        }
    }

    fn aliases(&self) -> HostAliasResultsIter {
        HostAliasResultsIter {
            next: unsafe { &*(self.hostent().h_aliases as *const _) },
        }
    }

    fn display(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "Hostname: {}, ", self.hostname())?;
        let addresses = self.addresses().format(", ");
        write!(fmt, "Addresses: [{}]", addresses)?;
        let aliases = self.aliases().format(", ");
        write!(fmt, "Aliases: [{}]", aliases)
    }
}

impl HasHostent for HostentOwned {
    fn hostent(&self) -> &c_types::hostent {
        unsafe { &*self.inner }
    }
}

impl<'a> HasHostent for HostentBorrowed<'a> {
    fn hostent(&self) -> &c_types::hostent {
        self.inner
    }
}

unsafe impl Send for HostentOwned {}
unsafe impl Sync for HostentOwned {}

unsafe impl<'a> Send for HostentBorrowed<'a> {}
unsafe impl<'a> Sync for HostentBorrowed<'a> {}

/// Iterator of `IpAddr`s.
#[derive(Clone, Copy, Debug)]
pub struct HostAddressResultsIter<'a> {
    family: Option<AddressFamily>,
    next: &'a *const c_char,
}

// Get an IpAddr from a family and an array of bytes, as found in a `hostent`.
unsafe fn ip_address_from_bytes(family: AddressFamily, h_addr: *const u8) -> Option<IpAddr> {
    match family {
        AddressFamily::INET => {
            let source = slice::from_raw_parts(h_addr, 4);
            let mut bytes: [u8; 4] = mem::uninitialized();
            bytes.copy_from_slice(source);
            let ipv4 = Ipv4Addr::from(bytes);
            Some(IpAddr::V4(ipv4))
        }
        AddressFamily::INET6 => {
            let source = slice::from_raw_parts(h_addr, 16);
            let mut bytes: [u8; 16] = mem::uninitialized();
            bytes.copy_from_slice(source);
            let ipv6 = Ipv6Addr::from(bytes);
            Some(IpAddr::V6(ipv6))
        }
        _ => None,
    }
}

impl<'a> Iterator for HostAddressResultsIter<'a> {
    type Item = IpAddr;
    fn next(&mut self) -> Option<Self::Item> {
        let h_addr = *self.next;
        if h_addr.is_null() {
            None
        } else {
            unsafe {
                self.next = &*(self.next as *const _).offset(1);
                self.family
                    .and_then(|family| ip_address_from_bytes(family, h_addr as *const u8))
            }
        }
    }
}

unsafe impl<'a> Send for HostAddressResultsIter<'a> {}
unsafe impl<'a> Sync for HostAddressResultsIter<'a> {}

/// Iterator of `&'a str`s.
#[derive(Clone, Copy, Debug)]
pub struct HostAliasResultsIter<'a> {
    next: &'a *const c_char,
}

impl<'a> Iterator for HostAliasResultsIter<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<Self::Item> {
        let h_alias = *self.next;
        if h_alias.is_null() {
            None
        } else {
            unsafe {
                self.next = &*(self.next as *const _).offset(1);
                let c_str = CStr::from_ptr(h_alias);
                Some(str::from_utf8_unchecked(c_str.to_bytes()))
            }
        }
    }
}

unsafe impl<'a> Send for HostAliasResultsIter<'a> {}
unsafe impl<'a> Sync for HostAliasResultsIter<'a> {}
<commit_msg>compiler inference got less helpful<commit_after>use std::ffi::CStr;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::os::raw::{c_char, c_int};
use std::slice;
use std::str;

use c_ares_sys;
use c_types;
use itertools::Itertools;

use types::AddressFamily;
use utils::address_family;

#[derive(Debug)]
pub struct HostentOwned {
    inner: *mut c_types::hostent,
    phantom: PhantomData<c_types::hostent>,
}

impl HostentOwned {
    pub fn new(hostent: *mut c_types::hostent) -> HostentOwned {
        HostentOwned {
            inner: hostent,
            phantom: PhantomData,
        }
    }
}

impl Drop for HostentOwned {
    fn drop(&mut self) {
        unsafe {
            c_ares_sys::ares_free_hostent(self.inner);
        }
    }
}

#[derive(Clone, Copy)]
pub struct HostentBorrowed<'a> {
    inner: &'a c_types::hostent,
}

impl<'a> HostentBorrowed<'a> {
    pub fn new(hostent: &'a c_types::hostent) -> HostentBorrowed<'a> {
        HostentBorrowed { inner: hostent }
    }
}

pub trait HasHostent {
    fn hostent(&self) -> &c_types::hostent;

    fn hostname(&self) -> &str {
        unsafe {
            let c_str = CStr::from_ptr(self.hostent().h_name);
            str::from_utf8_unchecked(c_str.to_bytes())
        }
    }

    fn addresses(&self) -> HostAddressResultsIter {
        // h_addrtype is `c_short` on windows, `c_int` on unix.  Tell clippy to
        // allow the identity conversion in the latter case.
        #[cfg_attr(feature = "cargo-clippy", allow(identity_conversion))]
        let addrtype = c_int::from(self.hostent().h_addrtype);
        HostAddressResultsIter {
            family: address_family(addrtype),
            next: unsafe { &*(self.hostent().h_addr_list as *const _) },
        }
    }

    fn aliases(&self) -> HostAliasResultsIter {
        HostAliasResultsIter {
            next: unsafe { &*(self.hostent().h_aliases as *const _) },
        }
    }

    fn display(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "Hostname: {}, ", self.hostname())?;
        let addresses = self.addresses().format(", ");
        write!(fmt, "Addresses: [{}]", addresses)?;
        let aliases = self.aliases().format(", ");
        write!(fmt, "Aliases: [{}]", aliases)
    }
}

impl HasHostent for HostentOwned {
    fn hostent(&self) -> &c_types::hostent {
        unsafe { &*self.inner }
    }
}

impl<'a> HasHostent for HostentBorrowed<'a> {
    fn hostent(&self) -> &c_types::hostent {
        self.inner
    }
}

unsafe impl Send for HostentOwned {}
unsafe impl Sync for HostentOwned {}

unsafe impl<'a> Send for HostentBorrowed<'a> {}
unsafe impl<'a> Sync for HostentBorrowed<'a> {}

/// Iterator of `IpAddr`s.
#[derive(Clone, Copy, Debug)]
pub struct HostAddressResultsIter<'a> {
    family: Option<AddressFamily>,
    next: &'a *const c_char,
}

// Get an IpAddr from a family and an array of bytes, as found in a `hostent`.
unsafe fn ip_address_from_bytes(family: AddressFamily, h_addr: *const u8) -> Option<IpAddr> {
    match family {
        AddressFamily::INET => {
            let source = slice::from_raw_parts(h_addr, 4);
            let mut bytes: [u8; 4] = mem::uninitialized();
            bytes.copy_from_slice(source);
            let ipv4 = Ipv4Addr::from(bytes);
            Some(IpAddr::V4(ipv4))
        }
        AddressFamily::INET6 => {
            let source = slice::from_raw_parts(h_addr, 16);
            let mut bytes: [u8; 16] = mem::uninitialized();
            bytes.copy_from_slice(source);
            let ipv6 = Ipv6Addr::from(bytes);
            Some(IpAddr::V6(ipv6))
        }
        _ => None,
    }
}

impl<'a> Iterator for HostAddressResultsIter<'a> {
    type Item = IpAddr;
    fn next(&mut self) -> Option<Self::Item> {
        let h_addr = *self.next;
        if h_addr.is_null() {
            None
        } else {
            unsafe {
                self.next = &*(self.next as *const *const c_char).offset(1);
                self.family
                    .and_then(|family| ip_address_from_bytes(family, h_addr as *const u8))
            }
        }
    }
}

unsafe impl<'a> Send for HostAddressResultsIter<'a> {}
unsafe impl<'a> Sync for HostAddressResultsIter<'a> {}

/// Iterator of `&'a str`s.
#[derive(Clone, Copy, Debug)]
pub struct HostAliasResultsIter<'a> {
    next: &'a *const c_char,
}

impl<'a> Iterator for HostAliasResultsIter<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<Self::Item> {
        let h_alias = *self.next;
        if h_alias.is_null() {
            None
        } else {
            unsafe {
                self.next = &*(self.next as *const *const c_char).offset(1);
                let c_str = CStr::from_ptr(h_alias);
                Some(str::from_utf8_unchecked(c_str.to_bytes()))
            }
        }
    }
}

unsafe impl<'a> Send for HostAliasResultsIter<'a> {}
unsafe impl<'a> Sync for HostAliasResultsIter<'a> {}
<|endoftext|>


<commit_before>use tokio::reactor::Handle;
use {RamStore, sexp_proto, TailService, ServerService};
use service::{Service, NewService};
use tokio::io::FramedIo;
use futures::{Poll, Async};
use proto::pipeline::Frame;

use std::net::SocketAddr;
use std::io;
use std::sync::{Mutex, Arc};

use std::collections::BTreeMap;
use void::Void;
use std::marker::PhantomData;

pub trait Host : Sized {
    type Addr;

    fn build_server(&mut self,
                    service: CoreService,
                    handle: &Handle,
                    head_addr: Self::Addr,
                    tail_addr: Self::Addr)
                    -> Result<HostConfig<Self::Addr>, io::Error>;
}

#[derive(Debug, Clone)]
pub struct HostConfig<A> {
    pub head: A,
    pub tail: A,
}

#[derive(Debug)]
pub struct CoreService {
    head: ServerService<RamStore>,
    tail: TailService<RamStore>,
}

pub struct SexpHost;

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BovineAddr(pub usize);

pub struct InnerBovine<S: Service> {
    new_services: BTreeMap<BovineAddr,
                           Box<NewService<Request = S::Request,
                                          Response = S::Response,
                                          Item = S,
                                          Error = S::Error>>>,
}

pub struct SphericalBovine<S: Service> {
    inner: Arc<Mutex<InnerBovine<S>>>,
}

#[derive(Debug)]
pub struct BovinePort<Req, Resp>(PhantomData<(Req, Resp)>);

impl CoreService {
    pub fn new() -> Self {
        let store = RamStore::new();

        CoreService {
            head: ServerService::new(store.clone()),
            tail: TailService::new(store.clone()),
        }
    }
}

impl Host for SexpHost {
    type Addr = SocketAddr;

    fn build_server(&mut self,
                    service: CoreService,
                    handle: &Handle,
                    head_addr: Self::Addr,
                    tail_addr: Self::Addr)
                    -> Result<HostConfig<Self::Addr>, io::Error> {
        let CoreService { head, tail } = service;
        let head_host = try!(sexp_proto::server::serve(handle, head_addr, head));
        let tail_host = try!(sexp_proto::server::serve(handle, tail_addr, tail));

        Ok(HostConfig {
            head: head_host.local_addr().clone(),
            tail: tail_host.local_addr().clone(),
        })
    }
}

impl<S: Service> SphericalBovine<S> {
    pub fn new() -> Self {
        let inner = InnerBovine { new_services: BTreeMap::new() };
        SphericalBovine { inner: Arc::new(Mutex::new(inner)) }
    }

    fn serve<N>(&self, service: N, handle: &Handle) -> Result<BovineAddr, io::Error>
        where N: NewService<Request = S::Request,
                            Response = S::Response,
                            Item = S,
                            Error = S::Error> + 'static
    {
        // try!(pipeline::Server::new(service, transport))
        let mut inner = self.inner.lock().expect("lock");
        let n = inner.new_services.len();
        let addr = BovineAddr(n);
        assert!(inner.new_services.get(&addr).is_none());
        inner.new_services.insert(addr, Box::new(service));
        Ok(addr)
    }

    fn connect_transport(&self,
                         addr: BovineAddr)
                         -> Result<BovinePort<S::Request, S::Response>, io::Error> {
        unimplemented!()
        // let cloned_ref = self.inner.clone();
        //
        // let mut inner = self.inner.lock().expect("lock");
        // let service = try!(self.new_services.new_service());
        //
        // let n = inner.connections.len();
        // assert!(inner.new_services.get(&n).is_none());
        //
        // inner.new_services.insert(n, Box::new(service));
        // Ok(BovinePort(cloned_ref)
        //
    }
}

impl<S: Service> Host for SphericalBovine<S> {
    type Addr = BovineAddr;

    fn build_server(&mut self,
                    service: CoreService,
                    handle: &Handle,
                    head_addr: Self::Addr,
                    tail_addr: Self::Addr)
                    -> Result<HostConfig<Self::Addr>, io::Error> {
        let CoreService { head, tail } = service;
        let head_host = unimplemented!();
        let tail_host = unimplemented!();

        Ok(HostConfig {
            head: head_addr,
            tail: tail_addr,
        })
    }
}

impl<Req, Resp> FramedIo for BovinePort<Req, Resp> {
    type In = Frame<Req, Void, io::Error>;
    type Out = Frame<Resp, Void, io::Error>;

    fn poll_read(&mut self) -> Async<()> {
        unimplemented!()
    }
    fn read(&mut self) -> Poll<Self::Out, io::Error> {
        unimplemented!()
    }
    fn poll_write(&mut self) -> Async<()> {
        unimplemented!()
    }
    fn write(&mut self, msg: Self::In) -> Poll<(), io::Error> {
        unimplemented!()
    }
    fn flush(&mut self) -> Poll<(), io::Error> {
        unimplemented!()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use tokio::reactor::{Core, Handle};
    use proto::{self, pipeline, Message};
    use service::{simple_service, Service};
    use void::Void;
    use futures;
    use std::io;

    struct MyService;

    #[test]
    #[ignore]
    fn should_go_moo() {
        let mut core = Core::new().expect("Core::new");
        let service = simple_service(|n: usize| -> Result<usize, ()> { Ok(n + 1) });

        let net = SphericalBovine::new();
        let addr = net.serve(service, &core.handle()).expect("serve");

        let client : proto::Client<usize, usize, futures::stream::Empty<Void, io::Error>, io::Error> =
            pipeline::connect(|| net.connect_transport(addr.clone()), &core.handle());

        let f = client.call(Message::WithoutBody(42));

        let ret = core.run(f).expect("run");
        assert_eq!(ret, 42);
    }
}
<commit_msg>It is probably fine to have one "network" per service for now.<commit_after>use tokio::reactor::Handle;
use {RamStore, sexp_proto, TailService, ServerService};
use service::{Service, NewService};
use tokio::io::FramedIo;
use futures::{Poll, Async};
use proto::pipeline::Frame;

use std::net::SocketAddr;
use std::io;
use std::sync::{Mutex, Arc};

use std::collections::BTreeMap;
use void::Void;
use std::marker::PhantomData;

pub trait Host : Sized {
    type Addr;

    fn build_server(&mut self,
                    service: CoreService,
                    handle: &Handle,
                    head_addr: Self::Addr,
                    tail_addr: Self::Addr)
                    -> Result<HostConfig<Self::Addr>, io::Error>;
}

#[derive(Debug, Clone)]
pub struct HostConfig<A> {
    pub head: A,
    pub tail: A,
}

#[derive(Debug)]
pub struct CoreService {
    head: ServerService<RamStore>,
    tail: TailService<RamStore>,
}

pub struct SexpHost;

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BovineAddr(pub usize);

pub struct InnerBovine<S: Service> {
    new_service: Box<NewService<Request = S::Request,
                                Response = S::Response,
                                Item = S,
                                Error = S::Error>>,
}

pub struct SphericalBovine<S: Service> {
    inner: Arc<Mutex<InnerBovine<S>>>,
}

#[derive(Debug)]
pub struct BovinePort<Req, Resp>(PhantomData<(Req, Resp)>);

impl CoreService {
    pub fn new() -> Self {
        let store = RamStore::new();

        CoreService {
            head: ServerService::new(store.clone()),
            tail: TailService::new(store.clone()),
        }
    }
}

impl Host for SexpHost {
    type Addr = SocketAddr;

    fn build_server(&mut self,
                    service: CoreService,
                    handle: &Handle,
                    head_addr: Self::Addr,
                    tail_addr: Self::Addr)
                    -> Result<HostConfig<Self::Addr>, io::Error> {
        let CoreService { head, tail } = service;
        let head_host = try!(sexp_proto::server::serve(handle, head_addr, head));
        let tail_host = try!(sexp_proto::server::serve(handle, tail_addr, tail));

        Ok(HostConfig {
            head: head_host.local_addr().clone(),
            tail: tail_host.local_addr().clone(),
        })
    }
}

impl<S: Service> SphericalBovine<S> {
    pub fn new<N>(service: N) -> Self
        where N: NewService<Request = S::Request,
                            Response = S::Response,
                            Item = S,
                            Error = S::Error> + 'static
    {
        let inner = InnerBovine { new_service: Box::new(service) };
        SphericalBovine { inner: Arc::new(Mutex::new(inner)) }
    }

    fn connect_transport(&self) -> Result<BovinePort<S::Request, S::Response>, io::Error> {
        unimplemented!()
        // let cloned_ref = self.inner.clone();
        //
        // let mut inner = self.inner.lock().expect("lock");
        // let service = try!(self.new_services.new_service());
        //
        // let n = inner.connections.len();
        // assert!(inner.new_services.get(&n).is_none());
        //
        // inner.new_services.insert(n, Box::new(service));
        // Ok(BovinePort(cloned_ref)
        //
    }
}

impl<S: Service> Host for SphericalBovine<S> {
    type Addr = BovineAddr;

    fn build_server(&mut self,
                    service: CoreService,
                    handle: &Handle,
                    head_addr: Self::Addr,
                    tail_addr: Self::Addr)
                    -> Result<HostConfig<Self::Addr>, io::Error> {
        let CoreService { head, tail } = service;
        let head_host = unimplemented!();
        let tail_host = unimplemented!();

        Ok(HostConfig {
            head: head_addr,
            tail: tail_addr,
        })
    }
}

impl<Req, Resp> FramedIo for BovinePort<Req, Resp> {
    type In = Frame<Req, Void, io::Error>;
    type Out = Frame<Resp, Void, io::Error>;

    fn poll_read(&mut self) -> Async<()> {
        unimplemented!()
    }
    fn read(&mut self) -> Poll<Self::Out, io::Error> {
        unimplemented!()
    }
    fn poll_write(&mut self) -> Async<()> {
        unimplemented!()
    }
    fn write(&mut self, msg: Self::In) -> Poll<(), io::Error> {
        unimplemented!()
    }
    fn flush(&mut self) -> Poll<(), io::Error> {
        unimplemented!()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use tokio::reactor::{Core, Handle};
    use proto::{self, pipeline, Message};
    use service::{simple_service, Service};
    use void::Void;
    use futures;
    use std::io;

    struct MyService;

    #[test]
    #[ignore]
    fn should_go_moo() {
        let mut core = Core::new().expect("Core::new");
        let service = simple_service(|n: usize| -> Result<usize, ()> { Ok(n + 1) });

        let net = SphericalBovine::new(service);

        let client : proto::Client<usize, usize, futures::stream::Empty<Void, io::Error>, io::Error> =
            pipeline::connect(|| net.connect_transport(), &core.handle());

        let f = client.call(Message::WithoutBody(42));

        let ret = core.run(f).expect("run");
        assert_eq!(ret, 42);
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
//
// 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

#[rustversion::attr(any(not(stable), before(1.54), since(1.55)), ignore)]
#[test]
fn ui() {
    let t = trybuild::TestCases::new();
    t.compile_fail("tests/derive_ui/*.rs");
}
<commit_msg>fix: Limit when ui tests run<commit_after>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
//
// 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

#[cfg(feature = "derive")]
#[rustversion::attr(any(not(stable), before(1.54), since(1.55)), ignore)]
#[test]
fn ui() {
    let t = trybuild::TestCases::new();
    t.compile_fail("tests/derive_ui/*.rs");
}
<|endoftext|>


<commit_before>use std::{env, ffi::OsString, fmt, net::SocketAddr, path::PathBuf, str::FromStr};

use anyhow::bail;
use clap::{Arg, ArgGroup, Command as ClapCommand};
use sccache::{config, dist::ServerId};
use syslog::Facility;

use crate::cmdline::{AuthSubcommand, Command};

#[derive(Debug)]
struct TokenBits(usize);

impl TokenBits {
    fn as_bytes(&self) -> usize {
        self.0 / 8
    }

    fn default_str() -> &'static str {
        // Size based on https://briansmith.org/rustdoc/ring/hmac/fn.recommended_key_len.html
        "256"
    }
}

impl Default for TokenBits {
    fn default() -> Self {
        Self::default_str().parse().expect("Default isn't valid")
    }
}

impl FromStr for TokenBits {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let bits: usize = s.parse()?;
        if bits % 8 != 0 || bits < 64 || bits > 4_096 {
            bail!("Number of bits must be divisible by 8, greater than 64 and less than 4096")
        }

        Ok(Self(bits))
    }
}

impl fmt::Display for TokenBits {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

#[derive(Clone, Copy)]
enum LogLevel {
    Error,
    Warn,
    Info,
    Debug,
    Trace,
}

impl LogLevel {
    fn as_str(&self) -> &'static str {
        match self {
            Self::Error => "error",
            Self::Warn => "warn",
            Self::Info => "info",
            Self::Debug => "debug",
            Self::Trace => "trace",
        }
    }

    fn values() -> &'static [Self] {
        &[
            Self::Error,
            Self::Warn,
            Self::Info,
            Self::Debug,
            Self::Trace,
        ]
    }
}

impl FromStr for LogLevel {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let variant = match s {
            "error" => Self::Error,
            "warn" => Self::Warn,
            "info" => Self::Info,
            "debug" => Self::Debug,
            "trace" => Self::Trace,
            _ => bail!("Unknown log level: {:?}", s),
        };

        Ok(variant)
    }
}

impl From<LogLevel> for log::LevelFilter {
    fn from(log_level: LogLevel) -> Self {
        match log_level {
            LogLevel::Error => Self::Error,
            LogLevel::Warn => Self::Warn,
            LogLevel::Info => Self::Info,
            LogLevel::Debug => Self::Debug,
            LogLevel::Trace => Self::Trace,
        }
    }
}

/// Parse the commandline into a `Command` to execute.
pub fn parse() -> Command {
    match try_parse_from(env::args()) {
        Ok(cmd) => cmd,
        Err(e) => match e.downcast::<clap::error::Error>() {
            Ok(clap_err) => clap_err.exit(),
            Err(some_other_err) => {
                println!("sccache-dist: {some_other_err}");
                for source_err in some_other_err.chain().skip(1) {
                    println!("sccache-dist: caused by: {source_err}");
                }
                std::process::exit(1);
            }
        },
    }
}

fn flag_infer_long(name: &'static str) -> Arg<'static> {
    Arg::new(name).long(name)
}

fn get_clap_command() -> ClapCommand<'static> {
    let syslog = flag_infer_long("syslog")
        .help("Log to the syslog with LEVEL")
        .value_name("LEVEL")
        .possible_values(LogLevel::values().iter().map(LogLevel::as_str));
    let config_with_help_message =
        |help: &'static str| flag_infer_long("config").help(help).value_name("PATH");

    ClapCommand::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .subcommand_required(true)
        .subcommand(
            ClapCommand::new("auth")
                .subcommand_required(true)
                .subcommand(ClapCommand::new("generate-jwt-hs256-key"))
                .subcommand(
                    ClapCommand::new("generate-jwt-hs256-server-token")
                        .args(&[
                            flag_infer_long("server")
                                .help("Generate a key for the specified server")
                                .value_name("SERVER_ADDR")
                                .required(true),
                            flag_infer_long("secret-key")
                                .help("Use specified key to create the token")
                                .value_name("KEY"),
                            config_with_help_message(
                                "Use the key from the scheduler config file at PATH",
                            ),
                        ])
                        .group(
                            ArgGroup::new("key_source_mutual_exclusion")
                                .args(&["config", "secret-key"])
                                .required(true),
                        ),
                )
                .subcommand(
                    ClapCommand::new("generate-shared-token").arg(
                        flag_infer_long("bits")
                            .help("Use the specified number of bits of randomness")
                            .value_name("BITS")
                            .default_value(TokenBits::default_str()),
                    ),
                ),
        )
        .subcommand(ClapCommand::new("scheduler").args(&[
            config_with_help_message("Use the scheduler config file at PATH").required(true),
            syslog.clone(),
        ]))
        .subcommand(ClapCommand::new("server").args(&[
            config_with_help_message("Use the server config file at PATH").required(true),
            syslog,
        ]))
}

fn check_init_syslog(name: &str, log_level: LogLevel) {
    let level = log::LevelFilter::from(log_level);
    drop(syslog::init(Facility::LOG_DAEMON, level, Some(name)));
}

fn try_parse_from(
    args: impl IntoIterator<Item = impl Into<OsString> + Clone>,
) -> anyhow::Result<Command> {
    let matches = get_clap_command().try_get_matches_from(args)?;

    Ok(match matches.subcommand() {
        Some(("auth", matches)) => Command::Auth(match matches.subcommand() {
            Some(("generate-jwt-hs256-key", _)) => AuthSubcommand::Base64 {
                num_bytes: TokenBits::default().as_bytes(),
            },
            Some(("generate-jwt-hs256-server-token", matches)) => {
                let server_addr: SocketAddr = matches.value_of_t("server")?;
                let server_id = ServerId::new(server_addr);

                let secret_key = if matches.is_present("config") {
                    let config_path: PathBuf = matches.value_of_t("config")?;
                    if let Some(config) = config::scheduler::from_path(&config_path)? {
                        match config.server_auth {
                            config::scheduler::ServerAuth::JwtHS256 { secret_key } => secret_key,
                            config::scheduler::ServerAuth::Insecure
                            | config::scheduler::ServerAuth::Token { .. } => {
                                bail!("Scheduler not configured with JWT HS256")
                            }
                        }
                    } else {
                        bail!("Could not load config")
                    }
                } else {
                    matches.value_of_t("secret-key")?
                };

                AuthSubcommand::JwtHS256ServerToken {
                    secret_key,
                    server_id,
                }
            }
            Some(("generate-shared-token", matches)) => {
                let token_bits: TokenBits = matches.value_of_t("bits")?;
                AuthSubcommand::Base64 {
                    num_bytes: token_bits.as_bytes(),
                }
            }
            _ => unreachable!("Subcommand is enforced by clap"),
        }),
        Some(("scheduler", matches)) => {
            if matches.is_present("syslog") {
                let log_level: LogLevel = matches.value_of_t("syslog")?;
                check_init_syslog("sccache-scheduler", log_level);
            }

            let config_path: PathBuf = matches.value_of_t("config")?;
            if let Some(config) = config::scheduler::from_path(&config_path)? {
                Command::Scheduler(config)
            } else {
                bail!("Could not load config")
            }
        }
        Some(("server", matches)) => {
            if matches.is_present("syslog") {
                let log_level: LogLevel = matches.value_of_t("syslog")?;
                check_init_syslog("sccache-buildserver", log_level);
            }

            let config_path: PathBuf = matches.value_of_t("config")?;
            if let Some(config) = config::server::from_path(&config_path)? {
                Command::Server(config)
            } else {
                bail!("Could not load config")
            }
        }
        _ => unreachable!("Subcommand is enforced by clap"),
    })
}

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

    const EXE: &str = "sccache-dist";

    fn auth_generate_shared_tokens_bits(bit_val: &'static str) -> Vec<&'static str> {
        vec![EXE, "auth", "generate-shared-token", "--bits", bit_val]
    }

    fn auth_generate_jwt_hs256_server_token(subcommand_args: &[&'static str]) -> Vec<&'static str> {
        let mut args = vec![EXE, "auth", "generate-jwt-hs256-server-token"];
        args.extend_from_slice(subcommand_args);
        args
    }

    #[test]
    fn debug_assert() {
        get_clap_command().debug_assert()
    }

    #[test]
    fn missing_required_subcommands_fails() {
        let args_sets = &[vec![EXE], vec![EXE, "auth"]];

        for args in args_sets {
            assert!(try_parse_from(args).is_err());
        }
    }

    #[test]
    fn invalid_token_bits_fails() {
        let invalid_vals = vec!["not_a_num", "58", "8000", "70"];

        for invalid_val in invalid_vals {
            let args = auth_generate_shared_tokens_bits(invalid_val);
            assert!(try_parse_from(args).is_err());
        }
    }

    #[test]
    fn auth_generate_server_token_needs_key_source() {
        let server_args = &["--server", "127.0.0.1:4321"];

        let no_key = auth_generate_jwt_hs256_server_token(server_args);
        assert!(try_parse_from(no_key).is_err());

        let mut too_many_keys = auth_generate_jwt_hs256_server_token(server_args);
        too_many_keys.extend_from_slice(&["--secret-key", "secret", "--config", "some/path.toml"]);
        assert!(try_parse_from(too_many_keys).is_err());
    }

    // This is all just to work around `PartialEq` not being on some of the values used in variants
    // for `Command` yet
    fn assert_args_parse_to_auth(args: Vec<&'static str>, ideal_auth: AuthSubcommand) {
        match try_parse_from(&args) {
            Ok(Command::Auth(auth)) => assert_eq!(auth, ideal_auth),
            _ => panic!("Bad parsing for: {:#?}", args),
        }
    }

    #[test]
    fn auth_generate_jwt_hs256_key_good() {
        let args = vec![EXE, "auth", "generate-jwt-hs256-key"];

        assert_args_parse_to_auth(args, AuthSubcommand::Base64 { num_bytes: 256 / 8 });
    }

    #[test]
    fn auth_generate_jwt_hs256_server_token_good() {
        let base = auth_generate_jwt_hs256_server_token(&["--server", "127.0.0.1:4321"]);
        let server_socket: SocketAddr = "127.0.0.1:4321".parse().unwrap();
        let server_id = ServerId::new(server_socket);

        let mut secret_key = base.clone();
        secret_key.extend_from_slice(&["--secret-key", "very secret"]);
        assert_args_parse_to_auth(
            secret_key,
            AuthSubcommand::JwtHS256ServerToken {
                server_id,
                secret_key: "very secret".to_owned(),
            },
        );

        // TODO: need some sample config to be used here
        // let mut config = base;
        // config.extend_from_slice(&["--config", "some/path.toml"]);
    }

    #[test]
    fn auth_generate_shared_token_good() {
        let raw_to_expected_bit_vals = &[
            ("64", 64 / 8),
            ("128", 128 / 8),
            ("136", 136 / 8),
            ("4000", 4_000 / 8),
        ];

        for (raw, expected) in raw_to_expected_bit_vals {
            let args = auth_generate_shared_tokens_bits(raw);
            assert_args_parse_to_auth(
                args,
                AuthSubcommand::Base64 {
                    num_bytes: *expected,
                },
            );
        }
    }

    // TODO: add tests for with sample config file
    // #[test]
    // fn scheduler_good() {
    //     todo!()
    // }

    // TODO: add tests for with sample config file
    // #[test]
    // fn server_good() {
    //     todo!()
    // }
}
<commit_msg>Make behavior of `TokenBits` more obvious<commit_after>use std::{env, ffi::OsString, fmt, net::SocketAddr, path::PathBuf, str::FromStr};

use anyhow::bail;
use clap::{Arg, ArgGroup, Command as ClapCommand};
use sccache::{config, dist::ServerId};
use syslog::Facility;

use crate::cmdline::{AuthSubcommand, Command};

#[derive(Debug)]
struct TokenLength(usize);

impl TokenLength {
    fn as_bytes(&self) -> usize {
        self.0 / 8
    }

    fn from_bits(bits: &str) -> anyhow::Result<Self> {
        let bits: usize = bits.parse()?;
        if bits % 8 != 0 || bits < 64 || bits > 4_096 {
            bail!("Number of bits must be divisible by 8, greater than 64 and less than 4096")
        }

        Ok(Self(bits))
    }
}

impl fmt::Display for TokenLength {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

#[derive(Clone, Copy)]
enum LogLevel {
    Error,
    Warn,
    Info,
    Debug,
    Trace,
}

impl LogLevel {
    fn as_str(&self) -> &'static str {
        match self {
            Self::Error => "error",
            Self::Warn => "warn",
            Self::Info => "info",
            Self::Debug => "debug",
            Self::Trace => "trace",
        }
    }

    fn values() -> &'static [Self] {
        &[
            Self::Error,
            Self::Warn,
            Self::Info,
            Self::Debug,
            Self::Trace,
        ]
    }
}

impl FromStr for LogLevel {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let variant = match s {
            "error" => Self::Error,
            "warn" => Self::Warn,
            "info" => Self::Info,
            "debug" => Self::Debug,
            "trace" => Self::Trace,
            _ => bail!("Unknown log level: {:?}", s),
        };

        Ok(variant)
    }
}

impl From<LogLevel> for log::LevelFilter {
    fn from(log_level: LogLevel) -> Self {
        match log_level {
            LogLevel::Error => Self::Error,
            LogLevel::Warn => Self::Warn,
            LogLevel::Info => Self::Info,
            LogLevel::Debug => Self::Debug,
            LogLevel::Trace => Self::Trace,
        }
    }
}

/// Parse the commandline into a `Command` to execute.
pub fn parse() -> Command {
    match try_parse_from(env::args()) {
        Ok(cmd) => cmd,
        Err(e) => match e.downcast::<clap::error::Error>() {
            Ok(clap_err) => clap_err.exit(),
            Err(some_other_err) => {
                println!("sccache-dist: {some_other_err}");
                for source_err in some_other_err.chain().skip(1) {
                    println!("sccache-dist: caused by: {source_err}");
                }
                std::process::exit(1);
            }
        },
    }
}

fn flag_infer_long(name: &'static str) -> Arg<'static> {
    Arg::new(name).long(name)
}

fn get_clap_command() -> ClapCommand<'static> {
    let syslog = flag_infer_long("syslog")
        .help("Log to the syslog with LEVEL")
        .value_name("LEVEL")
        .possible_values(LogLevel::values().iter().map(LogLevel::as_str));
    let config_with_help_message =
        |help: &'static str| flag_infer_long("config").help(help).value_name("PATH");

    ClapCommand::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .subcommand_required(true)
        .subcommand(
            ClapCommand::new("auth")
                .subcommand_required(true)
                .subcommand(ClapCommand::new("generate-jwt-hs256-key"))
                .subcommand(
                    ClapCommand::new("generate-jwt-hs256-server-token")
                        .args(&[
                            flag_infer_long("server")
                                .help("Generate a key for the specified server")
                                .value_name("SERVER_ADDR")
                                .required(true),
                            flag_infer_long("secret-key")
                                .help("Use specified key to create the token")
                                .value_name("KEY"),
                            config_with_help_message(
                                "Use the key from the scheduler config file at PATH",
                            ),
                        ])
                        .group(
                            ArgGroup::new("key_source_mutual_exclusion")
                                .args(&["config", "secret-key"])
                                .required(true),
                        ),
                )
                .subcommand(
                    ClapCommand::new("generate-shared-token").arg(
                        flag_infer_long("bits")
                            .help("Use the specified number of bits of randomness")
                            .value_name("BITS")
                            .default_value("256")
                            .validator(TokenLength::from_bits),
                    ),
                ),
        )
        .subcommand(ClapCommand::new("scheduler").args(&[
            config_with_help_message("Use the scheduler config file at PATH").required(true),
            syslog.clone(),
        ]))
        .subcommand(ClapCommand::new("server").args(&[
            config_with_help_message("Use the server config file at PATH").required(true),
            syslog,
        ]))
}

fn check_init_syslog(name: &str, log_level: LogLevel) {
    let level = log::LevelFilter::from(log_level);
    drop(syslog::init(Facility::LOG_DAEMON, level, Some(name)));
}

fn try_parse_from(
    args: impl IntoIterator<Item = impl Into<OsString> + Clone>,
) -> anyhow::Result<Command> {
    let matches = get_clap_command().try_get_matches_from(args)?;

    Ok(match matches.subcommand() {
        Some(("auth", matches)) => Command::Auth(match matches.subcommand() {
            // Size based on https://briansmith.org/rustdoc/ring/hmac/fn.recommended_key_len.html
            Some(("generate-jwt-hs256-key", _)) => AuthSubcommand::Base64 { num_bytes: 256 / 8 },
            Some(("generate-jwt-hs256-server-token", matches)) => {
                let server_addr: SocketAddr = matches.value_of_t("server")?;
                let server_id = ServerId::new(server_addr);

                let secret_key = if matches.is_present("config") {
                    let config_path: PathBuf = matches.value_of_t("config")?;
                    if let Some(config) = config::scheduler::from_path(&config_path)? {
                        match config.server_auth {
                            config::scheduler::ServerAuth::JwtHS256 { secret_key } => secret_key,
                            config::scheduler::ServerAuth::Insecure
                            | config::scheduler::ServerAuth::Token { .. } => {
                                bail!("Scheduler not configured with JWT HS256")
                            }
                        }
                    } else {
                        bail!("Could not load config")
                    }
                } else {
                    matches.value_of_t("secret-key")?
                };

                AuthSubcommand::JwtHS256ServerToken {
                    secret_key,
                    server_id,
                }
            }
            Some(("generate-shared-token", matches)) => {
                let token_bits = TokenLength::from_bits(
                    matches.value_of("bits").expect("clap provides default"),
                )
                .expect("clap uses `from_bits` as a validator");

                AuthSubcommand::Base64 {
                    num_bytes: token_bits.as_bytes(),
                }
            }
            _ => unreachable!("Subcommand is enforced by clap"),
        }),
        Some(("scheduler", matches)) => {
            if matches.is_present("syslog") {
                let log_level: LogLevel = matches.value_of_t("syslog")?;
                check_init_syslog("sccache-scheduler", log_level);
            }

            let config_path: PathBuf = matches.value_of_t("config")?;
            if let Some(config) = config::scheduler::from_path(&config_path)? {
                Command::Scheduler(config)
            } else {
                bail!("Could not load config")
            }
        }
        Some(("server", matches)) => {
            if matches.is_present("syslog") {
                let log_level: LogLevel = matches.value_of_t("syslog")?;
                check_init_syslog("sccache-buildserver", log_level);
            }

            let config_path: PathBuf = matches.value_of_t("config")?;
            if let Some(config) = config::server::from_path(&config_path)? {
                Command::Server(config)
            } else {
                bail!("Could not load config")
            }
        }
        _ => unreachable!("Subcommand is enforced by clap"),
    })
}

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

    const EXE: &str = "sccache-dist";

    fn auth_generate_shared_tokens_bits(bit_val: &'static str) -> Vec<&'static str> {
        vec![EXE, "auth", "generate-shared-token", "--bits", bit_val]
    }

    fn auth_generate_jwt_hs256_server_token(subcommand_args: &[&'static str]) -> Vec<&'static str> {
        let mut args = vec![EXE, "auth", "generate-jwt-hs256-server-token"];
        args.extend_from_slice(subcommand_args);
        args
    }

    #[test]
    fn debug_assert() {
        get_clap_command().debug_assert()
    }

    #[test]
    fn missing_required_subcommands_fails() {
        let args_sets = &[vec![EXE], vec![EXE, "auth"]];

        for args in args_sets {
            assert!(try_parse_from(args).is_err());
        }
    }

    #[test]
    fn invalid_token_bits_fails() {
        let invalid_vals = vec!["not_a_num", "58", "8000", "70"];

        for invalid_val in invalid_vals {
            let args = auth_generate_shared_tokens_bits(invalid_val);
            assert!(try_parse_from(args).is_err());
        }
    }

    #[test]
    fn auth_generate_server_token_needs_key_source() {
        let server_args = &["--server", "127.0.0.1:4321"];

        let no_key = auth_generate_jwt_hs256_server_token(server_args);
        assert!(try_parse_from(no_key).is_err());

        let mut too_many_keys = auth_generate_jwt_hs256_server_token(server_args);
        too_many_keys.extend_from_slice(&["--secret-key", "secret", "--config", "some/path.toml"]);
        assert!(try_parse_from(too_many_keys).is_err());
    }

    // This is all just to work around `PartialEq` not being on some of the values used in variants
    // for `Command` yet
    fn assert_args_parse_to_auth(args: Vec<&'static str>, ideal_auth: AuthSubcommand) {
        match try_parse_from(&args) {
            Ok(Command::Auth(auth)) => assert_eq!(auth, ideal_auth),
            _ => panic!("Bad parsing for: {:#?}", args),
        }
    }

    #[test]
    fn auth_generate_jwt_hs256_key_good() {
        let args = vec![EXE, "auth", "generate-jwt-hs256-key"];

        assert_args_parse_to_auth(args, AuthSubcommand::Base64 { num_bytes: 256 / 8 });
    }

    #[test]
    fn auth_generate_jwt_hs256_server_token_good() {
        let base = auth_generate_jwt_hs256_server_token(&["--server", "127.0.0.1:4321"]);
        let server_socket: SocketAddr = "127.0.0.1:4321".parse().unwrap();
        let server_id = ServerId::new(server_socket);

        let mut secret_key = base.clone();
        secret_key.extend_from_slice(&["--secret-key", "very secret"]);
        assert_args_parse_to_auth(
            secret_key,
            AuthSubcommand::JwtHS256ServerToken {
                server_id,
                secret_key: "very secret".to_owned(),
            },
        );

        // TODO: need some sample config to be used here
        // let mut config = base;
        // config.extend_from_slice(&["--config", "some/path.toml"]);
    }

    #[test]
    fn auth_generate_shared_token_good() {
        let raw_to_expected_bit_vals = &[
            ("64", 64 / 8),
            ("128", 128 / 8),
            ("136", 136 / 8),
            ("4000", 4_000 / 8),
        ];

        for (raw, expected) in raw_to_expected_bit_vals {
            let args = auth_generate_shared_tokens_bits(raw);
            assert_args_parse_to_auth(
                args,
                AuthSubcommand::Base64 {
                    num_bytes: *expected,
                },
            );
        }
    }

    // TODO: add tests for with sample config file
    // #[test]
    // fn scheduler_good() {
    //     todo!()
    // }

    // TODO: add tests for with sample config file
    // #[test]
    // fn server_good() {
    //     todo!()
    // }
}
<|endoftext|>


<commit_before>//! Handling for mobile originated (MO) messages.
//!
//! SBD messages really can come in two forms, mobile-originated and mobile-terminated. This module
//! provides functionality for the MO subset of messages.

use std::io::Read;
use std::path::Path;

use byteorder::{ReadBytesExt, BigEndian};
use chrono::{DateTime, Duration, TimeZone, UTC};
use num::traits::FromPrimitive;

use {Error, Result};
use information_element::{SessionStatus, InformationElementType};
use message::Message;

/// A mobile originated (MO) message;
#[derive(Debug)]
pub struct MobileOriginated {
    cdr_reference: u32,
    imei: [u8; 15],
    session_status: SessionStatus,
    momsn: u16,
    mtmsn: u16,
    time: DateTime<UTC>,
    payload: Vec<u8>,
}

impl Default for MobileOriginated {
    fn default() -> MobileOriginated {
        MobileOriginated {
            cdr_reference: 0,
            imei: [0; 15],
            session_status: SessionStatus::Unknown,
            momsn: 0,
            mtmsn: 0,
            time: UTC.ymd(1970, 1, 1).and_hms(0, 0, 0),
            payload: Vec::new(),
        }
    }
}

impl MobileOriginated {
    /// Reads a mobile originated message from a `Path`.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::mobile_originated::MobileOriginated;
    /// MobileOriginated::from_path("data/0-mo.sbd");
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<MobileOriginated> {
        Ok(try!(try!(Message::from_path(path)).into_mobile_originated()))
    }

    /// Returns the cdr reference, also called the auto ID.
    ///
    /// This is a unique value given each call data record.
    pub fn cdr_reference(&self) -> u32 {
        self.cdr_reference
    }

    /// Returns the IMEI number.
    ///
    /// This is unique to each unit, and is always 15 characters long.
    pub fn imei(&self) -> &[u8] {
        &self.imei
    }

    /// Returns the status of the mobile originated session.
    pub fn session_status(&self) -> SessionStatus {
        self.session_status
    }

    /// Returns the mobile originated message sequence number.
    ///
    /// This value is set by the IMEI, and is incremented for every successful SBD session.
    pub fn momsn(&self) -> u16 {
        self.momsn
    }

    /// Returns the mobile terminated message squence number.
    ///
    /// This value is set by the Gateway when the message is queued for delivery. The MTMSN is
    /// tranferred to the IMEI as part of the MT payload transfer, regardless of the session
    /// success.
    pub fn mtmsn(&self) -> u16 {
        self.mtmsn
    }

    /// Returns the UTC timestamp in the form of an epoch integer.
    ///
    /// This is the time of the session between the IMEI and the Gateway, in seconds since the
    /// start of the epoch: 1/1/1970 00:00:00.
    pub fn time(&self) -> DateTime<UTC> {
        self.time
    }

    /// Returns the MO payload.
    pub fn payload(&self) -> &Vec<u8> {
        &self.payload
    }
}

impl Message {
    /// Convert a message into a mobile originated message.
    ///
    /// Returns an error if this message isn't really mobile originated.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// use sbd::mobile_originated;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// message.into_mobile_originated().unwrap();
    /// ```
    pub fn into_mobile_originated(self) -> Result<MobileOriginated> {
        let mut information_elements = self.into_information_elements();

        // TODO should we fail if the payload is absent?
        let header = match information_elements.remove(&InformationElementType::MobileOriginatedHeader) {
            Some(header) => header,
            None => return Err(Error::NoMobileOriginatedHeader),
        };
        let payload = match information_elements.remove(&InformationElementType::MobileOriginatedPayload) {
            Some(payload) => payload,
            None => return Err(Error::NoMobileOriginatedPayload),
        };

        let mut readable = header.as_contents_reader();
        let mut mobile_originated: MobileOriginated = Default::default();
        mobile_originated.cdr_reference = try!(readable.read_u32::<BigEndian>());
        let bytes_read = try!(readable.read(&mut mobile_originated.imei));
        if bytes_read != mobile_originated.imei.len() {
            return Err(Error::Undersized(bytes_read));
        }
        mobile_originated.session_status = match SessionStatus::from_u8(try!(readable.read_u8())) {
            Some(status) => status,
            None => SessionStatus::Unknown,
        };
        mobile_originated.momsn = try!(readable.read_u16::<BigEndian>());
        mobile_originated.mtmsn = try!(readable.read_u16::<BigEndian>());
        mobile_originated.time = UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) +
            Duration::seconds(try!(readable.read_u32::<BigEndian>()) as i64);

        mobile_originated.payload = payload.into_contents();

        Ok(mobile_originated)
    }
}

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

    use std::str;

    use chrono::{TimeZone, UTC};

    use information_element::SessionStatus;
    use message::Message;

    #[test]
    fn into_mobile_originated() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        message.into_mobile_originated().unwrap();
    }

    #[test]
    fn from_path() {
        MobileOriginated::from_path("data/0-mo.sbd").unwrap();
    }

    #[test]
    fn mo_header_values() {
        let mo = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        assert_eq!(1894516585, mo.cdr_reference());
        assert_eq!("300234063904190", str::from_utf8(mo.imei()).unwrap());
        assert_eq!(SessionStatus::Ok, mo.session_status());
        assert_eq!(75, mo.momsn());
        assert_eq!(0, mo.mtmsn());
        assert_eq!(UTC.ymd(2015, 7, 9).and_hms(18, 15, 08), mo.time());
    }

    #[test]
    fn mo_payload() {
        let mo = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        assert_eq!("test message from pete", str::from_utf8(mo.payload()).unwrap());
    }
}
<commit_msg>Stash the beginnings of MO write_to<commit_after>//! Handling for mobile originated (MO) messages.
//!
//! SBD messages really can come in two forms, mobile-originated and mobile-terminated. This module
//! provides functionality for the MO subset of messages.

use std::io::{Read, Write};
use std::path::Path;

use byteorder::{ReadBytesExt, BigEndian};
use chrono::{DateTime, Duration, TimeZone, UTC};
use num::traits::FromPrimitive;

use {Error, Result};
use information_element::{SessionStatus, InformationElementType};
use message::Message;

/// A mobile originated (MO) message;
#[derive(Debug)]
pub struct MobileOriginated {
    cdr_reference: u32,
    imei: [u8; 15],
    session_status: SessionStatus,
    momsn: u16,
    mtmsn: u16,
    time: DateTime<UTC>,
    payload: Vec<u8>,
}

impl Default for MobileOriginated {
    fn default() -> MobileOriginated {
        MobileOriginated {
            cdr_reference: 0,
            imei: [0; 15],
            session_status: SessionStatus::Unknown,
            momsn: 0,
            mtmsn: 0,
            time: UTC.ymd(1970, 1, 1).and_hms(0, 0, 0),
            payload: Vec::new(),
        }
    }
}

impl MobileOriginated {
    /// Reads a mobile originated message from a `Path`.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::mobile_originated::MobileOriginated;
    /// MobileOriginated::from_path("data/0-mo.sbd");
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<MobileOriginated> {
        Ok(try!(try!(Message::from_path(path)).into_mobile_originated()))
    }

    /// Reads a mobile originated message from a `Read`.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::mobile_originated::MobileOriginated;
    /// use std::fs::File;
    /// let file = File::open("data/0-mo.sbd");
    /// MobileOriginated::read_from(&mut file);
    pub fn read_from<R: Read>(readable: &mut R) -> Result<MobileOriginated> {
        Ok(try!(try!(Message::read_from(readable)).into_mobile_originated()))
    }

    /// Writes this mobile originated to a object that can `Write`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use sbd::mobile_originated::MobileOriginated;
    /// let mobile_originated = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
    /// let cursor = Cursor::new(Vec::new());
    /// mobile_originated.write_to(&mut cursor);
    /// ```
    pub fn write_to<W: Write>(&self, writeable: &mut W) -> Result<()> {
        Ok(())
    }

    /// Returns the cdr reference, also called the auto ID.
    ///
    /// This is a unique value given each call data record.
    pub fn cdr_reference(&self) -> u32 {
        self.cdr_reference
    }

    /// Returns the IMEI number.
    ///
    /// This is unique to each unit, and is always 15 characters long.
    pub fn imei(&self) -> &[u8] {
        &self.imei
    }

    /// Returns the status of the mobile originated session.
    pub fn session_status(&self) -> SessionStatus {
        self.session_status
    }

    /// Returns the mobile originated message sequence number.
    ///
    /// This value is set by the IMEI, and is incremented for every successful SBD session.
    pub fn momsn(&self) -> u16 {
        self.momsn
    }

    /// Returns the mobile terminated message squence number.
    ///
    /// This value is set by the Gateway when the message is queued for delivery. The MTMSN is
    /// tranferred to the IMEI as part of the MT payload transfer, regardless of the session
    /// success.
    pub fn mtmsn(&self) -> u16 {
        self.mtmsn
    }

    /// Returns the UTC timestamp in the form of an epoch integer.
    ///
    /// This is the time of the session between the IMEI and the Gateway, in seconds since the
    /// start of the epoch: 1/1/1970 00:00:00.
    pub fn time(&self) -> DateTime<UTC> {
        self.time
    }

    /// Returns the MO payload.
    pub fn payload(&self) -> &Vec<u8> {
        &self.payload
    }
}

impl Message {
    /// Convert a message into a mobile originated message.
    ///
    /// Returns an error if this message isn't really mobile originated.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// use sbd::mobile_originated;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// message.into_mobile_originated().unwrap();
    /// ```
    pub fn into_mobile_originated(self) -> Result<MobileOriginated> {
        let mut information_elements = self.into_information_elements();

        // TODO should we fail if the payload is absent?
        let header = match information_elements.remove(&InformationElementType::MobileOriginatedHeader) {
            Some(header) => header,
            None => return Err(Error::NoMobileOriginatedHeader),
        };
        let payload = match information_elements.remove(&InformationElementType::MobileOriginatedPayload) {
            Some(payload) => payload,
            None => return Err(Error::NoMobileOriginatedPayload),
        };

        let mut readable = header.as_contents_reader();
        let mut mobile_originated: MobileOriginated = Default::default();
        mobile_originated.cdr_reference = try!(readable.read_u32::<BigEndian>());
        let bytes_read = try!(readable.read(&mut mobile_originated.imei));
        if bytes_read != mobile_originated.imei.len() {
            return Err(Error::Undersized(bytes_read));
        }
        mobile_originated.session_status = match SessionStatus::from_u8(try!(readable.read_u8())) {
            Some(status) => status,
            None => SessionStatus::Unknown,
        };
        mobile_originated.momsn = try!(readable.read_u16::<BigEndian>());
        mobile_originated.mtmsn = try!(readable.read_u16::<BigEndian>());
        mobile_originated.time = UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) +
            Duration::seconds(try!(readable.read_u32::<BigEndian>()) as i64);

        mobile_originated.payload = payload.into_contents();

        Ok(mobile_originated)
    }
}

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

    use std::io::Cursor;
    use std::str;

    use chrono::{TimeZone, UTC};

    use information_element::SessionStatus;
    use message::Message;

    #[test]
    fn into_mobile_originated() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        message.into_mobile_originated().unwrap();
    }

    #[test]
    fn from_path() {
        MobileOriginated::from_path("data/0-mo.sbd").unwrap();
    }

    #[test]
    fn mo_header_values() {
        let mo = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        assert_eq!(1894516585, mo.cdr_reference());
        assert_eq!("300234063904190", str::from_utf8(mo.imei()).unwrap());
        assert_eq!(SessionStatus::Ok, mo.session_status());
        assert_eq!(75, mo.momsn());
        assert_eq!(0, mo.mtmsn());
        assert_eq!(UTC.ymd(2015, 7, 9).and_hms(18, 15, 08), mo.time());
    }

    #[test]
    fn mo_payload() {
        let mo = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        assert_eq!("test message from pete", str::from_utf8(mo.payload()).unwrap());
    }

    #[test]
    fn write_to() {
        let mobile_originated = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        let mut cursor = Cursor::new(Vec::new());
        mobile_originated.write_to(&mut cursor).unwrap();
        cursor.set_position(0);
        MobileOriginated::read_from(&mut cursor).unwrap();
    }
}
<|endoftext|>


<commit_before>//! Senkou text splitter
//!
//! Just split text by half(VNR captures double text)

extern crate clipboard_win;
use clipboard_win::{set_clipboard, ClipboardManager};

fn handler_clip_text(text: &String) {
    let len = text.len();
    let len_half = len / 2;
    if len == 0 || len % 2 == 1 { return; }

    let chars: Vec<char> = text.chars().collect();
    if !chars.starts_with(&chars[chars.len()/2..]) { return; }

    drop(chars);
    println!(">>>Action:");
    if set_clipboard(&text[..len_half]).is_err() {
        println!("Failed to update clipboard");
    }

    println!("String repetition is removed(string is halved).");
}

fn main() {
    println!("####################################");
    println!("#  Senkou Text repetition remover  #");
    println!("####################################");
    ClipboardManager::new().delay(10).ok_callback(handler_clip_text).run();
}
<commit_msg>Rust: senkou improve text extraction<commit_after>//! Senkou text splitter
//!
//! Just split text by half(VNR captures double text)

extern crate clipboard_win;
use clipboard_win::{set_clipboard, ClipboardManager};

fn handler_clip_text(text: &String) {
    let len = text.len();
    if len == 0 || len % 2 == 1 { return; }

    const BEGIN: &'static[char] = &['「', '（'];
    const END: &'static[char] = &['」', '）'];
    if let (Some(begin_pos), Some(end_pos)) = (text.rfind(BEGIN), text.rfind(END)) {
        println!(">>>Action:");
        //+3 to skip begin symbol
        if set_clipboard(&text[begin_pos+3..end_pos]).is_err() {
            println!("Failed to update clipboard");
        }

        println!("Dialogue is extracted");
    }
    else {
        let chars: Vec<char> = text.chars().collect();
        if !chars.starts_with(&chars[chars.len()/2..]) { return; }

        drop(chars);
        println!(">>>Action:");
        if set_clipboard(&text[..len/2]).is_err() {
            println!("Failed to update clipboard");
        }

        println!("String repetition is removed(string is halved).");
    }
}

fn main() {
    println!("####################################");
    println!("#  Senkou Text repetition remover  #");
    println!("####################################");
    ClipboardManager::new().delay(1).ok_callback(handler_clip_text).run();
}
<|endoftext|>