<commit_before>extern crate "rustc-serialize" as rustc_serialize;
extern crate uuid;

use uuid::Uuid;
use rustc_serialize::{json, Encodable, Decodable};
use rustc_serialize::base64::{ToBase64, FromBase64, Config, CharacterSet, Newline};
use types::NodeState::{Leader, Follower, Candidate};
use types::TransactionState::{Polling, Accepted, Rejected};
use std::fs::{File, OpenOptions};
use std::fs;
use std::str;
use std::str::StrExt;
use std::io;
use std::io::{Write, ReadExt, Seek};
use std::old_io::IoError;

/// Persistent state
/// **Must be updated to stable storage before RPC response.**
pub struct PersistentState<T: Encodable + Decodable + Send + Clone> {
    current_term: u64,
    voted_for: Option<u64>, // request_vote cares if this is `None`
    log: File,
    last_index: u64,             // The last index of the file.
}

impl<T: Encodable + Decodable + Send + Clone> PersistentState<T> {
    pub fn new(current_term: u64, log_path: Path) -> PersistentState<T> {
        let mut open_opts = OpenOptions::new();
        open_opts.read(true);
        open_opts.write(true);
        open_opts.create(true);
        let mut file = open_opts.open(&log_path).unwrap();
        write!(&mut file, "{:20} {:20}\n", current_term, 0).unwrap();
        PersistentState {
            current_term: current_term,
            voted_for: None,
            log: file,
            last_index: 0,
        }
    }
    /// Gets the `last_index` which you can use to make append requests with.
    pub fn get_last_index(&self) -> u64 { self.last_index }
    /// Gets the `current_term` which is used for request vote.
    pub fn get_current_term(&self) -> u64 { self.current_term }
    /// Sets the current_term. **This should reflect on stable storage.**
    pub fn set_current_term(&mut self, new: u64) -> io::Result<()> {
        // The first line is the header with `current_term`, `voted_for`.
        self.log.seek(io::SeekFrom::Start(0)); // Take the start.
        self.current_term = new;
        // TODO: What do we do about the none case?
        write!(&mut self.log, "{:20} {:20}\n", self.current_term, self.voted_for.unwrap_or(0))
    }
    /// Increments the current_term. **This should reflect on stable storage.**
    pub fn inc_current_term(&mut self) { self.current_term += 1 }
    /// Gets the `voted_for`.
    pub fn get_voted_for(&mut self) -> Option<u64> { self.voted_for }
    /// Sets the `voted_for. **This should reflect on stable storage.**
    pub fn set_voted_for(&mut self, new: Option<u64>) -> io::Result<()> {
        // The first line is the header with `current_term`, `voted_for`.
        self.log.seek(io::SeekFrom::Start(0)); // Take the start.
        self.voted_for = new;
        // TODO: What do we do about the none case?
        write!(&mut self.log, "{:20} {:20}\n", self.current_term, self.voted_for.unwrap_or(0))
    }
    pub fn append_entries(&mut self, prev_log_index: u64, prev_log_term: u64,
                      entries: Vec<(u64, T)>) -> io::Result<()> {
        let position = self.move_to(prev_log_index + 1);
        let number = entries.len();
        // TODO: Possibly purge.
        for (term, entry) in entries {
            // TODO: I don't like the "doubling" here. How can we do this better?
            write!(&mut self.log, "{} {}\n", term, PersistentState::encode(entry));
        }
        self.last_index = if prev_log_index == 0 {
            number as u64 - 1
        } else { prev_log_index + number as u64 };
        Ok(())
    }
    fn encode(entry: T) -> String {
        let json_encoded = json::encode(&entry)
            .unwrap(); // TODO: Don't unwrap.
        json_encoded.as_bytes().to_base64(Config {
            char_set: CharacterSet::UrlSafe,
            newline: Newline::LF,
            pad: false,
            line_length: None,
        })
    }
    fn decode(bytes: String) -> Result<T, rustc_serialize::json::DecoderError> {
        let based = bytes.from_base64()
            .ok().expect("Decoding error. log likely corrupt.");
        let string = str::from_utf8(based.as_slice())
                .unwrap();
        json::decode::<T>(string)
    }
    /// Returns the number of bytes containing `line` lines.
    /// TODO: Cache?
    fn move_to(&mut self, line: u64) -> io::Result<u64> {
        // Gotcha: The first line is NOT a log entry.
        let mut lines_read = 0u64;
        self.log.seek(io::SeekFrom::Start(0)); // Take the start.
        // Go until we've reached `from` new lines.
        let _ = self.log.by_ref().chars().skip_while(|opt| {
            match *opt {
                Ok(val) => {
                    if val == '\n' {
                        lines_read += 1;
                        if lines_read > line { // Greater than here because the first line is a bust.
                            false // At right location.
                        } else {
                            true // Not done yet, more lines to go.
                        }
                    } else {
                        true // Not a new line.
                    }
                },
                _ => false // At EOF. Nothing to purge.
            }
        }).next(); // Side effects.
        self.log.seek(io::SeekFrom::Current(0)) // Where are we?
    }
    /// Do *not* invoke this unless you update the `last_index`!
    fn purge_from_bytes(&mut self, from_bytes: u64) -> io::Result<()> {
        self.log.set_len(from_bytes) // Chop off the file at the given position.
    }
    /// Removes all entries from `from` to the last entry, inclusively.
    pub fn purge_from_index(&mut self, from_line: u64) -> io::Result<()> {
        let position = try!(self.move_to(from_line));
        self.last_index = from_line - 1;
        self.purge_from_bytes(position)
    }
    pub fn retrieve_entries(&mut self, start: u64, end: u64) -> io::Result<Vec<(u64, T)>> {
        let position = self.move_to(start);
        let mut index = start;
        let mut out = vec![];
        let mut read_in = self.log.by_ref()
            .chars()
            .take_while(|val| val.is_ok())
            .filter_map(|val| val.ok()); // We don't really care about issues here.
        for index in range(start, end +1) {
            let mut chars = read_in.by_ref()
                .take_while(|&val| val != '\n')
                .collect::<String>();
            let mut splits = chars.split(' ');
            let term = {
                let chunk = splits.next()
                    .and_then(|v| v.parse::<u64>().ok());
                match chunk {
                    Some(v) => v,
                    None => break,
                }
            };
            let encoded = {
                let chunk = splits.next();
                match chunk {
                    Some(v) => v,
                    None => break,
                }
            };
            let decoded: T = PersistentState::decode(encoded.to_string())
                .ok().expect("Could not unwrap log entry.");
            out.push((term, decoded));
        }
        Ok(out)
    }
    pub fn retrieve_entry(&mut self, index: u64) -> io::Result<(u64, T)> {
        let position = self.move_to(index);
        let mut chars = self.log.by_ref()
            .chars()
            .take_while(|val| val.is_ok())
            .filter_map(|val| val.ok()) // We don't really care about issues here.
            .take_while(|&val| val != '\n').collect::<String>();
        let mut splits = chars.split(' ');
        let term = {
            let chunk = splits.next()
                .and_then(|v| v.parse::<u64>().ok());
            match chunk {
                Some(v) => v,
                None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "Could not parse term.", None)),
            }
        };
        let encoded = {
            let chunk = splits.next();
            match chunk {
                Some(v) => v,
                None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "Could not parse encoded data.", None)),
            }
        };
        let decoded: T = PersistentState::decode(encoded.to_string())
            .ok().expect("Could not unwrap log entry.");
        Ok((term, decoded))
    }
}

/// Volatile state
#[derive(Copy)]
pub struct VolatileState {
    pub commit_index: u64,
    pub last_applied: u64
}

/// Leader Only
/// **Reinitialized after election.**
#[derive(PartialEq, Eq, Clone)]
pub struct LeaderState {
    pub next_index: Vec<u64>,
    pub match_index: Vec<u64>
}

/// Nodes can either be:
///
///   * A `Follower`, which replicates AppendEntries requests and votes for it's leader.
///   * A `Leader`, which leads the cluster by serving incoming requests, ensuring data is
///     replicated, and issuing heartbeats..
///   * A `Candidate`, which campaigns in an election and may become a `Leader` (if it gets enough
///     votes) or a `Follower`, if it hears from a `Leader`.
#[derive(PartialEq, Eq, Clone)]
pub enum NodeState {
    Follower,
    Leader(LeaderState),
    Candidate(Vec<Transaction>),
}

#[derive(PartialEq, Eq, Clone)]
pub struct Transaction {
    pub uuid: Uuid,
    pub state: TransactionState,
}

/// Used to signify the state of of a Request/Response pair. This is only needed
/// on the original sender... not on the reciever.
#[derive(PartialEq, Eq, Copy, Clone)]
pub enum TransactionState {
    Polling,
    Accepted,
    Rejected,
}

#[test]
fn test_persistent_state() {
    let path = Path::new("/tmp/test_path");
    fs::remove_file(&path.clone());
    let mut state = PersistentState::new(0, path.clone());
    // Add 0, 1
    assert_eq!(state.append_entries(0, 0,
        vec![(1, "Foo".to_string()),
             (2, "Bar".to_string())]),
        Ok(()));
    // Check index.
    assert_eq!(state.get_last_index(), 1);
    // Check 0
    assert_eq!(state.retrieve_entry(0),
        Ok((1, "Foo".to_string())));
    // Check 0, 1
    assert_eq!(state.retrieve_entries(0, 1),
        Ok(vec![(1, "Foo".to_string()),
                (2, "Bar".to_string())
        ]));
    // Check 1
    assert_eq!(state.retrieve_entry(1),
        Ok((2, "Bar".to_string())));
    // Add 2, 3
    assert_eq!(state.append_entries(1, 2,
        vec![(2, "Baz".to_string()),
             (3, "FooBarBaz".to_string())]),
        Ok(()));
    assert_eq!(state.get_last_index(), 3);
    // Check 2, 3
    assert_eq!(state.retrieve_entries(2, 3),
        Ok(vec![(2, "Baz".to_string()),
                (3, "FooBarBaz".to_string())
        ]));
    // Remove 2, 3
    assert_eq!(state.purge_from_index(2),
        Ok(()));
    assert_eq!(state.get_last_index(), 1);
    // Check 3,4 are removed, and that code handles lack of entry gracefully.
    assert_eq!(state.retrieve_entries(0, 4),
        Ok(vec![(1, "Foo".to_string()),
                (2, "Bar".to_string())
        ]));
    // Add 3,4,5.
    fs::remove_file(&path.clone());
}
<commit_msg>Add purge to append for #1<commit_after>extern crate "rustc-serialize" as rustc_serialize;
extern crate uuid;

use uuid::Uuid;
use rustc_serialize::{json, Encodable, Decodable};
use rustc_serialize::base64::{ToBase64, FromBase64, Config, CharacterSet, Newline};
use types::NodeState::{Leader, Follower, Candidate};
use types::TransactionState::{Polling, Accepted, Rejected};
use std::fs::{File, OpenOptions};
use std::fs;
use std::str;
use std::str::StrExt;
use std::io;
use std::io::{Write, ReadExt, Seek};
use std::old_io::IoError;

/// Persistent state
/// **Must be updated to stable storage before RPC response.**
pub struct PersistentState<T: Encodable + Decodable + Send + Clone> {
    current_term: u64,
    voted_for: Option<u64>,      // request_vote cares if this is `None`
    log: File,
    last_index: u64,             // The last index of the file.
}

impl<T: Encodable + Decodable + Send + Clone> PersistentState<T> {
    pub fn new(current_term: u64, log_path: Path) -> PersistentState<T> {
        let mut open_opts = OpenOptions::new();
        open_opts.read(true);
        open_opts.write(true);
        open_opts.create(true);
        let mut file = open_opts.open(&log_path).unwrap();
        write!(&mut file, "{:20} {:20}\n", current_term, 0).unwrap();
        PersistentState {
            current_term: current_term,
            voted_for: None,
            log: file,
            last_index: 0,
        }
    }
    /// Gets the `last_index` which you can use to make append requests with.
    pub fn get_last_index(&self) -> u64 { self.last_index }
    /// Gets the `current_term` which is used for request vote.
    pub fn get_current_term(&self) -> u64 { self.current_term }
    /// Sets the current_term. **This should reflect on stable storage.**
    pub fn set_current_term(&mut self, new: u64) -> io::Result<()> {
        // The first line is the header with `current_term`, `voted_for`.
        self.log.seek(io::SeekFrom::Start(0)); // Take the start.
        self.current_term = new;
        // TODO: What do we do about the none case?
        write!(&mut self.log, "{:20} {:20}\n", self.current_term, self.voted_for.unwrap_or(0))
    }
    /// Increments the current_term. **This should reflect on stable storage.**
    pub fn inc_current_term(&mut self) { self.current_term += 1 }
    /// Gets the `voted_for`.
    pub fn get_voted_for(&mut self) -> Option<u64> { self.voted_for }
    /// Sets the `voted_for. **This should reflect on stable storage.**
    pub fn set_voted_for(&mut self, new: Option<u64>) -> io::Result<()> {
        // The first line is the header with `current_term`, `voted_for`.
        self.log.seek(io::SeekFrom::Start(0)); // Take the start.
        self.voted_for = new;
        // TODO: What do we do about the none case?
        write!(&mut self.log, "{:20} {:20}\n", self.current_term, self.voted_for.unwrap_or(0))
    }
    pub fn append_entries(&mut self, prev_log_index: u64, prev_log_term: u64,
                      entries: Vec<(u64, T)>) -> io::Result<()> {
        let position = try!(self.move_to(prev_log_index + 1));
        let number = entries.len();
        try!(self.purge_from_bytes(position)); // Update `last_log_index` later.
        // TODO: Possibly purge.
        for (term, entry) in entries {
            // TODO: I don't like the "doubling" here. How can we do this better?
            write!(&mut self.log, "{} {}\n", term, PersistentState::encode(entry));
        }
        self.last_index = if prev_log_index == 0 {
            number as u64 - 1
        } else { prev_log_index + number as u64 };
        Ok(())
    }
    fn encode(entry: T) -> String {
        let json_encoded = json::encode(&entry)
            .unwrap(); // TODO: Don't unwrap.
        json_encoded.as_bytes().to_base64(Config {
            char_set: CharacterSet::UrlSafe,
            newline: Newline::LF,
            pad: false,
            line_length: None,
        })
    }
    fn decode(bytes: String) -> Result<T, rustc_serialize::json::DecoderError> {
        let based = bytes.from_base64()
            .ok().expect("Decoding error. log likely corrupt.");
        let string = str::from_utf8(based.as_slice())
                .unwrap();
        json::decode::<T>(string)
    }
    /// Returns the number of bytes containing `line` lines.
    /// TODO: Cache?
    fn move_to(&mut self, line: u64) -> io::Result<u64> {
        // Gotcha: The first line is NOT a log entry.
        let mut lines_read = 0u64;
        self.log.seek(io::SeekFrom::Start(0)); // Take the start.
        // Go until we've reached `from` new lines.
        let _ = self.log.by_ref().chars().skip_while(|opt| {
            match *opt {
                Ok(val) => {
                    if val == '\n' {
                        lines_read += 1;
                        if lines_read > line { // Greater than here because the first line is a bust.
                            false // At right location.
                        } else {
                            true // Not done yet, more lines to go.
                        }
                    } else {
                        true // Not a new line.
                    }
                },
                _ => false // At EOF. Nothing to purge.
            }
        }).next(); // Side effects.
        self.log.seek(io::SeekFrom::Current(0)) // Where are we?
    }
    /// Do *not* invoke this unless you update the `last_index`!
    fn purge_from_bytes(&mut self, from_bytes: u64) -> io::Result<()> {
        self.log.set_len(from_bytes) // Chop off the file at the given position.
    }
    /// Removes all entries from `from` to the last entry, inclusively.
    pub fn purge_from_index(&mut self, from_line: u64) -> io::Result<()> {
        let position = try!(self.move_to(from_line));
        self.last_index = from_line - 1;
        self.purge_from_bytes(position)
    }
    pub fn retrieve_entries(&mut self, start: u64, end: u64) -> io::Result<Vec<(u64, T)>> {
        let position = self.move_to(start);
        let mut index = start;
        let mut out = vec![];
        let mut read_in = self.log.by_ref()
            .chars()
            .take_while(|val| val.is_ok())
            .filter_map(|val| val.ok()); // We don't really care about issues here.
        for index in range(start, end +1) {
            let mut chars = read_in.by_ref()
                .take_while(|&val| val != '\n')
                .collect::<String>();
            let mut splits = chars.split(' ');
            let term = {
                let chunk = splits.next()
                    .and_then(|v| v.parse::<u64>().ok());
                match chunk {
                    Some(v) => v,
                    None => break,
                }
            };
            let encoded = {
                let chunk = splits.next();
                match chunk {
                    Some(v) => v,
                    None => break,
                }
            };
            let decoded: T = PersistentState::decode(encoded.to_string())
                .ok().expect("Could not unwrap log entry.");
            out.push((term, decoded));
        }
        Ok(out)
    }
    pub fn retrieve_entry(&mut self, index: u64) -> io::Result<(u64, T)> {
        let position = self.move_to(index);
        let mut chars = self.log.by_ref()
            .chars()
            .take_while(|val| val.is_ok())
            .filter_map(|val| val.ok()) // We don't really care about issues here.
            .take_while(|&val| val != '\n').collect::<String>();
        let mut splits = chars.split(' ');
        let term = {
            let chunk = splits.next()
                .and_then(|v| v.parse::<u64>().ok());
            match chunk {
                Some(v) => v,
                None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "Could not parse term.", None)),
            }
        };
        let encoded = {
            let chunk = splits.next();
            match chunk {
                Some(v) => v,
                None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "Could not parse encoded data.", None)),
            }
        };
        let decoded: T = PersistentState::decode(encoded.to_string())
            .ok().expect("Could not unwrap log entry.");
        Ok((term, decoded))
    }
}

/// Volatile state
#[derive(Copy)]
pub struct VolatileState {
    pub commit_index: u64,
    pub last_applied: u64
}

/// Leader Only
/// **Reinitialized after election.**
#[derive(PartialEq, Eq, Clone)]
pub struct LeaderState {
    pub next_index: Vec<u64>,
    pub match_index: Vec<u64>
}

/// Nodes can either be:
///
///   * A `Follower`, which replicates AppendEntries requests and votes for it's leader.
///   * A `Leader`, which leads the cluster by serving incoming requests, ensuring data is
///     replicated, and issuing heartbeats..
///   * A `Candidate`, which campaigns in an election and may become a `Leader` (if it gets enough
///     votes) or a `Follower`, if it hears from a `Leader`.
#[derive(PartialEq, Eq, Clone)]
pub enum NodeState {
    Follower,
    Leader(LeaderState),
    Candidate(Vec<Transaction>),
}

#[derive(PartialEq, Eq, Clone)]
pub struct Transaction {
    pub uuid: Uuid,
    pub state: TransactionState,
}

/// Used to signify the state of of a Request/Response pair. This is only needed
/// on the original sender... not on the reciever.
#[derive(PartialEq, Eq, Copy, Clone)]
pub enum TransactionState {
    Polling,
    Accepted,
    Rejected,
}

#[test]
fn test_persistent_state() {
    let path = Path::new("/tmp/test_path");
    fs::remove_file(&path.clone());
    let mut state = PersistentState::new(0, path.clone());
    // Add 0, 1
    assert_eq!(state.append_entries(0, 0,
        vec![(0, "Zero".to_string()),
             (1, "One".to_string())]),
        Ok(()));
    // Check index.
    assert_eq!(state.get_last_index(), 1);
    // Check 0
    assert_eq!(state.retrieve_entry(0),
        Ok((0, "Zero".to_string())));
    // Check 0, 1
    assert_eq!(state.retrieve_entries(0, 1),
        Ok(vec![(0, "Zero".to_string()),
                (1, "One".to_string())
        ]));
    // Check 1
    assert_eq!(state.retrieve_entry(1),
        Ok((1, "One".to_string())));
    // Add 2, 3
    assert_eq!(state.append_entries(1, 2,
        vec![(2, "Two".to_string()),
             (3, "Three".to_string())]),
        Ok(()));
    assert_eq!(state.get_last_index(), 3);
    // Check 2, 3
    assert_eq!(state.retrieve_entries(2, 3),
        Ok(vec![(2, "Two".to_string()),
                (3, "Three".to_string())
        ]));
    // Remove 2, 3
    assert_eq!(state.purge_from_index(2),
        Ok(()));
    assert_eq!(state.get_last_index(), 1);
    // Check 3,4 are removed, and that code handles lack of entry gracefully.
    assert_eq!(state.retrieve_entries(0, 4),
        Ok(vec![(0, "Zero".to_string()),
                (1, "One".to_string())
        ]));
    // Add 2,3,4.
    assert_eq!(state.append_entries(1, 2,
        vec![(2, "Two".to_string()),
             (3, "Three".to_string()),
             (4, "Four".to_string())]),
        Ok(()));
    assert_eq!(state.get_last_index(), 4);
    // Add 2,3 again. (4 should be purged)
    assert_eq!(state.append_entries(1, 2,
        vec![(2, "Two".to_string()),
             (3, "Three".to_string())]),
        Ok(()));
    assert_eq!(state.get_last_index(), 3);
    fs::remove_file(&path.clone());
}
<|endoftext|>


<commit_before>//! Globally-used traits, structs, and enums

use svd::types::SVDError;
use eigenvalues::types::EigenError;
use solve_linear::types::SolveError;
use least_squares::LeastSquaresError;
pub use lapack::{c32, c64};

/// Enum for symmetric matrix inputs.
#[repr(u8)]
pub enum Symmetric {
    /// Read elements from the upper-triangular portion of the matrix
    Upper = b'U',

    /// Read elements from the lower-triangular portion of the matrix
    Lower = b'L',
}

/// Universal `linxal` error enum
///
/// This enum can be used as a catch-all for errors from `linxal`
/// computations.
pub enum Error {
    SVD(SVDError),
    Eigen(EigenError),
    LeastSquares(LeastSquaresError),
    SolveLinear(SolveError),
}

impl From<SVDError> for Error {
    fn from(e: SVDError) -> Error {
        Error::SVD(e)
    }
}

impl From<EigenError> for Error {
    fn from(e: EigenError) -> Error {
        Error::Eigen(e)
    }
}

impl From<LeastSquaresError> for Error {
    fn from(e: LeastSquaresError) -> Error {
        Error::LeastSquares(e)
    }
}

impl From<SolveError> for Error {
    fn from(e: SolveError) -> Error {
        Error::SolveLinear(e)
    }
}

pub trait Magnitude: Copy {
    fn mag(self) -> f64;
}

impl Magnitude for f32 {
    fn mag(self) -> f64 {
        self.abs() as f64
    }
}

impl Magnitude for f64 {
    fn mag(self) -> f64 {
        self.abs()
    }
}

impl Magnitude for c32 {
    fn mag(self) -> f64 {
        self.norm() as f64
    }
}

impl Magnitude for c64 {
    fn mag(self) -> f64 {
        self.norm()
    }
}
<commit_msg>feat(LinxalScalar) create LinxalScalar trait for common functionality<commit_after>//! Globally-used traits, structs, and enums

use svd::types::SVDError;
use eigenvalues::types::EigenError;
use solve_linear::types::SolveError;
use least_squares::LeastSquaresError;
use std::ops::Sub;
use std::fmt::Debug;
use num_traits::Float;
pub use lapack::{c32, c64};

/// Enum for symmetric matrix inputs.
#[repr(u8)]
pub enum Symmetric {
    /// Read elements from the upper-triangular portion of the matrix
    Upper = b'U',

    /// Read elements from the lower-triangular portion of the matrix
    Lower = b'L',
}

/// Universal `linxal` error enum
///
/// This enum can be used as a catch-all for errors from `linxal`
/// computations.
pub enum Error {
    SVD(SVDError),
    Eigen(EigenError),
    LeastSquares(LeastSquaresError),
    SolveLinear(SolveError),
}

impl From<SVDError> for Error {
    fn from(e: SVDError) -> Error {
        Error::SVD(e)
    }
}

impl From<EigenError> for Error {
    fn from(e: EigenError) -> Error {
        Error::Eigen(e)
    }
}

impl From<LeastSquaresError> for Error {
    fn from(e: LeastSquaresError) -> Error {
        Error::LeastSquares(e)
    }
}

impl From<SolveError> for Error {
    fn from(e: SolveError) -> Error {
        Error::SolveLinear(e)
    }
}

/// Represents quantities that have a magnitude.
pub trait Magnitude: Copy {
    fn mag(self) -> f64;
}

impl Magnitude for f32 {
    fn mag(self) -> f64 {
        self.abs() as f64
    }
}

impl Magnitude for f64 {
    fn mag(self) -> f64 {
        self.abs()
    }
}

impl Magnitude for c32 {
    fn mag(self) -> f64 {
        self.norm() as f64
    }
}

impl Magnitude for c64 {
    fn mag(self) -> f64 {
        self.norm()
    }
}

/// Common traits for all operations.
pub trait LinxalScalar: Sized + Clone + Magnitude + Debug + Sub<Output=Self> {}
impl<T: Sized + Clone + Magnitude + Debug + Sub<Output=T>> LinxalScalar for T {}

/// Scalars that are also (real) floats.
pub trait LinxalFloat: LinxalScalar + Float {}
impl<T: LinxalScalar + Float> LinxalFloat for T {}
<|endoftext|>


<commit_before>/// This file contain misc functions that are not built into the language,
/// or the standard library, but are still needed for this program

use std::path::PathBuf;
use std::env::{home_dir, current_dir};

use error::DottyError;
use file::FileError;

/*
pub fn path_from_env(env_name : &str) -> Result<PathBuf, DottyError> {
    if let Some(env) = var_os(&env_name) {
        let paths = split_paths(&env).collect::<Vec<_>>();
        Ok(PathBuf::from(join_paths(paths).unwrap()))
    }
    else {
        Err(DottyError::EnvNotFound(env_name.to_string()))
    }
}*/

pub fn to_absolute(path : &PathBuf) -> Result<PathBuf, FileError> {
    if path.is_relative() {
        let mut c_dir = try!(current_dir().map_err(|e| FileError(e, PathBuf::from("."))));
        c_dir.push(path);
        Ok(c_dir)
    } else {
        Ok(path.clone())
    }
}

pub fn resolve_tilde(path : &PathBuf) -> Result<PathBuf, DottyError> {
    let home = try!(home_dir().ok_or(
        DottyError::EnvNotFound(String::from("HOME"))
    ));

    if let Ok(path) = path.strip_prefix("~") {
        Ok(home.join(path))
    } else {
        Ok(path.clone())
    }
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
pub mod os_utils {
    use std::os::unix::fs::symlink;
    use std::path::Path;
    use std::io;

    pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<(), io::Error> {
        symlink(src, dst)
    }
}

#[cfg(target_os = "windows")]
pub mod os_utils {
    use std::os::windows::fs::{symlink_file, symlink_dir};
    use std::path::Path;
    use std::io;

    pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<(), io::Error> {
        if src.as_ref().is_file() {
            symlink_file(src, dst)
        } else if src.as_ref().is_dir() {
            symlink_dir(src, dst)
        } else {
            unreachable!();
        }
    }
}

#[test]
fn test_resolve_tilde_simple() {
    let correct_path = home_dir();
    let result = resolve_tilde(&PathBuf::from("~")).ok();

    assert_eq!(correct_path, result);
}

#[test]
fn test_resolve_tilde_complex() {
    let correct_path = home_dir();
    let result = resolve_tilde(&PathBuf::from("~/a/longer/path")).ok();

    if let Some(mut p) = correct_path {
        p.push("a/longer/path");
        assert_eq!(Some(p), result);
    } else {
        assert_eq!(None, result)
    }
}<commit_msg>Fix failure to build on unix.<commit_after>/// This file contain misc functions that are not built into the language,
/// or the standard library, but are still needed for this program

use std::path::PathBuf;
use std::env::{home_dir, current_dir};

use error::DottyError;
use file::FileError;

/*
pub fn path_from_env(env_name : &str) -> Result<PathBuf, DottyError> {
    if let Some(env) = var_os(&env_name) {
        let paths = split_paths(&env).collect::<Vec<_>>();
        Ok(PathBuf::from(join_paths(paths).unwrap()))
    }
    else {
        Err(DottyError::EnvNotFound(env_name.to_string()))
    }
}*/

pub fn to_absolute(path : &PathBuf) -> Result<PathBuf, FileError> {
    if path.is_relative() {
        let mut c_dir = try!(current_dir().map_err(|e| FileError(e, PathBuf::from("."))));
        c_dir.push(path);
        Ok(c_dir)
    } else {
        Ok(path.clone())
    }
}

pub fn resolve_tilde(path : &PathBuf) -> Result<PathBuf, DottyError> {
    let home = try!(home_dir().ok_or(
        DottyError::EnvNotFound(String::from("HOME"))
    ));

    if let Ok(path) = path.strip_prefix("~") {
        Ok(home.join(path))
    } else {
        Ok(path.clone())
    }
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
pub mod os_utils {
    use std::os::unix::fs::symlink as unix_symlink;
    use std::path::Path;
    use std::io;

    pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<(), io::Error> {
        unix_symlink(src, dst)
    }
}

#[cfg(target_os = "windows")]
pub mod os_utils {
    use std::os::windows::fs::{symlink_file, symlink_dir};
    use std::path::Path;
    use std::io;

    pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<(), io::Error> {
        if src.as_ref().is_file() {
            symlink_file(src, dst)
        } else if src.as_ref().is_dir() {
            symlink_dir(src, dst)
        } else {
            unreachable!();
        }
    }
}

#[test]
fn test_resolve_tilde_simple() {
    let correct_path = home_dir();
    let result = resolve_tilde(&PathBuf::from("~")).ok();

    assert_eq!(correct_path, result);
}

#[test]
fn test_resolve_tilde_complex() {
    let correct_path = home_dir();
    let result = resolve_tilde(&PathBuf::from("~/a/longer/path")).ok();

    if let Some(mut p) = correct_path {
        p.push("a/longer/path");
        assert_eq!(Some(p), result);
    } else {
        assert_eq!(None, result)
    }
}<|endoftext|>


<commit_before>// Higher level OpenCL wrappers.

use CL::*;
use CL::ll::*;

struct Platform {
    id: cl_platform_id
}

pub fn get_platforms() -> ~[Platform] {
    let mut num_platforms = 0;

    // TODO: Check result status
    clGetPlatformIDs(0, ptr::null(), ptr::addr_of(&num_platforms));

    let ids = vec::to_mut(vec::from_elem(num_platforms as uint,
                                         0 as cl_platform_id));

    do vec::as_imm_buf(ids) |ids, len| {
        clGetPlatformIDs(len as cl_uint,
                         ids, ptr::addr_of(&num_platforms))
    };

    do ids.map |id| { Platform { id: *id } }
}

struct Device {
    id: cl_device_id
}

pub fn get_devices(platform: Platform) -> ~[Device] {
    let mut num_devices = 0;

    clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, 0, ptr::null(),
                   ptr::addr_of(&num_devices));

    let ids = vec::to_mut(vec::from_elem(num_devices as uint,
                                         0 as cl_device_id));
    do vec::as_imm_buf(ids) |ids, len| {
        clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, len as cl_uint,
                       ids, ptr::addr_of(&num_devices));
    };

    do ids.map |id| { Device {id: *id }}
}

struct Context {
    ctx: cl_context,

    drop {
        clReleaseContext(self.ctx);
    }
}

pub fn create_context(device: Device) -> Context {
    // TODO: Support for multiple devices
    let mut errcode = 0;

    // TODO: Proper error messages
    let ctx = clCreateContext(ptr::null(), 1, ptr::addr_of(&device.id),
                              ptr::null(), ptr::null(), ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create opencl context!"
    }

    Context { ctx: ctx }
}

struct CommandQueue {
    cqueue: cl_command_queue,

    drop {
        clReleaseCommandQueue(self.cqueue);
    }
}

pub fn create_commandqueue(ctx: &Context, device: Device) -> CommandQueue {
    let mut errcode = 0;

    let cqueue = clCreateCommandQueue(ctx.ctx, device.id, 0, ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create command queue!"
    }

    CommandQueue { cqueue: cqueue }
}

struct Buffer {
    buffer: cl_mem,
    size: int,

    drop {
        clReleaseMemObject(self.buffer);
    }
}

// TODO: How to make this function cleaner and nice
pub fn create_buffer(ctx: &Context, size: int, flags: cl_mem_flags) -> Buffer {
    let mut errcode = 0;

    let buffer = clCreateBuffer(ctx.ctx, flags, size as libc::size_t, ptr::null(),
                                ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create buffer!"
    }

    Buffer { buffer: buffer, size: size }
}

pub fn enqueue_write_buffer(cqueue: CommandQueue, buf: Buffer, host_vector: ~[int]) unsafe {
    let ret = clEnqueueWriteBuffer(cqueue.cqueue, buf.buffer, CL_TRUE, 0,
                                   buf.size as libc::size_t,
                                   vec::raw::to_ptr(host_vector) as *libc::c_void,
                                   0, ptr::null(), ptr::null());
    if ret != CL_SUCCESS {
        fail ~"Failed to enqueue write buffer!"
    }
}

struct Program {
    prg: cl_program,

    drop {
        clReleaseProgram(self.prg);
    }
}

// TODO: Support multiple devices
pub fn create_program_with_binary(ctx: Context, device: Device, binary_path: & Path){
    let mut errcode = 0;
    let binary = io::read_whole_file_str(binary_path).get();
    let program = do str::as_c_str(binary) |kernel_binary| {
        clCreateProgramWithBinary(ctx.ctx, 1, ptr::addr_of(&device.id),
                                  ptr::addr_of(&(binary.leb() + 1)) as *libc::size_t,
                                  ptr::addr_of(&kernel_binary) as **libc::c_uchar,
                                  ptr::null(),
                                  ptr::addr_of(&errcode))
    };

    if errcode != CL_SUCCESS {
        fail ~"Failed to create open cl program with binary!"
    }

    Program { prg: program }
}

<commit_msg>syntax error fix.<commit_after>// Higher level OpenCL wrappers.

use CL::*;
use CL::ll::*;

struct Platform {
    id: cl_platform_id
}

pub fn get_platforms() -> ~[Platform] {
    let mut num_platforms = 0;

    // TODO: Check result status
    clGetPlatformIDs(0, ptr::null(), ptr::addr_of(&num_platforms));

    let ids = vec::to_mut(vec::from_elem(num_platforms as uint,
                                         0 as cl_platform_id));

    do vec::as_imm_buf(ids) |ids, len| {
        clGetPlatformIDs(len as cl_uint,
                         ids, ptr::addr_of(&num_platforms))
    };

    do ids.map |id| { Platform { id: *id } }
}

struct Device {
    id: cl_device_id
}

pub fn get_devices(platform: Platform) -> ~[Device] {
    let mut num_devices = 0;

    clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, 0, ptr::null(),
                   ptr::addr_of(&num_devices));

    let ids = vec::to_mut(vec::from_elem(num_devices as uint,
                                         0 as cl_device_id));
    do vec::as_imm_buf(ids) |ids, len| {
        clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, len as cl_uint,
                       ids, ptr::addr_of(&num_devices));
    };

    do ids.map |id| { Device {id: *id }}
}

struct Context {
    ctx: cl_context,

    drop {
        clReleaseContext(self.ctx);
    }
}

pub fn create_context(device: Device) -> Context {
    // TODO: Support for multiple devices
    let mut errcode = 0;

    // TODO: Proper error messages
    let ctx = clCreateContext(ptr::null(), 1, ptr::addr_of(&device.id),
                              ptr::null(), ptr::null(), ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create opencl context!"
    }

    Context { ctx: ctx }
}

struct CommandQueue {
    cqueue: cl_command_queue,

    drop {
        clReleaseCommandQueue(self.cqueue);
    }
}

pub fn create_commandqueue(ctx: &Context, device: Device) -> CommandQueue {
    let mut errcode = 0;

    let cqueue = clCreateCommandQueue(ctx.ctx, device.id, 0, ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create command queue!"
    }

    CommandQueue { cqueue: cqueue }
}

struct Buffer {
    buffer: cl_mem,
    size: int,

    drop {
        clReleaseMemObject(self.buffer);
    }
}

// TODO: How to make this function cleaner and nice
pub fn create_buffer(ctx: &Context, size: int, flags: cl_mem_flags) -> Buffer {
    let mut errcode = 0;

    let buffer = clCreateBuffer(ctx.ctx, flags, size as libc::size_t, ptr::null(),
                                ptr::addr_of(&errcode));

    if errcode != CL_SUCCESS {
        fail ~"Failed to create buffer!"
    }

    Buffer { buffer: buffer, size: size }
}

pub fn enqueue_write_buffer(cqueue: CommandQueue, buf: Buffer, host_vector: ~[int]) unsafe {
    let ret = clEnqueueWriteBuffer(cqueue.cqueue, buf.buffer, CL_TRUE, 0,
                                   buf.size as libc::size_t,
                                   vec::raw::to_ptr(host_vector) as *libc::c_void,
                                   0, ptr::null(), ptr::null());
    if ret != CL_SUCCESS {
        fail ~"Failed to enqueue write buffer!"
    }
}

struct Program {
    prg: cl_program,

    drop {
        clReleaseProgram(self.prg);
    }
}

// TODO: Support multiple devices
pub fn create_program_with_binary(ctx: Context, device: Device, binary_path: & Path){
    let mut errcode = 0;
    let binary = io::read_whole_file_str(binary_path).get();
    let program = do str::as_c_str(binary) |kernel_binary| {
        clCreateProgramWithBinary(ctx.ctx, 1, ptr::addr_of(&device.id),
                                  ptr::addr_of(&(binary.len() + 1)) as *libc::size_t,
                                  ptr::addr_of(&kernel_binary) as **libc::c_uchar,
                                  ptr::null(),
                                  ptr::addr_of(&errcode))
    };

    if errcode != CL_SUCCESS {
        fail ~"Failed to create open cl program with binary!"
    }

    Program { prg: program }
}

<|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 libc::{c_int, c_void, size_t, c_char};

// 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")]
#[link(name = "hubbub")]
#[link(name = "parserutils")]
#[link(name = "iconv")]
extern { }

#[cfg(target_os = "linux")]
#[cfg(target_os = "android")]
#[link(name = "hubbub")]
#[link(name = "parserutils")]
extern { }

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 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: extern "C" fn(*mut c_void, size_t, *c_void) -> *mut c_void,
                                    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: extern "C" fn(*c_void, *String, *mut *c_void) -> Error,
    create_doctype: extern "C" fn(*c_void, *Doctype, *mut *c_void) -> Error,
    create_element: extern "C" fn(*c_void, *Tag, *mut *c_void) -> Error,
    create_text: extern "C" fn(*c_void, *String, *mut *c_void) -> Error,
    ref_node: extern "C" fn(*c_void, *c_void) -> Error,
    unref_node: extern "C" fn(*c_void, *c_void) -> Error,
    append_child: extern "C" fn(*c_void, *c_void, *c_void, *mut *c_void) -> Error,
    insert_before: extern "C" fn(*c_void, *c_void, *c_void, *mut *c_void) -> Error,
    remove_child: extern "C" fn(*c_void, *c_void, *c_void, *mut *c_void) -> Error,
    clone_node: extern "C" fn(*c_void, *c_void, bool, *mut *c_void) -> Error,
    reparent_children: extern "C" fn(*c_void, *c_void, *c_void) -> Error,
    get_parent: extern "C" fn(*c_void, *c_void, bool, *mut *c_void) -> Error,
    has_children: extern "C" fn(*c_void, *c_void, *mut bool) -> Error,
    form_associate: extern "C" fn(*c_void, *c_void, *c_void) -> Error,
    add_attributes: extern "C" fn(*c_void, *c_void, *Attribute, u32) -> Error,
    set_quirks_mode: extern "C" fn(*c_void, QuirksMode) -> Error,
    encoding_change: extern "C" fn(*c_void, *c_char) -> Error,
    complete_script: extern "C" fn(*c_void, *c_void) -> Error,
    complete_style: extern "C" fn(*c_void, *c_void) -> Error,
    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>Update to current rust: struct fields are private by default.<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 libc::{c_int, c_void, size_t, c_char};

// 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")]
#[link(name = "hubbub")]
#[link(name = "parserutils")]
#[link(name = "iconv")]
extern { }

#[cfg(target_os = "linux")]
#[cfg(target_os = "android")]
#[link(name = "hubbub")]
#[link(name = "parserutils")]
extern { }

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 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: extern "C" fn(*mut c_void, size_t, *c_void) -> *mut c_void,
                                    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 {
    pub create_comment: extern "C" fn(*c_void, *String, *mut *c_void) -> Error,
    pub create_doctype: extern "C" fn(*c_void, *Doctype, *mut *c_void) -> Error,
    pub create_element: extern "C" fn(*c_void, *Tag, *mut *c_void) -> Error,
    pub create_text: extern "C" fn(*c_void, *String, *mut *c_void) -> Error,
    pub ref_node: extern "C" fn(*c_void, *c_void) -> Error,
    pub unref_node: extern "C" fn(*c_void, *c_void) -> Error,
    pub append_child: extern "C" fn(*c_void, *c_void, *c_void, *mut *c_void) -> Error,
    pub insert_before: extern "C" fn(*c_void, *c_void, *c_void, *mut *c_void) -> Error,
    pub remove_child: extern "C" fn(*c_void, *c_void, *c_void, *mut *c_void) -> Error,
    pub clone_node: extern "C" fn(*c_void, *c_void, bool, *mut *c_void) -> Error,
    pub reparent_children: extern "C" fn(*c_void, *c_void, *c_void) -> Error,
    pub get_parent: extern "C" fn(*c_void, *c_void, bool, *mut *c_void) -> Error,
    pub has_children: extern "C" fn(*c_void, *c_void, *mut bool) -> Error,
    pub form_associate: extern "C" fn(*c_void, *c_void, *c_void) -> Error,
    pub add_attributes: extern "C" fn(*c_void, *c_void, *Attribute, u32) -> Error,
    pub set_quirks_mode: extern "C" fn(*c_void, QuirksMode) -> Error,
    pub encoding_change: extern "C" fn(*c_void, *c_char) -> Error,
    pub complete_script: extern "C" fn(*c_void, *c_void) -> Error,
    pub complete_style: extern "C" fn(*c_void, *c_void) -> Error,
    pub 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 {
    pub ptr: *u8,
    pub len: size_t
}

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

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

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

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

<|endoftext|>


<commit_before>extern crate teleborg;
extern crate reqwest;

#[cfg(test)]
mod tests {
    use teleborg::{Bot, ParseMode};
    use teleborg::objects::{Button, Markup};
    use reqwest::Client;

    use std::env;

    const BASE_URL: &'static str = "https://api.telegram.org/bot";

    fn setup() -> (String, i64) {
        let token = env::var("TELEGRAM_BOT_TOKEN")
            .ok()
            .expect("Can't find TELEGRAM_BOT_TOKEN env variable");
        let chat_id = env::var("TELEGRAM_CHAT_ID")
            .ok()
            .expect("Can't find TELEGRAM_CHAT_ID env variable")
            .parse::<i64>()
            .unwrap();
        (token, chat_id)
    }

    #[test]
    fn test_url_inline_keyboard() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();

        let mut total = Vec::<Vec<Button>>::new();
        let mut row = Vec::<Button>::new();
        row.push(Button::new("TestButton".to_string(), Some("http://github.com/voider1/teleborg".to_string()), None, None, None));
        total.push(row);
        let markup = Markup::new(total);
        let message = bot.send_message(&chat_id, "test", None, None, None, None, Some(&markup));
        assert!(message.is_ok());
    }
}<commit_msg>changed names from Markup and Button to InlineKeyboardMarkup and InlineKeyboardButton<commit_after>extern crate teleborg;
extern crate reqwest;

#[cfg(test)]
mod tests {
    use teleborg::{Bot, ParseMode};
    use teleborg::objects::{InlineKeyboardButton, InlineKeyboardMarkup};
    use reqwest::Client;

    use std::env;

    const BASE_URL: &'static str = "https://api.telegram.org/bot";

    fn setup() -> (String, i64) {
        let token = env::var("TELEGRAM_BOT_TOKEN")
            .ok()
            .expect("Can't find TELEGRAM_BOT_TOKEN env variable");
        let chat_id = env::var("TELEGRAM_CHAT_ID")
            .ok()
            .expect("Can't find TELEGRAM_CHAT_ID env variable")
            .parse::<i64>()
            .unwrap();
        (token, chat_id)
    }

    #[test]
    fn test_url_inline_keyboard() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();

        let mut total = Vec::<Vec<InlineKeyboardButton>>::new();
        let mut row = Vec::<InlineKeyboardButton>::new();
        row.push(InlineKeyboardButton::new("TestButton".to_string(), Some("http://github.com/voider1/teleborg".to_string()), None, None, None));
        total.push(row);
        let markup = InlineKeyboardMarkup::new(total);
        let message = bot.send_message(&chat_id, "test", None, None, None, None, Some(&markup));
        assert!(message.is_ok());
    }
}<|endoftext|>


<commit_before>use io::WriterUtil;
use std::map;
use map::HashMap;

use syntax::ast;
use syntax::ast_util::*;
use syntax::ext::base;
use syntax::ext::build;
use syntax::parse;
use syntax::print::pprust;

use types::*;

struct GenCtx {
    ext_cx: base::ext_ctxt,
    mut unnamed_ty: uint,
    keywords: HashMap<~str, ()>
}

fn rust_id(ctx: &GenCtx, name: ~str) -> ~str {
    if ctx.keywords.contains_key(name) {
        return ~"_" + name;
    }
    return name;
}

fn unnamed_name(ctx: &GenCtx) -> ~str {
    ctx.unnamed_ty += 1;
    return fmt!("Unnamed%u", ctx.unnamed_ty);
}

fn gen_rs(out: io::Writer, link: ~str, globs: ~[Global]) {
    let ctx = GenCtx { ext_cx: base::mk_ctxt(parse::new_parse_sess(None), ~[]),
                       mut unnamed_ty: 0,
                       keywords: syntax::parse::token::keyword_table()
                     };

    let mut fs = ~[];
    let mut vs = ~[];
    let mut gs = ~[];
    for globs.each |g| {
        match *g {
            GOther => {}
            GFunc(_) => fs.push(*g),
            GVar(_) => vs.push(*g),
            _ => gs.push(*g)
        }
    }

    let mut defs = ~[];
    gs = remove_redundent_decl(gs);
    for gs.each |g| {
        match *g {
            GType(ti) => defs += ctypedef_to_rs(&ctx, ti.name, ti.ty),
            GCompDecl(ci) => if ci.cstruct {
                defs += ctypedef_to_rs(&ctx, ~"Struct_" + ci.name, @TVoid)
            } else {
                defs += ctypedef_to_rs(&ctx, ~"Union_" + ci.name, @TVoid)
            },
            GComp(ci) => if ci.cstruct {
                defs.push(cstruct_to_rs(&ctx, ci.name, ci.fields, false))
            } else {
                defs += cunion_to_rs(&ctx, ci.name, ci.fields, false)
            },
            GEnumDecl(ei) => defs += ctypedef_to_rs(&ctx, ~"Enum_" + ei.name, @TVoid),
            GEnum(ei) => defs += cenum_to_rs(&ctx, ei.name, ei.items, ei.kind, false),
            _ => { }
        }
    }

    let vars = do vs.map |v| {
        match *v {
            GVar(vi) => cvar_to_rs(&ctx, vi.name, vi.ty),
            _ => { fail ~"generate global variables" }
        }
    };

    let funcs = do fs.map |f| {
        match *f {
            GFunc(vi) => {
                match *vi.ty {
                    TFunc(rty, aty, var) => cfunc_to_rs(&ctx, vi.name, rty, aty, var),
                    _ => { fail ~"generate functions" }
                }
            },
            _ => { fail ~"generate functions" }
        }
    };

    let views = ~[mk_import(&ctx, ~[~"libc"])];
    defs.push(mk_extern(&ctx, link, vars, funcs));

    let crate = @dummy_spanned({
        directives: ~[],
        module: {
            view_items: views,
            items: defs,
        },
        attrs: ~[],
        config: ~[]
    });

    let ps = pprust::rust_printer(out, ctx.ext_cx.parse_sess().interner);
    out.write_line(~"/* automatically generated by rust-bindgen */\n");
    pprust::print_crate_(ps, crate);
}

fn mk_import(ctx: &GenCtx, path: ~[~str]) -> @ast::view_item {
    let view = ast::view_item_import(~[
        @dummy_spanned(
            ast::view_path_glob(
                @{ span: dummy_sp(),
                   global: false,
                   idents: path.map(|p| ctx.ext_cx.ident_of(*p)),
                   rp: None,
                   types: ~[]
                },
                ctx.ext_cx.next_id()
            )
        )
    ]);

    return @{ node: view,
              attrs: ~[],
              vis: ast::inherited,
              span: dummy_sp()
           };
}

fn mk_extern(ctx: &GenCtx, link: ~str, vars: ~[@ast::foreign_item], funcs: ~[@ast::foreign_item]) -> @ast::item {
    let link_args = dummy_spanned({
        style: ast::attr_outer,
        value: dummy_spanned(
            ast::meta_name_value(
                ~"link_args",
                dummy_spanned(ast::lit_str(@(~"-l"+link)))
            )
        ),
        is_sugared_doc: false
    });

    let ext = ast::item_foreign_mod({
        sort: ast::anonymous,
        view_items: ~[],
        items: vars + funcs
    });

    return @{ ident: ctx.ext_cx.ident_of(~""),
              attrs: ~[link_args],
              id: ctx.ext_cx.next_id(),
              node: ext,
              vis: ast::inherited,
              span: dummy_sp()
           };
}

fn remove_redundent_decl(gs: ~[Global]) -> ~[Global] {
    let mut ret = ~[];
    let mut pre = GOther;

    for gs.each |g| {
        match (pre, *g) {
            (GComp(ci1), GType(ti)) => match *ti.ty {
                TComp(ci2) => if !str::is_empty(ci1.name) {
                    ret.push(pre)
                },
                _ => ret.push(pre)
            },
            (GEnum(ei1), GType(ti)) => match *ti.ty {
                TEnum(ei2) => if !str::is_empty(ei1.name) {
                    ret.push(pre)
                },
                _ => ret.push(pre)
            },
            _ => ret.push(pre)
        }
        pre = *g;
    }

    ret.push(gs.last());

    return ret;
}

fn ctypedef_to_rs(ctx: &GenCtx, name: ~str, ty: @Type) -> ~[@ast::item] {
    fn mk_item(ctx: &GenCtx, name: ~str, ty: @Type) -> @ast::item {
        let rust_name = rust_id(ctx, name);
        let rust_ty = cty_to_rs(ctx, ty);
        let base = ast::item_ty(
            @{ id: ctx.ext_cx.next_id(),
               node: rust_ty.node,
               span: dummy_sp(),
            },
            ~[]
        );

        return @{ ident: ctx.ext_cx.ident_of(rust_name),
                  attrs: ~[],
                  id: ctx.ext_cx.next_id(),
                  node: base,
                  vis: ast::inherited,
                  span: dummy_sp()
               };
    }

    return match *ty {
        TComp(ci) => if str::is_empty(ci.name) {
            if ci.cstruct {
                ~[cstruct_to_rs(ctx, name, ci.fields, true)]
            } else {
                cunion_to_rs(ctx, name, ci.fields, true)
            }
        } else {
            ~[mk_item(ctx, name, ty)]
        },
        TEnum(ei) => if str::is_empty(ei.name) {
            cenum_to_rs(ctx, name, ei.items, ei.kind, true)
        } else {
            ~[mk_item(ctx, name, ty)]
        },
        _ => ~[mk_item(ctx, name, ty)]
    }
}

fn cstruct_to_rs(ctx: &GenCtx, name: ~str, fields: ~[@FieldInfo], typedef: bool) -> @ast::item {
    let prefix = if typedef { ~"" } else { ~"Struct_" };
    let s_name = if str::is_empty(name) {
        unnamed_name(ctx)
    } else {
        name
    };

    let mut unnamed = 0;
    let fs = do fields.map | f| {
        let f_name = if str::is_empty(f.name) {
            unnamed += 1;
            fmt!("unnamed_field%u", unnamed)
        } else {
            rust_id(ctx, f.name)
        };

        let f_ty = cty_to_rs(ctx, f.ty);

        @dummy_spanned({
            kind: ast::named_field(
                ctx.ext_cx.ident_of(f_name),
                ast::class_immutable,
                ast::inherited
            ),
            id: ctx.ext_cx.next_id(),
            ty: f_ty
        })
    };

    let def = ast::item_class(
        @{ traits: ~[],
           fields: fs,
           methods: ~[],
           ctor: None,
           dtor: None
        },
        ~[]
    );

    return @{ ident: ctx.ext_cx.ident_of(rust_id(ctx, prefix + s_name)),
              attrs: ~[],
              id: ctx.ext_cx.next_id(),
              node: def,
              vis: ast::inherited,
              span: dummy_sp()
           };
}

fn cunion_to_rs(ctx: &GenCtx, name: ~str, _fields: ~[@FieldInfo], typedef: bool) -> ~[@ast::item] {
    let prefix = if typedef { ~"" } else { ~"Union_" };
    let u_name = if str::is_empty(name) {
        unnamed_name(ctx)
    } else {
        name
    };

    return ctypedef_to_rs(ctx, rust_id(ctx, prefix + u_name), @TVoid);
}

fn cenum_to_rs(ctx: &GenCtx, name: ~str, items: ~[@EnumItem], kind: IKind, typedef: bool) -> ~[@ast::item] {
    let prefix = if typedef { ~"" } else { ~"Enum_" };
    let e_name = if str::is_empty(name) {
        unnamed_name(ctx)
    } else {
        name
    };

    let ty = @TInt(kind);
    let ty_def = ctypedef_to_rs(ctx, rust_id(ctx, prefix + e_name), ty);
    let val_ty = cty_to_rs(ctx, ty);
    let mut def = ty_def;

    for items.each |it| {
        let cst = ast::item_const(
            val_ty,
            build::mk_int(ctx.ext_cx, dummy_sp(), it.val)
        );

        let val_def = @{ ident: ctx.ext_cx.ident_of(rust_id(ctx, it.name)),
                         attrs: ~[],
                         id: ctx.ext_cx.next_id(),
                         node: cst,
                         vis: ast::inherited,
                         span: dummy_sp()
                      };

        def.push(val_def);
    }

    return def;
}

fn cvar_to_rs(ctx: &GenCtx, name: ~str, ty: @Type) -> @ast::foreign_item {
    return @{ ident: ctx.ext_cx.ident_of(rust_id(ctx, name)),
              attrs: ~[],
              node: ast::foreign_item_const(cty_to_rs(ctx, ty)),
              id: ctx.ext_cx.next_id(),
              span: dummy_sp(),
              vis: ast::inherited,
           };
}

fn cfunc_to_rs(ctx: &GenCtx, name: ~str, rty: @Type, aty: ~[(~str, @Type)], var: bool) -> @ast::foreign_item {
    let ret = match *rty {
        TVoid => @{
            id: ctx.ext_cx.next_id(),
            node: ast::ty_nil,
            span: dummy_sp()
        },
        _ => cty_to_rs(ctx, rty)
    };

    let mut unnamed = 0;
    let args = do aty.map |arg| {
        let (n, t) = *arg;

        let arg_name = if str::is_empty(n) {
            unnamed += 1;
            fmt!("arg%u", unnamed)
        } else {
            rust_id(ctx, n)
        };

        let arg_ty = cty_to_rs(ctx, t);

        { mode: ast::expl(ast::by_val),
          ty: arg_ty,
          ident: ctx.ext_cx.ident_of(arg_name),
          id: ctx.ext_cx.next_id()
        }
    };

    let decl = ast::foreign_item_fn(
        { inputs: args,
          output: ret,
          cf: ast::return_val
        },
        ast::impure_fn,
        ~[]
    );

    return @{ ident: ctx.ext_cx.ident_of(rust_id(ctx, name)),
              attrs: ~[],
              node: decl,
              id: ctx.ext_cx.next_id(),
              span: dummy_sp(),
              vis: ast::inherited,
           };
}

fn cty_to_rs(ctx: &GenCtx, ty: @Type) -> @ast::ty {
    return match *ty {
        TVoid => mk_ty(ctx, ~"c_void"),
        TInt(i) => match i {
            IBool => mk_ty(ctx, ~"c_int"),
            ISChar => mk_ty(ctx, ~"c_schar"),
            IUChar => mk_ty(ctx, ~"c_uchar"),
            IInt => mk_ty(ctx, ~"c_int"),
            IUInt => mk_ty(ctx, ~"c_uint"),
            IShort => mk_ty(ctx, ~"c_short"),
            IUShort => mk_ty(ctx, ~"c_ushort"),
            ILong => mk_ty(ctx, ~"c_long"),
            IULong => mk_ty(ctx, ~"c_ulong"),
            ILongLong => mk_ty(ctx, ~"c_longlong"),
            IULongLong => mk_ty(ctx, ~"c_ulonglong")
        },
        TFloat(f) => match f {
            FFloat => mk_ty(ctx, ~"c_float"),
            FDouble => mk_ty(ctx, ~"c_double")
        },
        TPtr(t) => mk_ptrty(ctx, cty_to_rs(ctx, t)),
        TArray(t, s) => mk_arrty(ctx, cty_to_rs(ctx, t), s),
        TFunc(_, _, _) => mk_fnty(ctx),
        TNamed(ti) => mk_ty(ctx, rust_id(ctx, ti.name)),
        TComp(ci) => if ci.cstruct {
            mk_ty(ctx, ~"Struct_" + ci.name)
        } else {
            mk_ty(ctx, ~"Union_" + ci.name)
        },
        TEnum(ei) => mk_ty(ctx, ~"Enum_" + ei.name)
    };
}

fn mk_ty(ctx: &GenCtx, name: ~str) -> @ast::ty {
    let ty = ast::ty_path(
        @{ span: dummy_sp(),
           global: false,
           idents: ~[ ctx.ext_cx.ident_of(name) ],
           rp: None,
           types: ~[]
        },
        ctx.ext_cx.next_id()
    );

    return @{ id: ctx.ext_cx.next_id(),
              node: ty,
              span: dummy_sp()
           };
}

fn mk_ptrty(ctx: &GenCtx, base: @ast::ty) -> @ast::ty {
    let ty = ast::ty_ptr({
        ty: base,
        mutbl: ast::m_imm
    });

    return @{ id: ctx.ext_cx.next_id(),
              node: ty,
              span: dummy_sp()
           };
}

fn mk_arrty(ctx: &GenCtx, base: @ast::ty, n: uint) -> @ast::ty {
    let vec = @{
        id: ctx.ext_cx.next_id(),
        node: ast::ty_vec({
            ty: base,
            mutbl: ast::m_imm
        }),
        span: dummy_sp()
    };
    let ty = ast::ty_fixed_length(vec, Some(n));

    return @{ id: ctx.ext_cx.next_id(),
              node: ty,
              span: dummy_sp()
           };
}

fn mk_fnty(ctx: &GenCtx) -> @ast::ty {
    let ty = mk_ptrty(ctx, mk_ty(ctx, ~"u8"));

    return @{ id: ctx.ext_cx.next_id(),
              node: ty.node,
              span: dummy_sp()
           };
}
<commit_msg>fix anonymous struct<commit_after>use io::WriterUtil;
use std::map;
use map::HashMap;

use syntax::ast;
use syntax::ast_util::*;
use syntax::ext::base;
use syntax::ext::build;
use syntax::parse;
use syntax::print::pprust;

use types::*;

struct GenCtx {
    ext_cx: base::ext_ctxt,
    mut unnamed_ty: uint,
    keywords: HashMap<~str, ()>
}

fn rust_id(ctx: &GenCtx, name: ~str) -> ~str {
    if ctx.keywords.contains_key(name) {
        return ~"_" + name;
    }
    return name;
}

fn unnamed_name(ctx: &GenCtx, name: ~str) -> ~str {
    return if str::is_empty(name) {
        ctx.unnamed_ty += 1;
        fmt!("Unnamed%u", ctx.unnamed_ty)
    } else {
        name
    };
}

fn gen_rs(out: io::Writer, link: ~str, globs: ~[Global]) {
    let ctx = GenCtx { ext_cx: base::mk_ctxt(parse::new_parse_sess(None), ~[]),
                       mut unnamed_ty: 0,
                       keywords: syntax::parse::token::keyword_table()
                     };

    let mut fs = ~[];
    let mut vs = ~[];
    let mut gs = ~[];
    for globs.each |g| {
        match *g {
            GOther => {}
            GFunc(_) => fs.push(*g),
            GVar(_) => vs.push(*g),
            _ => gs.push(*g)
        }
    }

    let mut defs = ~[];
    gs = remove_redundent_decl(gs);
    for gs.each |g| {
        match *g {
            GType(ti) => defs += ctypedef_to_rs(&ctx, ti.name, ti.ty),
            GCompDecl(ci) => {
                ci.name = unnamed_name(&ctx, ci.name);
                if ci.cstruct {
                    defs += ctypedef_to_rs(&ctx, ~"Struct_" + ci.name, @TVoid)
                } else {
                    defs += ctypedef_to_rs(&ctx, ~"Union_" + ci.name, @TVoid)
                }
            },
            GComp(ci) => {
                ci.name = unnamed_name(&ctx, ci.name);
                if ci.cstruct {
                    defs.push(cstruct_to_rs(&ctx, ~"Struct_" + ci.name, ci.fields))
                } else {
                    defs += cunion_to_rs(&ctx, ~"Union_" + ci.name, ci.fields)
                }
            },
            GEnumDecl(ei) => {
                ei.name = unnamed_name(&ctx, ei.name);
                defs += ctypedef_to_rs(&ctx, ~"Enum_" + ei.name, @TVoid)
            },
            GEnum(ei) => {
                ei.name = unnamed_name(&ctx, ei.name);
                defs += cenum_to_rs(&ctx, ~"Enum_" + ei.name, ei.items, ei.kind)
            },
            _ => { }
        }
    }

    let vars = do vs.map |v| {
        match *v {
            GVar(vi) => cvar_to_rs(&ctx, vi.name, vi.ty),
            _ => { fail ~"generate global variables" }
        }
    };

    let funcs = do fs.map |f| {
        match *f {
            GFunc(vi) => {
                match *vi.ty {
                    TFunc(rty, aty, var) => cfunc_to_rs(&ctx, vi.name, rty, aty, var),
                    _ => { fail ~"generate functions" }
                }
            },
            _ => { fail ~"generate functions" }
        }
    };

    let views = ~[mk_import(&ctx, ~[~"libc"])];
    defs.push(mk_extern(&ctx, link, vars, funcs));

    let crate = @dummy_spanned({
        directives: ~[],
        module: {
            view_items: views,
            items: defs,
        },
        attrs: ~[],
        config: ~[]
    });

    let ps = pprust::rust_printer(out, ctx.ext_cx.parse_sess().interner);
    out.write_line(~"/* automatically generated by rust-bindgen */\n");
    pprust::print_crate_(ps, crate);
}

fn mk_import(ctx: &GenCtx, path: ~[~str]) -> @ast::view_item {
    let view = ast::view_item_import(~[
        @dummy_spanned(
            ast::view_path_glob(
                @{ span: dummy_sp(),
                   global: false,
                   idents: path.map(|p| ctx.ext_cx.ident_of(*p)),
                   rp: None,
                   types: ~[]
                },
                ctx.ext_cx.next_id()
            )
        )
    ]);

    return @{ node: view,
              attrs: ~[],
              vis: ast::inherited,
              span: dummy_sp()
           };
}

fn mk_extern(ctx: &GenCtx, link: ~str, vars: ~[@ast::foreign_item], funcs: ~[@ast::foreign_item]) -> @ast::item {
    let link_args = dummy_spanned({
        style: ast::attr_outer,
        value: dummy_spanned(
            ast::meta_name_value(
                ~"link_args",
                dummy_spanned(ast::lit_str(@(~"-l"+link)))
            )
        ),
        is_sugared_doc: false
    });

    let ext = ast::item_foreign_mod({
        sort: ast::anonymous,
        view_items: ~[],
        items: vars + funcs
    });

    return @{ ident: ctx.ext_cx.ident_of(~""),
              attrs: ~[link_args],
              id: ctx.ext_cx.next_id(),
              node: ext,
              vis: ast::inherited,
              span: dummy_sp()
           };
}

fn remove_redundent_decl(gs: ~[Global]) -> ~[Global] {
    let typedefs = do gs.filter |g| {
        match(*g) {
            GType(_) => true,
            _ => false
        }
    };

    return do gs.filter |g| {
        !do typedefs.any |t| {
            match (*g, *t) {
                (GComp(ci1), GType(ti)) => match *ti.ty {
                    TComp(ci2) => ptr::ref_eq(ci1, ci2) && str::is_empty(ci1.name),
                    _ => false
                },
                (GEnum(ei1), GType(ti)) => match *ti.ty {
                    TEnum(ei2) => ptr::ref_eq(ei1, ei2) && str::is_empty(ei1.name),
                    _ => false
                },
                _ => false
            }
        }
    };
}

fn ctypedef_to_rs(ctx: &GenCtx, name: ~str, ty: @Type) -> ~[@ast::item] {
    fn mk_item(ctx: &GenCtx, name: ~str, ty: @Type) -> @ast::item {
        let rust_name = rust_id(ctx, name);
        let rust_ty = cty_to_rs(ctx, ty);
        let base = ast::item_ty(
            @{ id: ctx.ext_cx.next_id(),
               node: rust_ty.node,
               span: dummy_sp(),
            },
            ~[]
        );

        return @{ ident: ctx.ext_cx.ident_of(rust_name),
                  attrs: ~[],
                  id: ctx.ext_cx.next_id(),
                  node: base,
                  vis: ast::inherited,
                  span: dummy_sp()
               };
    }

    return match *ty {
        TComp(ci) => if str::is_empty(ci.name) {
            ci.name = name;
            if ci.cstruct {
                ~[cstruct_to_rs(ctx, name, ci.fields)]
            } else {
                cunion_to_rs(ctx, name, ci.fields)
            }
        } else {
            ~[mk_item(ctx, name, ty)]
        },
        TEnum(ei) => if str::is_empty(ei.name) {
            ei.name = name;
            cenum_to_rs(ctx, name, ei.items, ei.kind)
        } else {
            ~[mk_item(ctx, name, ty)]
        },
        _ => ~[mk_item(ctx, name, ty)]
    }
}

fn cstruct_to_rs(ctx: &GenCtx, name: ~str, fields: ~[@FieldInfo]) -> @ast::item {
    let mut unnamed = 0;
    let fs = do fields.map | f| {
        let f_name = if str::is_empty(f.name) {
            unnamed += 1;
            fmt!("unnamed_field%u", unnamed)
        } else {
            rust_id(ctx, f.name)
        };

        let f_ty = cty_to_rs(ctx, f.ty);

        @dummy_spanned({
            kind: ast::named_field(
                ctx.ext_cx.ident_of(f_name),
                ast::class_immutable,
                ast::inherited
            ),
            id: ctx.ext_cx.next_id(),
            ty: f_ty
        })
    };

    let def = ast::item_class(
        @{ traits: ~[],
           fields: fs,
           methods: ~[],
           dtor: None
        },
        ~[]
    );

    return @{ ident: ctx.ext_cx.ident_of(rust_id(ctx, name)),
              attrs: ~[],
              id: ctx.ext_cx.next_id(),
              node: def,
              vis: ast::inherited,
              span: dummy_sp()
           };
}

fn cunion_to_rs(ctx: &GenCtx, name: ~str, _fields: ~[@FieldInfo]) -> ~[@ast::item] {
    return ctypedef_to_rs(ctx, rust_id(ctx, name), @TVoid);
}

fn cenum_to_rs(ctx: &GenCtx, name: ~str, items: ~[@EnumItem], kind: IKind) -> ~[@ast::item] {
    let ty = @TInt(kind);
    let ty_def = ctypedef_to_rs(ctx, rust_id(ctx, name), ty);
    let val_ty = cty_to_rs(ctx, ty);
    let mut def = ty_def;

    for items.each |it| {
        let cst = ast::item_const(
            val_ty,
            build::mk_int(ctx.ext_cx, dummy_sp(), it.val)
        );

        let val_def = @{ ident: ctx.ext_cx.ident_of(rust_id(ctx, it.name)),
                         attrs: ~[],
                         id: ctx.ext_cx.next_id(),
                         node: cst,
                         vis: ast::inherited,
                         span: dummy_sp()
                      };

        def.push(val_def);
    }

    return def;
}

fn cvar_to_rs(ctx: &GenCtx, name: ~str, ty: @Type) -> @ast::foreign_item {
    return @{ ident: ctx.ext_cx.ident_of(rust_id(ctx, name)),
              attrs: ~[],
              node: ast::foreign_item_const(cty_to_rs(ctx, ty)),
              id: ctx.ext_cx.next_id(),
              span: dummy_sp(),
              vis: ast::inherited,
           };
}

fn cfunc_to_rs(ctx: &GenCtx, name: ~str, rty: @Type, aty: ~[(~str, @Type)], var: bool) -> @ast::foreign_item {
    let ret = match *rty {
        TVoid => @{
            id: ctx.ext_cx.next_id(),
            node: ast::ty_nil,
            span: dummy_sp()
        },
        _ => cty_to_rs(ctx, rty)
    };

    let mut unnamed = 0;
    let args = do aty.map |arg| {
        let (n, t) = *arg;

        let arg_name = if str::is_empty(n) {
            unnamed += 1;
            fmt!("arg%u", unnamed)
        } else {
            rust_id(ctx, n)
        };

        let arg_ty = cty_to_rs(ctx, t);

        { mode: ast::expl(ast::by_val),
          ty: arg_ty,
          ident: ctx.ext_cx.ident_of(arg_name),
          id: ctx.ext_cx.next_id()
        }
    };

    let decl = ast::foreign_item_fn(
        { inputs: args,
          output: ret,
          cf: ast::return_val
        },
        ast::impure_fn,
        ~[]
    );

    return @{ ident: ctx.ext_cx.ident_of(rust_id(ctx, name)),
              attrs: ~[],
              node: decl,
              id: ctx.ext_cx.next_id(),
              span: dummy_sp(),
              vis: ast::inherited,
           };
}

fn cty_to_rs(ctx: &GenCtx, ty: @Type) -> @ast::ty {
    return match *ty {
        TVoid => mk_ty(ctx, ~"c_void"),
        TInt(i) => match i {
            IBool => mk_ty(ctx, ~"c_int"),
            ISChar => mk_ty(ctx, ~"c_schar"),
            IUChar => mk_ty(ctx, ~"c_uchar"),
            IInt => mk_ty(ctx, ~"c_int"),
            IUInt => mk_ty(ctx, ~"c_uint"),
            IShort => mk_ty(ctx, ~"c_short"),
            IUShort => mk_ty(ctx, ~"c_ushort"),
            ILong => mk_ty(ctx, ~"c_long"),
            IULong => mk_ty(ctx, ~"c_ulong"),
            ILongLong => mk_ty(ctx, ~"c_longlong"),
            IULongLong => mk_ty(ctx, ~"c_ulonglong")
        },
        TFloat(f) => match f {
            FFloat => mk_ty(ctx, ~"c_float"),
            FDouble => mk_ty(ctx, ~"c_double")
        },
        TPtr(t) => mk_ptrty(ctx, cty_to_rs(ctx, t)),
        TArray(t, s) => mk_arrty(ctx, cty_to_rs(ctx, t), s),
        TFunc(_, _, _) => mk_fnty(ctx),
        TNamed(ti) => mk_ty(ctx, rust_id(ctx, ti.name)),
        TComp(ci) => {
            ci.name = unnamed_name(ctx, ci.name);
            if ci.cstruct {
                mk_ty(ctx, ~"Struct_" + ci.name)
            } else {
                mk_ty(ctx, ~"Union_" + ci.name)
            }
        },
        TEnum(ei) => {
            ei.name = unnamed_name(ctx, ei.name);
            mk_ty(ctx, ~"Enum_" + ei.name)
        }
    };
}

fn mk_ty(ctx: &GenCtx, name: ~str) -> @ast::ty {
    let ty = ast::ty_path(
        @{ span: dummy_sp(),
           global: false,
           idents: ~[ ctx.ext_cx.ident_of(name) ],
           rp: None,
           types: ~[]
        },
        ctx.ext_cx.next_id()
    );

    return @{ id: ctx.ext_cx.next_id(),
              node: ty,
              span: dummy_sp()
           };
}

fn mk_ptrty(ctx: &GenCtx, base: @ast::ty) -> @ast::ty {
    let ty = ast::ty_ptr({
        ty: base,
        mutbl: ast::m_imm
    });

    return @{ id: ctx.ext_cx.next_id(),
              node: ty,
              span: dummy_sp()
           };
}

fn mk_arrty(ctx: &GenCtx, base: @ast::ty, n: uint) -> @ast::ty {
    let vec = @{
        id: ctx.ext_cx.next_id(),
        node: ast::ty_vec({
            ty: base,
            mutbl: ast::m_imm
        }),
        span: dummy_sp()
    };
    let ty = ast::ty_fixed_length(vec, Some(n));

    return @{ id: ctx.ext_cx.next_id(),
              node: ty,
              span: dummy_sp()
           };
}

fn mk_fnty(ctx: &GenCtx) -> @ast::ty {
    let ty = mk_ptrty(ctx, mk_ty(ctx, ~"u8"));

    return @{ id: ctx.ext_cx.next_id(),
              node: ty.node,
              span: dummy_sp()
           };
}
<|endoftext|>