Bug 1346665 - P2: Import rustified cubeb_pulse.c. r=kinetik,rillian

Enable by setting:

  media.cubeb.backend = "pulse-rust"

MozReview-Commit-ID: EZXJAzN3X7c
This commit is contained in:
Dan Glastonbury 2017-03-13 09:52:45 +10:00
parent 709bb40008
commit 92a65a96fb
61 changed files with 9238 additions and 0 deletions

View File

@ -0,0 +1 @@
Dan Glastonbury <dglastonbury@mozilla.com>

View File

@ -0,0 +1,13 @@
[package]
name = "cubeb-pulse"
version = "0.0.1"
authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
description = "Cubeb backed for PulseAudio written in Rust"
[features]
pulse-dlopen = ["pulse-ffi/dlopen"]
[dependencies]
cubeb-ffi = { path = "cubeb-ffi" }
pulse-ffi = { path = "pulse-ffi" }
semver = "^0.6"

View File

@ -0,0 +1,13 @@
Copyright © 2011 Mozilla Foundation
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -0,0 +1,5 @@
# cubeb-pulse-rs
Implementation of PulseAudio backend for Cubeb written in Rust.
[![Travis Build Status](https://travis-ci.org/djg/cubeb-pulse-rs.svg?branch=master)](https://travis-ci.org/djg/cubeb-pulse-rs)

View File

@ -0,0 +1,8 @@
The source from this directory was copied from the cubeb-pulse-rs
git repository using the update.sh script. The only changes
made were those applied by update.sh and the addition of
Makefile.in build files for the Mozilla build system.
The cubeb-pulse-rs git repository is: https://github.com/djg/cubeb-pulse-rs.git
The git commit ID used was faa1dcf3a061144c1f7edee76f23691eabd1f436 (2017-04-20 11:23:09 +1000)

View File

@ -0,0 +1,8 @@
[package]
name = "cubeb-ffi"
version = "0.0.1"
authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
description = "FFI bindings for implementing cubeb backends"
[dependencies]
bitflags = "^0.7.0"

View File

@ -0,0 +1,6 @@
pub const OK: i32 = 0;
pub const ERROR: i32 = -1;
pub const ERROR_INVALID_FORMAT: i32 = -2;
pub const ERROR_INVALID_PARAMETER: i32 = -3;
pub const ERROR_NOT_SUPPORTED: i32 = -4;
pub const ERROR_DEVICE_UNAVAILABLE: i32 = -5;

View File

@ -0,0 +1,520 @@
// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use std::default::Default;
use std::os::raw::{c_char, c_long, c_void};
use std::ptr;
pub enum Context {}
pub enum Stream {}
// TODO endian check
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SampleFormat(i32);
// These need to match cubeb_sample_format
pub const SAMPLE_S16LE: SampleFormat = SampleFormat(0);
pub const SAMPLE_S16BE: SampleFormat = SampleFormat(1);
pub const SAMPLE_FLOAT32LE: SampleFormat = SampleFormat(2);
pub const SAMPLE_FLOAT32BE: SampleFormat = SampleFormat(3);
#[cfg(target_endian = "little")]
pub const SAMPLE_S16NE: SampleFormat = SAMPLE_S16LE;
#[cfg(target_endian = "little")]
pub const SAMPLE_FLOAT32NE: SampleFormat = SAMPLE_FLOAT32LE;
#[cfg(target_endian = "big")]
pub const SAMPLE_S16NE: SampleFormat = SAMPLE_S16BE;
#[cfg(target_endian = "big")]
pub const SAMPLE_FLOAT32NE: SampleFormat = SAMPLE_FLOAT32BE;
pub type DeviceId = *const c_void;
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ChannelLayout(i32);
// These need to match cubeb_channel_layout
pub const LAYOUT_UNDEFINED: ChannelLayout = ChannelLayout(0);
pub const LAYOUT_DUAL_MONO: ChannelLayout = ChannelLayout(1);
pub const LAYOUT_DUAL_MONO_LFE: ChannelLayout = ChannelLayout(2);
pub const LAYOUT_MONO: ChannelLayout = ChannelLayout(3);
pub const LAYOUT_MONO_LFE: ChannelLayout = ChannelLayout(4);
pub const LAYOUT_STEREO: ChannelLayout = ChannelLayout(5);
pub const LAYOUT_STEREO_LFE: ChannelLayout = ChannelLayout(6);
pub const LAYOUT_3F: ChannelLayout = ChannelLayout(7);
pub const LAYOUT_3F_LFE: ChannelLayout = ChannelLayout(8);
pub const LAYOUT_2F1: ChannelLayout = ChannelLayout(9);
pub const LAYOUT_2F1_LFE: ChannelLayout = ChannelLayout(10);
pub const LAYOUT_3F1: ChannelLayout = ChannelLayout(11);
pub const LAYOUT_3F1_LFE: ChannelLayout = ChannelLayout(12);
pub const LAYOUT_2F2: ChannelLayout = ChannelLayout(13);
pub const LAYOUT_2F2_LFE: ChannelLayout = ChannelLayout(14);
pub const LAYOUT_3F2: ChannelLayout = ChannelLayout(15);
pub const LAYOUT_3F2_LFE: ChannelLayout = ChannelLayout(16);
pub const LAYOUT_3F3R_LFE: ChannelLayout = ChannelLayout(17);
pub const LAYOUT_3F4_LFE: ChannelLayout = ChannelLayout(18);
pub const LAYOUT_MAX: ChannelLayout = ChannelLayout(19);
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct StreamParams {
pub format: SampleFormat,
pub rate: u32,
pub channels: u32,
pub layout: ChannelLayout,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Device {
pub output_name: *mut c_char,
pub input_name: *mut c_char,
}
impl Default for Device {
fn default() -> Self {
Device {
output_name: ptr::null_mut(),
input_name: ptr::null_mut(),
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct State(i32);
// These need to match cubeb_state
pub const STATE_STARTED: State = State(0);
pub const STATE_STOPPED: State = State(1);
pub const STATE_DRAINED: State = State(2);
pub const STATE_ERROR: State = State(3);
pub const OK: i32 = 0;
pub const ERROR: i32 = -1;
pub const ERROR_INVALID_FORMAT: i32 = -2;
pub const ERROR_INVALID_PARAMETER: i32 = -3;
pub const ERROR_NOT_SUPPORTED: i32 = -4;
pub const ERROR_DEVICE_UNAVAILABLE: i32 = -5;
// These need to match cubeb_device_type
bitflags! {
#[repr(C)]
pub flags DeviceType : u32 {
const DEVICE_TYPE_UNKNOWN = 0b00,
const DEVICE_TYPE_INPUT = 0b01,
const DEVICE_TYPE_OUTPUT = 0b10,
const DEVICE_TYPE_ALL = 0b11,
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DeviceState {
Disabled = 0,
Unplugged = 1,
Enabled = 2,
}
// These need to match cubeb_device_fmt
bitflags! {
#[repr(C)]
pub flags DeviceFmt: u32 {
const DEVICE_FMT_S16LE = 0x0010,
const DEVICE_FMT_S16BE = 0x0020,
const DEVICE_FMT_F32LE = 0x1000,
const DEVICE_FMT_F32BE = 0x2000,
const DEVICE_FMT_S16_MASK = DEVICE_FMT_S16LE.bits | DEVICE_FMT_S16BE.bits,
const DEVICE_FMT_F32_MASK = DEVICE_FMT_F32LE.bits | DEVICE_FMT_F32BE.bits,
const DEVICE_FMT_ALL = DEVICE_FMT_S16_MASK.bits | DEVICE_FMT_F32_MASK.bits,
}
}
// Ideally these would be defined as part of `flags DeviceFmt` but
// that doesn't work with current bitflags crate.
#[cfg(target_endian = "little")]
pub const CUBEB_FMT_S16NE: DeviceFmt = DEVICE_FMT_S16LE;
#[cfg(target_endian = "little")]
pub const CUBEB_FMT_F32NE: DeviceFmt = DEVICE_FMT_F32LE;
#[cfg(target_endian = "big")]
pub const CUBEB_FMT_S16NE: DeviceFmt = DEVICE_FMT_S16BE;
#[cfg(target_endian = "big")]
pub const CUBEB_FMT_F32NE: DeviceFmt = DEVICE_FMT_F32BE;
// These need to match cubeb_device_pref
bitflags! {
#[repr(C)]
pub flags DevicePref : u32 {
const DEVICE_PREF_MULTIMEDIA = 0x1,
const DEVICE_PREF_VOICE = 0x2,
const DEVICE_PREF_NOTIFICATION = 0x4,
const DEVICE_PREF_ALL = 0xF
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct DeviceInfo {
pub devid: DeviceId,
pub device_id: *const c_char,
pub friendly_name: *const c_char,
pub group_id: *const c_char,
pub vendor_name: *const c_char,
pub devtype: DeviceType,
pub state: DeviceState,
pub preferred: DevicePref,
pub format: DeviceFmt,
pub default_format: DeviceFmt,
pub max_channels: u32,
pub default_rate: u32,
pub max_rate: u32,
pub min_rate: u32,
pub latency_lo: u32,
pub latency_hi: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct DeviceCollection {
/// Device count in collection.
pub count: u32,
/// Array of pointers to device info.
pub device: [*const DeviceInfo; 0],
}
pub type DataCallback = Option<unsafe extern "C" fn(stream: *mut Stream,
user_ptr: *mut c_void,
input_buffer: *const c_void,
output_buffer: *mut c_void,
nframes: c_long)
-> c_long>;
pub type StateCallback = Option<unsafe extern "C" fn(stream: *mut Stream, user_ptr: *mut c_void, state: State)>;
pub type DeviceChangedCallback = Option<unsafe extern "C" fn(user_ptr: *mut c_void)>;
pub type DeviceCollectionChangedCallback = Option<unsafe extern "C" fn(context: *mut Context, user_ptr: *mut c_void)>;
pub type StreamInitFn = Option<unsafe extern "C" fn(context: *mut Context,
stream: *mut *mut Stream,
stream_name: *const c_char,
input_device: DeviceId,
input_stream_params: *mut StreamParams,
output_device: DeviceId,
output_stream_params: *mut StreamParams,
latency: u32,
data_callback: DataCallback,
state_callback: StateCallback,
user_ptr: *mut c_void)
-> i32>;
pub type RegisterDeviceCollectionChangedFn = Option<unsafe extern "C" fn(context: *mut Context,
devtype: DeviceType,
callback: DeviceCollectionChangedCallback,
user_ptr: *mut c_void)
-> i32>;
#[repr(C)]
pub struct Ops {
pub init: Option<unsafe extern "C" fn(context: *mut *mut Context, context_name: *const c_char) -> i32>,
pub get_backend_id: Option<unsafe extern "C" fn(context: *mut Context) -> *const c_char>,
pub get_max_channel_count: Option<unsafe extern "C" fn(context: *mut Context, max_channels: *mut u32) -> i32>,
pub get_min_latency: Option<unsafe extern "C" fn(context: *mut Context,
params: StreamParams,
latency_ms: *mut u32)
-> i32>,
pub get_preferred_sample_rate: Option<unsafe extern "C" fn(context: *mut Context, rate: *mut u32) -> i32>,
pub get_preferred_channel_layout:
Option<unsafe extern "C" fn(context: *mut Context, layout: *mut ChannelLayout) -> i32>,
pub enumerate_devices: Option<unsafe extern "C" fn(context: *mut Context,
devtype: DeviceType,
collection: *mut *mut DeviceCollection)
-> i32>,
pub destroy: Option<unsafe extern "C" fn(context: *mut Context)>,
pub stream_init: StreamInitFn,
pub stream_destroy: Option<unsafe extern "C" fn(stream: *mut Stream)>,
pub stream_start: Option<unsafe extern "C" fn(stream: *mut Stream) -> i32>,
pub stream_stop: Option<unsafe extern "C" fn(stream: *mut Stream) -> i32>,
pub stream_get_position: Option<unsafe extern "C" fn(stream: *mut Stream, position: *mut u64) -> i32>,
pub stream_get_latency: Option<unsafe extern "C" fn(stream: *mut Stream, latency: *mut u32) -> i32>,
pub stream_set_volume: Option<unsafe extern "C" fn(stream: *mut Stream, volumes: f32) -> i32>,
pub stream_set_panning: Option<unsafe extern "C" fn(stream: *mut Stream, panning: f32) -> i32>,
pub stream_get_current_device: Option<unsafe extern "C" fn(stream: *mut Stream, device: *mut *const Device) -> i32>,
pub stream_device_destroy: Option<unsafe extern "C" fn(stream: *mut Stream, device: *mut Device) -> i32>,
pub stream_register_device_changed_callback:
Option<unsafe extern "C" fn(stream: *mut Stream,
device_changed_callback: DeviceChangedCallback)
-> i32>,
pub register_device_collection_changed: RegisterDeviceCollectionChangedFn,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct LayoutMap {
pub name: *const c_char,
pub channels: u32,
pub layout: ChannelLayout,
}
// cubeb_mixer.h
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Channel(i32);
impl Into<i32> for Channel {
fn into(self) -> i32 {
self.0
}
}
// These need to match cubeb_channel
pub const CHANNEL_INVALID: Channel = Channel(-1);
pub const CHANNEL_MONO: Channel = Channel(0);
pub const CHANNEL_LEFT: Channel = Channel(1);
pub const CHANNEL_RIGHT: Channel = Channel(2);
pub const CHANNEL_CENTER: Channel = Channel(3);
pub const CHANNEL_LS: Channel = Channel(4);
pub const CHANNEL_RS: Channel = Channel(5);
pub const CHANNEL_RLS: Channel = Channel(6);
pub const CHANNEL_RCENTER: Channel = Channel(7);
pub const CHANNEL_RRS: Channel = Channel(8);
pub const CHANNEL_LFE: Channel = Channel(9);
pub const CHANNEL_MAX: Channel = Channel(10);
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct ChannelMap {
pub channels: u32,
pub map: [Channel; 10],
}
impl ::std::default::Default for ChannelMap {
fn default() -> Self {
ChannelMap {
channels: 0,
map: [CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID,
CHANNEL_INVALID],
}
}
}
extern "C" {
pub fn cubeb_channel_map_to_layout(channel_map: *const ChannelMap) -> ChannelLayout;
pub fn cubeb_should_upmix(stream: *const StreamParams, mixer: *const StreamParams) -> bool;
pub fn cubeb_should_downmix(stream: *const StreamParams, mixer: *const StreamParams) -> bool;
pub fn cubeb_downmix_float(input: *const f32,
inframes: c_long,
output: *mut f32,
in_channels: u32,
out_channels: u32,
in_layout: ChannelLayout,
out_layout: ChannelLayout);
pub fn cubeb_upmix_float(input: *const f32,
inframes: c_long,
output: *mut f32,
in_channels: u32,
out_channels: u32);
}
#[test]
fn bindgen_test_layout_stream_params() {
assert_eq!(::std::mem::size_of::<StreamParams>(),
16usize,
concat!("Size of: ", stringify!(StreamParams)));
assert_eq!(::std::mem::align_of::<StreamParams>(),
4usize,
concat!("Alignment of ", stringify!(StreamParams)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).format as *const _ as usize },
0usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(format)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).rate as *const _ as usize },
4usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(rate)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).channels as *const _ as usize },
8usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(channels)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).layout as *const _ as usize },
12usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(layout)));
}
#[test]
fn bindgen_test_layout_cubeb_device() {
assert_eq!(::std::mem::size_of::<Device>(),
16usize,
concat!("Size of: ", stringify!(Device)));
assert_eq!(::std::mem::align_of::<Device>(),
8usize,
concat!("Alignment of ", stringify!(Device)));
assert_eq!(unsafe { &(*(0 as *const Device)).output_name as *const _ as usize },
0usize,
concat!("Alignment of field: ",
stringify!(Device),
"::",
stringify!(output_name)));
assert_eq!(unsafe { &(*(0 as *const Device)).input_name as *const _ as usize },
8usize,
concat!("Alignment of field: ",
stringify!(Device),
"::",
stringify!(input_name)));
}
#[test]
fn bindgen_test_layout_cubeb_device_info() {
assert_eq!(::std::mem::size_of::<DeviceInfo>(),
88usize,
concat!("Size of: ", stringify!(DeviceInfo)));
assert_eq!(::std::mem::align_of::<DeviceInfo>(),
8usize,
concat!("Alignment of ", stringify!(DeviceInfo)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).devid as *const _ as usize },
0usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(devid)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).device_id as *const _ as usize },
8usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(device_id)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).friendly_name as *const _ as usize },
16usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(friendly_name)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).group_id as *const _ as usize },
24usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(group_id)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).vendor_name as *const _ as usize },
32usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(vendor_name)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).devtype as *const _ as usize },
40usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(type_)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).state as *const _ as usize },
44usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(state)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).preferred as *const _ as usize },
48usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(preferred)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).format as *const _ as usize },
52usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(format)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).default_format as *const _ as usize },
56usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(default_format)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).max_channels as *const _ as usize },
60usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(max_channels)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).default_rate as *const _ as usize },
64usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(default_rate)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).max_rate as *const _ as usize },
68usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(max_rate)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).min_rate as *const _ as usize },
72usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(min_rate)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).latency_lo as *const _ as usize },
76usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(latency_lo)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).latency_hi as *const _ as usize },
80usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(latency_hi)));
}
#[test]
fn bindgen_test_layout_cubeb_device_collection() {
assert_eq!(::std::mem::size_of::<DeviceCollection>(),
16usize,
concat!("Size of: ", stringify!(DeviceCollection)));
assert_eq!(::std::mem::align_of::<DeviceCollection>(),
8usize,
concat!("Alignment of ", stringify!(DeviceCollection)));
assert_eq!(unsafe { &(*(0 as *const DeviceCollection)).count as *const _ as usize },
0usize,
concat!("Alignment of field: ",
stringify!(DeviceCollection),
"::",
stringify!(count)));
assert_eq!(unsafe { &(*(0 as *const DeviceCollection)).device as *const _ as usize },
8usize,
concat!("Alignment of field: ",
stringify!(DeviceCollection),
"::",
stringify!(device)));
}
#[test]
fn test_normal_logging() {
log!("This is log at normal level");
log!("This is {} at normal level", "log with param");
}
#[test]
fn test_verbose_logging() {
logv!("This is a log at verbose level");
logv!("This is {} at verbose level", "log with param");
}

View File

@ -0,0 +1,34 @@
use libc::c_void;
use *;
#[repr(C)]
#[derive(Clone,Copy,Debug)]
pub struct LayoutMap {
pub name: *const i8,
pub channels: u32,
pub layout: ChannelLayout
}
#[repr(C)]
pub struct Ops {
pub init: Option<unsafe extern "C" fn(context: *mut *mut Context, context_name: *const i8) -> i32>,
pub get_backend_id: Option<unsafe extern "C" fn(context: *mut Context) -> *const i8>,
pub get_max_channel_count: Option<unsafe extern "C" fn(context: *mut Context, max_channels: *mut u32) -> i32>,
pub get_min_latency: Option<unsafe extern "C" fn(context: *mut Context, params: StreamParams, latency_ms: *mut u32) -> i32>,
pub get_preferred_sample_rate: Option<unsafe extern "C" fn(context: *mut Context, rate: *mut u32) -> i32>,
pub get_preferred_channel_layout: Option<unsafe extern "C" fn(context: *mut Context, layout: *mut ChannelLayout) -> i32>,
pub enumerate_devices: Option<unsafe extern "C" fn(context: *mut Context, devtype: DeviceType, collection: *mut *mut DeviceCollection) -> i32>,
pub destroy: Option<unsafe extern "C" fn(context: *mut Context)>,
pub stream_init: Option<unsafe extern "C" fn(context: *mut Context, stream: *mut *mut Stream, stream_name: *const i8, input_device: DeviceId, input_stream_params: *mut StreamParams, output_device: DeviceId, output_stream_params: *mut StreamParams, latency: u32, data_callback: DataCallback, state_callback: StateCallback, user_ptr: *mut c_void) -> i32>,
pub stream_destroy: Option<unsafe extern "C" fn(stream: *mut Stream)>,
pub stream_start: Option<unsafe extern "C" fn(stream: *mut Stream) -> i32>,
pub stream_stop: Option<unsafe extern "C" fn(stream: *mut Stream) -> i32>,
pub stream_get_position: Option<unsafe extern "C" fn(stream: *mut Stream, position: *mut u64) -> i32>,
pub stream_get_latency: Option<unsafe extern "C" fn(stream: *mut Stream, latency: *mut u32) -> i32>,
pub stream_set_volume: Option<unsafe extern "C" fn(stream: *mut Stream, volumes: f32) -> i32>,
pub stream_set_panning: Option<unsafe extern "C" fn(stream: *mut Stream, panning: f32)-> i32>,
pub stream_get_current_device: Option<unsafe extern "C" fn(stream: *mut Stream, device: *mut *const Device) -> i32>,
pub stream_device_destroy: Option<unsafe extern "C" fn(stream: *mut Stream, device: *mut Device) -> i32>,
pub stream_register_device_changed_callback: Option<unsafe extern "C" fn(stream: *mut Stream, device_changed_callback: DeviceChangedCallback) -> i32>,
pub register_device_collection_changed: Option<unsafe extern "C" fn(context: *mut Context, devtype: DeviceType, callback: DeviceCollectionChangedCallback, user_ptr: *mut c_void) -> i32>
}

View File

@ -0,0 +1,14 @@
// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
#[macro_use]
extern crate bitflags;
mod ffi;
mod log;
pub mod mixer;
pub use ffi::*;
pub use log::*;

View File

@ -0,0 +1,66 @@
use std::os::raw::c_char;
#[macro_export]
macro_rules! log_internal {
($level: expr, $msg: expr) => {
#[allow(unused_unsafe)]
unsafe {
if $level <= $crate::g_cubeb_log_level {
if let Some(log_callback) = $crate::g_cubeb_log_callback {
let cstr = ::std::ffi::CString::new(concat!("%s:%d: ", $msg, "\n")).unwrap();
log_callback(cstr.as_ptr(), file!(), line!());
}
}
}
};
($level: expr, $fmt: expr, $($arg:tt)+) => {
#[allow(unused_unsafe)]
unsafe {
if $level <= $crate::g_cubeb_log_level {
if let Some(log_callback) = $crate::g_cubeb_log_callback {
let cstr = ::std::ffi::CString::new(concat!("%s:%d: ", $fmt, "\n")).unwrap();
log_callback(cstr.as_ptr(), file!(), line!(), $($arg)+);
}
}
}
}
}
#[macro_export]
macro_rules! logv {
($msg: expr) => (log_internal!($crate::LogLevel::Verbose, $msg));
($fmt: expr, $($arg: tt)+) => (log_internal!($crate::LogLevel::Verbose, $fmt, $($arg)*));
}
#[macro_export]
macro_rules! log {
($msg: expr) => (log_internal!($crate::LogLevel::Normal, $msg));
($fmt: expr, $($arg: tt)+) => (log_internal!($crate::LogLevel::Normal, $fmt, $($arg)*));
}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LogLevel {
Disabled = 0,
Normal = 1,
Verbose = 2,
}
pub type LogCallback = Option<unsafe extern "C" fn(fmt: *const c_char, ...)>;
extern "C" {
pub static g_cubeb_log_level: LogLevel;
pub static g_cubeb_log_callback: LogCallback;
}
#[test]
fn test_normal_logging() {
log!("This is log at normal level");
log!("Formatted log %d", 1);
}
#[test]
fn test_verbose_logging() {
logv!("This is a log at verbose level");
logv!("Formatted log %d", 1);
}

View File

@ -0,0 +1,80 @@
// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use ::*;
static CHANNEL_LAYOUT_UNDEFINED: &'static [Channel] = &[CHANNEL_INVALID];
static CHANNEL_LAYOUT_DUAL_MONO: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT];
static CHANNEL_LAYOUT_DUAL_MONO_LFE: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE];
static CHANNEL_LAYOUT_MONO: &'static [Channel] = &[CHANNEL_MONO];
static CHANNEL_LAYOUT_MONO_LFE: &'static [Channel] = &[CHANNEL_MONO, CHANNEL_LFE];
static CHANNEL_LAYOUT_STEREO: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT];
static CHANNEL_LAYOUT_STEREO_LFE: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE];
static CHANNEL_LAYOUT_3F: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER];
static CHANNEL_LAYOUT_3FLFE: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE];
static CHANNEL_LAYOUT_2F1: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_RCENTER];
static CHANNEL_LAYOUT_2F1LFE: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE, CHANNEL_RCENTER];
static CHANNEL_LAYOUT_3F1: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_RCENTER];
static CHANNEL_LAYOUT_3F1LFE: &'static [Channel] = &[CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LFE,
CHANNEL_RCENTER];
static CHANNEL_LAYOUT_2F2: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LS, CHANNEL_RS];
static CHANNEL_LAYOUT_2F2LFE: &'static [Channel] = &[CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_LFE,
CHANNEL_LS,
CHANNEL_RS];
static CHANNEL_LAYOUT_3F2: &'static [Channel] = &[CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LS,
CHANNEL_RS];
static CHANNEL_LAYOUT_3F2LFE: &'static [Channel] = &[CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LFE,
CHANNEL_LS,
CHANNEL_RS];
static CHANNEL_LAYOUT_3F3RLFE: &'static [Channel] = &[CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LFE,
CHANNEL_RCENTER,
CHANNEL_LS,
CHANNEL_RS];
static CHANNEL_LAYOUT_3F4LFE: &'static [Channel] = &[CHANNEL_LEFT,
CHANNEL_RIGHT,
CHANNEL_CENTER,
CHANNEL_LFE,
CHANNEL_RLS,
CHANNEL_RRS,
CHANNEL_LS,
CHANNEL_RS];
pub fn channel_index_to_order(layout: ChannelLayout) -> &'static [Channel] {
match layout {
LAYOUT_DUAL_MONO => CHANNEL_LAYOUT_DUAL_MONO,
LAYOUT_DUAL_MONO_LFE => CHANNEL_LAYOUT_DUAL_MONO_LFE,
LAYOUT_MONO => CHANNEL_LAYOUT_MONO,
LAYOUT_MONO_LFE => CHANNEL_LAYOUT_MONO_LFE,
LAYOUT_STEREO => CHANNEL_LAYOUT_STEREO,
LAYOUT_STEREO_LFE => CHANNEL_LAYOUT_STEREO_LFE,
LAYOUT_3F => CHANNEL_LAYOUT_3F,
LAYOUT_3F_LFE => CHANNEL_LAYOUT_3FLFE,
LAYOUT_2F1 => CHANNEL_LAYOUT_2F1,
LAYOUT_2F1_LFE => CHANNEL_LAYOUT_2F1LFE,
LAYOUT_3F1 => CHANNEL_LAYOUT_3F1,
LAYOUT_3F1_LFE => CHANNEL_LAYOUT_3F1LFE,
LAYOUT_2F2 => CHANNEL_LAYOUT_2F2,
LAYOUT_2F2_LFE => CHANNEL_LAYOUT_2F2LFE,
LAYOUT_3F2 => CHANNEL_LAYOUT_3F2,
LAYOUT_3F2_LFE => CHANNEL_LAYOUT_3F2LFE,
LAYOUT_3F3R_LFE => CHANNEL_LAYOUT_3F3RLFE,
LAYOUT_3F4_LFE => CHANNEL_LAYOUT_3F4LFE,
_ => CHANNEL_LAYOUT_UNDEFINED,
}
}

View File

@ -0,0 +1,89 @@
use std::ops;
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct DeviceFmt(u32);
const DEVICE_FMT_S16LE: u32 = 0x0010;
const DEVICE_FMT_S16BE: u32 = 0x0020;
const DEVICE_FMT_F32LE: u32 = 0x1000;
const DEVICE_FMT_F32BE: u32 = 0x2000;
const DEVICE_FMT_S16_MASK: u32 = DEVICE_FMT_S16LE | DEVICE_FMT_S16BE;
const DEVICE_FMT_F32_MASK: u32 = DEVICE_FMT_F32LE | DEVICE_FMT_F32BE;
const DEVICE_FMT_ALL: u32 = DEVICE_FMT_S16_MASK | DEVICE_FMT_F32_MASK;
impl DeviceFmt {
pub fn empty() -> Self { DeviceFmt(0) }
#[inline] pub fn s16le() -> Self { DeviceFmt(DEVICE_FMT_S16LE) }
#[inline] pub fn s16be() -> Self { DeviceFmt(DEVICE_FMT_S16BE) }
#[inline] pub fn f32le() -> Self { DeviceFmt(DEVICE_FMT_F32LE) }
#[inline] pub fn f32be() -> Self { DeviceFmt(DEVICE_FMT_F32BE) }
#[inline] pub fn all() -> Self { DeviceFmt(DEVICE_FMT_ALL) }
#[inline] pub fn s16ne() -> Self {
if cfg!(target_endian = "little") {
DeviceFmt::s16le()
} else {
DeviceFmt::s16be()
}
}
#[inline] pub fn f32ne() -> Self {
if cfg!(target_endian = "little") {
DeviceFmt::f32le()
} else {
DeviceFmt::f32be()
}
}
#[inline] pub fn contains(&self, other: Self) -> bool { (*self & other) == other }
#[inline] pub fn insert(&mut self, other: Self) { self.0 |= other.0; }
#[inline] pub fn remove(&mut self, other: Self) { self.0 &= !other.0; }
}
impl ops::BitOr for DeviceFmt {
type Output = DeviceFmt;
#[inline]
fn bitor(self, other: Self) -> Self {
DeviceFmt(self.0 | other.0)
}
}
impl ops::BitXor for DeviceFmt {
type Output = DeviceFmt;
#[inline]
fn bitxor(self, other: Self) -> Self {
DeviceFmt(self.0 ^ other.0)
}
}
impl ops::BitAnd for DeviceFmt {
type Output = DeviceFmt;
#[inline]
fn bitand(self, other: Self) -> Self {
DeviceFmt(self.0 & other.0)
}
}
impl ops::Sub for DeviceFmt {
type Output = DeviceFmt;
#[inline]
fn sub(self, other: Self) -> Self {
DeviceFmt(self.0 & !other.0)
}
}
impl ops::Not for DeviceFmt {
type Output = DeviceFmt;
#[inline]
fn not(self) -> Self {
DeviceFmt(!self.0 & DEVICE_FMT_ALL)
}
}

View File

@ -0,0 +1,68 @@
use std::ops;
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct DevicePref(u32);
const DEVICE_PREF_MULTIMEDIA: u32 = 0x1;
const DEVICE_PREF_VOICE: u32 = 0x2;
const DEVICE_PREF_NOTIFICATION: u32 = 0x4;
const DEVICE_PREF_ALL: u32 = 0xF;
impl DevicePref {
pub fn none() -> Self { DevicePref(0) }
#[inline] pub fn multimedia() -> Self { DevicePref(DEVICE_PREF_MULTIMEDIA) }
#[inline] pub fn voice() -> Self { DevicePref(DEVICE_PREF_VOICE) }
#[inline] pub fn notification() -> Self { DevicePref(DEVICE_PREF_NOTIFICATION) }
#[inline] pub fn all() -> Self { DevicePref(DEVICE_PREF_ALL) }
#[inline] pub fn contains(&self, other: Self) -> bool { (*self & other) == other }
#[inline] pub fn insert(&mut self, other: Self) { self.0 |= other.0; }
#[inline] pub fn remove(&mut self, other: Self) { self.0 &= !other.0; }
}
impl ops::BitOr for DevicePref {
type Output = DevicePref;
#[inline]
fn bitor(self, other: Self) -> Self {
DevicePref(self.0 | other.0)
}
}
impl ops::BitXor for DevicePref {
type Output = DevicePref;
#[inline]
fn bitxor(self, other: Self) -> Self {
DevicePref(self.0 ^ other.0)
}
}
impl ops::BitAnd for DevicePref {
type Output = DevicePref;
#[inline]
fn bitand(self, other: Self) -> Self {
DevicePref(self.0 & other.0)
}
}
impl ops::Sub for DevicePref {
type Output = DevicePref;
#[inline]
fn sub(self, other: Self) -> Self {
DevicePref(self.0 & !other.0)
}
}
impl ops::Not for DevicePref {
type Output = DevicePref;
#[inline]
fn not(self) -> Self {
DevicePref(!self.0 & DEVICE_PREF_ALL)
}
}

View File

@ -0,0 +1,70 @@
use std::ops;
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct DeviceType(u32);
const DEVICE_TYPE_UNKNOWN: u32 = 0b00;
const DEVICE_TYPE_INPUT:u32 = 0b01;
const DEVICE_TYPE_OUTPUT: u32 = 0b10;
const DEVICE_TYPE_ALL: u32 = 0b11;
impl DeviceType {
pub fn unknown() -> Self { DeviceType(DEVICE_TYPE_UNKNOWN) }
#[inline] pub fn input() -> Self { DeviceType(DEVICE_TYPE_INPUT) }
#[inline] pub fn output() -> Self { DeviceType(DEVICE_TYPE_OUTPUT) }
#[inline] pub fn all() -> Self { DeviceType(DEVICE_TYPE_ALL) }
#[inline] pub fn is_input(&self) -> bool { self.contains(DeviceType::input()) }
#[inline] pub fn is_output(&self) -> bool { self.contains(DeviceType::output()) }
#[inline] pub fn contains(&self, other: Self) -> bool { (*self & other) == other }
#[inline] pub fn insert(&mut self, other: Self) { self.0 |= other.0; }
#[inline] pub fn remove(&mut self, other: Self) { self.0 &= !other.0; }
}
impl ops::BitOr for DeviceType {
type Output = DeviceType;
#[inline]
fn bitor(self, other: Self) -> Self {
DeviceType(self.0 | other.0)
}
}
impl ops::BitXor for DeviceType {
type Output = DeviceType;
#[inline]
fn bitxor(self, other: Self) -> Self {
DeviceType(self.0 ^ other.0)
}
}
impl ops::BitAnd for DeviceType {
type Output = DeviceType;
#[inline]
fn bitand(self, other: Self) -> Self {
DeviceType(self.0 & other.0)
}
}
impl ops::Sub for DeviceType {
type Output = DeviceType;
#[inline]
fn sub(self, other: Self) -> Self {
DeviceType(self.0 & !other.0)
}
}
impl ops::Not for DeviceType {
type Output = DeviceType;
#[inline]
fn not(self) -> Self {
DeviceType(!self.0 & DEVICE_TYPE_ALL)
}
}

View File

@ -0,0 +1,323 @@
use libc::c_void;
mod device_pref;
mod device_fmt;
mod device_type;
pub use self::device_pref::*;
pub use self::device_fmt::*;
pub use self::device_type::*;
/// Opaque handle to cubeb context.
pub enum Context {}
/// Opaque handle to cubeb stream.
pub enum Stream {}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum SampleFormat {
Signed16LE = 0,
Signed16BE = 1,
Float32LE = 2,
Float32BE = 3,
}
#[cfg(target_endian = "little")]
pub const SAMPLE_S16NE: SampleFormat = SampleFormat::Signed16LE;
#[cfg(target_endian = "little")]
pub const SAMPLE_FLOAT32NE: SampleFormat = SampleFormat::Float32LE;
#[cfg(target_endian = "big")]
pub const SAMPLE_S16NE: SampleFormat = SampleFormat::Signed16BE;
#[cfg(target_endian = "big")]
pub const SAMPLE_FLOAT32NE: SampleFormat = SampleFormat::Float32BE;
pub type DeviceId = *const c_void;
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ChannelLayout {
Undefined = 0,
DualMono = 1,
DualMonoLfe = 2,
Mono = 3,
MonoLfe = 4,
Stereo = 5,
StereoLfe = 6,
Layout3F = 7,
Layout3FLfe = 8,
Layout2F1 = 9,
Layout2F1Lfe = 10,
Layout3F1 = 11,
Layout3F1Lfe = 12,
Layout2F2 = 13,
Layout2F2Lfe = 14,
Layout3F2 = 15,
Layout3F2Lfe = 16,
Layout3F3RLfe = 17,
Layout3F4Lfe = 18,
Max = 19,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct StreamParams {
pub format: SampleFormat,
pub rate: u32,
pub channels: u32,
pub layout: ChannelLayout,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Device {
pub output_name: *mut i8,
pub input_name: *mut i8,
}
impl Default for Device {
fn default() -> Self {
Device {
output_name: 0 as *mut _,
input_name: 0 as *mut _,
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum State {
Started = 0,
Stopped = 1,
Drained = 2,
Error = 3,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum DeviceState {
Disabled = 0,
Unplugged = 1,
Enabled = 2,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct DeviceInfo {
pub devid: DeviceId,
pub device_id: *const i8,
pub friendly_name: *const i8,
pub group_id: *const i8,
pub vendor_name: *const i8,
pub devtype: DeviceType,
pub state: DeviceState,
pub preferred: DevicePref,
pub format: DeviceFmt,
pub default_format: DeviceFmt,
pub max_channels: u32,
pub default_rate: u32,
pub max_rate: u32,
pub min_rate: u32,
pub latency_lo: u32,
pub latency_hi: u32,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct DeviceCollection {
/// Device count in collection.
pub count: u32,
/// Array of pointers to device info.
pub device: [*const DeviceInfo; 0],
}
pub type DataCallback = Option<unsafe extern "C" fn(stream: *mut Stream, user_ptr: *mut c_void, input_buffer: *const c_void, output_buffer: *mut c_void, nframes: i64) -> i64>;
pub type StateCallback = Option<unsafe extern "C" fn(stream: *mut Stream, user_ptr: *mut c_void, state: State)>;
pub type DeviceChangedCallback = Option<unsafe extern "C" fn(user_ptr: *mut c_void)>;
pub type DeviceCollectionChangedCallback = Option<unsafe extern "C" fn(context: *mut Context, user_ptr: *mut c_void)>;
pub type LogCallback = Option<unsafe extern "C" fn(fmt: *const i8, ...)>;
#[test]
fn bindgen_test_layout_stream_params() {
assert_eq!(::std::mem::size_of::<StreamParams>(),
16usize,
concat!("Size of: ", stringify!(StreamParams)));
assert_eq!(::std::mem::align_of::<StreamParams>(),
4usize,
concat!("Alignment of ", stringify!(StreamParams)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).format as *const _ as usize },
0usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(format)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).rate as *const _ as usize },
4usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(rate)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).channels as *const _ as usize },
8usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(channels)));
assert_eq!(unsafe { &(*(0 as *const StreamParams)).layout as *const _ as usize },
12usize,
concat!("Alignment of field: ",
stringify!(StreamParams),
"::",
stringify!(layout)));
}
#[test]
fn bindgen_test_layout_cubeb_device() {
assert_eq!(::std::mem::size_of::<Device>(),
16usize,
concat!("Size of: ", stringify!(Device)));
assert_eq!(::std::mem::align_of::<Device>(),
8usize,
concat!("Alignment of ", stringify!(Device)));
assert_eq!(unsafe { &(*(0 as *const Device)).output_name as *const _ as usize },
0usize,
concat!("Alignment of field: ",
stringify!(Device),
"::",
stringify!(output_name)));
assert_eq!(unsafe { &(*(0 as *const Device)).input_name as *const _ as usize },
8usize,
concat!("Alignment of field: ",
stringify!(Device),
"::",
stringify!(input_name)));
}
#[test]
fn bindgen_test_layout_cubeb_device_info() {
assert_eq!(::std::mem::size_of::<DeviceInfo>(),
88usize,
concat!("Size of: ", stringify!(DeviceInfo)));
assert_eq!(::std::mem::align_of::<DeviceInfo>(),
8usize,
concat!("Alignment of ", stringify!(DeviceInfo)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).devid as *const _ as usize },
0usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(devid)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).device_id as *const _ as usize },
8usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(device_id)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).friendly_name as *const _ as usize },
16usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(friendly_name)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).group_id as *const _ as usize },
24usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(group_id)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).vendor_name as *const _ as usize },
32usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(vendor_name)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).devtype as *const _ as usize },
40usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(type_)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).state as *const _ as usize },
44usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(state)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).preferred as *const _ as usize },
48usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(preferred)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).format as *const _ as usize },
52usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(format)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).default_format as *const _ as usize },
56usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(default_format)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).max_channels as *const _ as usize },
60usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(max_channels)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).default_rate as *const _ as usize },
64usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(default_rate)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).max_rate as *const _ as usize },
68usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(max_rate)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).min_rate as *const _ as usize },
72usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(min_rate)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).latency_lo as *const _ as usize },
76usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(latency_lo)));
assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).latency_hi as *const _ as usize },
80usize,
concat!("Alignment of field: ",
stringify!(DeviceInfo),
"::",
stringify!(latency_hi)));
}
#[test]
fn bindgen_test_layout_cubeb_device_collection() {
assert_eq!(::std::mem::size_of::<DeviceCollection>(),
8usize,
concat!("Size of: ", stringify!(DeviceCollection)));
assert_eq!(::std::mem::align_of::<DeviceCollection>(),
8usize,
concat!("Alignment of ", stringify!(DeviceCollection)));
assert_eq!(unsafe { &(*(0 as *const DeviceCollection)).count as *const _ as usize },
0usize,
concat!("Alignment of field: ",
stringify!(DeviceCollection),
"::",
stringify!(count)));
assert_eq!(unsafe { &(*(0 as *const DeviceCollection)).device as *const _ as usize },
8usize,
concat!("Alignment of field: ",
stringify!(DeviceCollection),
"::",
stringify!(device)));
}

View File

@ -0,0 +1,11 @@
[package]
name = "pulse-ffi"
version = "0.1.0"
authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
description = "FFI for libpulse.so supporting static linking and dynamic loading."
[features]
dlopen = []
[dependencies]
libc = "^0.2.20"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,936 @@
#![allow(non_camel_case_types)]
use std::os::raw::{c_char, c_int, c_long, c_ulong, c_void};
/* automatically generated by rust-bindgen */
pub const PA_RATE_MAX: u32 = 48000 * 8;
pub const PA_SAMPLE_U8: c_int = 0;
pub const PA_SAMPLE_ALAW: c_int = 1;
pub const PA_SAMPLE_ULAW: c_int = 2;
pub const PA_SAMPLE_S16LE: c_int = 3;
pub const PA_SAMPLE_S16BE: c_int = 4;
pub const PA_SAMPLE_FLOAT32LE: c_int = 5;
pub const PA_SAMPLE_FLOAT32BE: c_int = 6;
pub const PA_SAMPLE_S32LE: c_int = 7;
pub const PA_SAMPLE_S32BE: c_int = 8;
pub const PA_SAMPLE_S24LE: c_int = 9;
pub const PA_SAMPLE_S24BE: c_int = 10;
pub const PA_SAMPLE_S24_32LE: c_int = 11;
pub const PA_SAMPLE_S24_32BE: c_int = 12;
pub const PA_SAMPLE_MAX: c_int = 13;
pub const PA_SAMPLE_INVALID: c_int = -1;
pub type pa_sample_format_t = c_int;
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Struct_pa_sample_spec {
pub format: pa_sample_format_t,
pub rate: u32,
pub channels: u8,
}
impl ::std::default::Default for Struct_pa_sample_spec {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub type pa_sample_spec = Struct_pa_sample_spec;
pub type pa_usec_t = u64;
// From pulse/timeval.h
pub const PA_USEC_PER_MSEC: pa_usec_t = 1_000;
pub const PA_USEC_PER_SEC: pa_usec_t = 1_000_000;
pub const PA_CONTEXT_UNCONNECTED: c_int = 0;
pub const PA_CONTEXT_CONNECTING: c_int = 1;
pub const PA_CONTEXT_AUTHORIZING: c_int = 2;
pub const PA_CONTEXT_SETTING_NAME: c_int = 3;
pub const PA_CONTEXT_READY: c_int = 4;
pub const PA_CONTEXT_FAILED: c_int = 5;
pub const PA_CONTEXT_TERMINATED: c_int = 6;
pub type pa_context_state_t = c_int;
#[allow(non_snake_case)]
pub fn PA_CONTEXT_IS_GOOD(x: pa_context_state_t) -> bool {
x == PA_CONTEXT_CONNECTING || x == PA_CONTEXT_AUTHORIZING || x == PA_CONTEXT_SETTING_NAME || x == PA_CONTEXT_READY
}
pub const PA_STREAM_UNCONNECTED: c_int = 0;
pub const PA_STREAM_CREATING: c_int = 1;
pub const PA_STREAM_READY: c_int = 2;
pub const PA_STREAM_FAILED: c_int = 3;
pub const PA_STREAM_TERMINATED: c_int = 4;
pub type pa_stream_state_t = c_int;
#[allow(non_snake_case)]
pub fn PA_STREAM_IS_GOOD(x: pa_stream_state_t) -> bool {
x == PA_STREAM_CREATING || x == PA_STREAM_READY
}
pub const PA_OPERATION_RUNNING: c_int = 0;
pub const PA_OPERATION_DONE: c_int = 1;
pub const PA_OPERATION_CANCELLED: c_int = 2;
pub type pa_operation_state_t = c_int;
pub const PA_CONTEXT_NOFLAGS: c_int = 0;
pub const PA_CONTEXT_NOAUTOSPAWN: c_int = 1;
pub const PA_CONTEXT_NOFAIL: c_int = 2;
pub type pa_context_flags_t = c_int;
pub const PA_DIRECTION_OUTPUT: c_int = 1;
pub const PA_DIRECTION_INPUT: c_int = 2;
pub type pa_direction_t = c_int;
pub const PA_DEVICE_TYPE_SINK: c_int = 0;
pub const PA_DEVICE_TYPE_SOURCE: c_int = 1;
pub type pa_device_type_t = c_int;
pub const PA_STREAM_NODIRECTION: c_int = 0;
pub const PA_STREAM_PLAYBACK: c_int = 1;
pub const PA_STREAM_RECORD: c_int = 2;
pub const PA_STREAM_UPLOAD: c_int = 3;
pub type pa_stream_direction_t = c_int;
pub const PA_STREAM_NOFLAGS: c_int = 0x0_0000;
pub const PA_STREAM_START_CORKED: c_int = 0x0_0001;
pub const PA_STREAM_INTERPOLATE_TIMING: c_int = 0x0_0002;
pub const PA_STREAM_NOT_MONOTONIC: c_int = 0x0_0004;
pub const PA_STREAM_AUTO_TIMING_UPDATE: c_int = 0x0_0008;
pub const PA_STREAM_NO_REMAP_CHANNELS: c_int = 0x0_0010;
pub const PA_STREAM_NO_REMIX_CHANNELS: c_int = 0x0_0020;
pub const PA_STREAM_FIX_FORMAT: c_int = 0x0_0040;
pub const PA_STREAM_FIX_RATE: c_int = 0x0_0080;
pub const PA_STREAM_FIX_CHANNELS: c_int = 0x0_0100;
pub const PA_STREAM_DONT_MOVE: c_int = 0x0_0200;
pub const PA_STREAM_VARIABLE_RATE: c_int = 0x0_0400;
pub const PA_STREAM_PEAK_DETECT: c_int = 0x0_0800;
pub const PA_STREAM_START_MUTED: c_int = 0x0_1000;
pub const PA_STREAM_ADJUST_LATENCY: c_int = 0x0_2000;
pub const PA_STREAM_EARLY_REQUESTS: c_int = 0x0_4000;
pub const PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND: c_int = 0x0_8000;
pub const PA_STREAM_START_UNMUTED: c_int = 0x1_0000;
pub const PA_STREAM_FAIL_ON_SUSPEND: c_int = 0x2_0000;
pub const PA_STREAM_RELATIVE_VOLUME: c_int = 0x4_0000;
pub const PA_STREAM_PASSTHROUGH: c_int = 0x8_0000;
pub type pa_stream_flags_t = c_int;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_buffer_attr {
pub maxlength: u32,
pub tlength: u32,
pub prebuf: u32,
pub minreq: u32,
pub fragsize: u32,
}
impl ::std::default::Default for pa_buffer_attr {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub const PA_OK: c_int = 0;
pub const PA_ERR_ACCESS: c_int = 1;
pub const PA_ERR_COMMAND: c_int = 2;
pub const PA_ERR_INVALID: c_int = 3;
pub const PA_ERR_EXIST: c_int = 4;
pub const PA_ERR_NOENTITY: c_int = 5;
pub const PA_ERR_CONNECTIONREFUSED: c_int = 6;
pub const PA_ERR_PROTOCOL: c_int = 7;
pub const PA_ERR_TIMEOUT: c_int = 8;
pub const PA_ERR_AUTHKEY: c_int = 9;
pub const PA_ERR_INTERNAL: c_int = 10;
pub const PA_ERR_CONNECTIONTERMINATED: c_int = 11;
pub const PA_ERR_KILLED: c_int = 12;
pub const PA_ERR_INVALIDSERVER: c_int = 13;
pub const PA_ERR_MODINITFAILED: c_int = 14;
pub const PA_ERR_BADSTATE: c_int = 15;
pub const PA_ERR_NODATA: c_int = 16;
pub const PA_ERR_VERSION: c_int = 17;
pub const PA_ERR_TOOLARGE: c_int = 18;
pub const PA_ERR_NOTSUPPORTED: c_int = 19;
pub const PA_ERR_UNKNOWN: c_int = 20;
pub const PA_ERR_NOEXTENSION: c_int = 21;
pub const PA_ERR_OBSOLETE: c_int = 22;
pub const PA_ERR_NOTIMPLEMENTED: c_int = 23;
pub const PA_ERR_FORKED: c_int = 24;
pub const PA_ERR_IO: c_int = 25;
pub const PA_ERR_BUSY: c_int = 26;
pub const PA_ERR_MAX: c_int = 27;
pub type pa_error_code_t = c_int;
pub const PA_SUBSCRIPTION_MASK_NULL: c_int = 0;
pub const PA_SUBSCRIPTION_MASK_SINK: c_int = 1;
pub const PA_SUBSCRIPTION_MASK_SOURCE: c_int = 2;
pub const PA_SUBSCRIPTION_MASK_SINK_INPUT: c_int = 4;
pub const PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT: c_int = 8;
pub const PA_SUBSCRIPTION_MASK_MODULE: c_int = 16;
pub const PA_SUBSCRIPTION_MASK_CLIENT: c_int = 32;
pub const PA_SUBSCRIPTION_MASK_SAMPLE_CACHE: c_int = 64;
pub const PA_SUBSCRIPTION_MASK_SERVER: c_int = 128;
pub const PA_SUBSCRIPTION_MASK_AUTOLOAD: c_int = 256;
pub const PA_SUBSCRIPTION_MASK_CARD: c_int = 512;
pub const PA_SUBSCRIPTION_MASK_ALL: c_int = 767;
pub type pa_subscription_mask_t = c_int;
pub const PA_SUBSCRIPTION_EVENT_SINK: c_int = 0;
pub const PA_SUBSCRIPTION_EVENT_SOURCE: c_int = 1;
pub const PA_SUBSCRIPTION_EVENT_SINK_INPUT: c_int = 2;
pub const PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: c_int = 3;
pub const PA_SUBSCRIPTION_EVENT_MODULE: c_int = 4;
pub const PA_SUBSCRIPTION_EVENT_CLIENT: c_int = 5;
pub const PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE: c_int = 6;
pub const PA_SUBSCRIPTION_EVENT_SERVER: c_int = 7;
pub const PA_SUBSCRIPTION_EVENT_AUTOLOAD: c_int = 8;
pub const PA_SUBSCRIPTION_EVENT_CARD: c_int = 9;
pub const PA_SUBSCRIPTION_EVENT_FACILITY_MASK: c_int = 15;
pub const PA_SUBSCRIPTION_EVENT_NEW: c_int = 0;
pub const PA_SUBSCRIPTION_EVENT_CHANGE: c_int = 16;
pub const PA_SUBSCRIPTION_EVENT_REMOVE: c_int = 32;
pub const PA_SUBSCRIPTION_EVENT_TYPE_MASK: c_int = 48;
pub type pa_subscription_event_type_t = c_int;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct timeval {
pub tv_sec: c_long,
pub tv_usec: c_long,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_timing_info {
pub timestamp: timeval,
pub synchronized_clocks: c_int,
pub sink_usec: pa_usec_t,
pub source_usec: pa_usec_t,
pub transport_usec: pa_usec_t,
pub playing: c_int,
pub write_index_corrupt: c_int,
pub write_index: i64,
pub read_index_corrupt: c_int,
pub read_index: i64,
pub configured_sink_usec: pa_usec_t,
pub configured_source_usec: pa_usec_t,
pub since_underrun: i64,
}
impl ::std::default::Default for pa_timing_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_spawn_api {
pub prefork: Option<extern "C" fn() -> ()>,
pub postfork: Option<extern "C" fn() -> ()>,
pub atfork: Option<extern "C" fn() -> ()>,
}
impl ::std::default::Default for pa_spawn_api {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub const PA_SEEK_RELATIVE: c_int = 0;
pub const PA_SEEK_ABSOLUTE: c_int = 1;
pub const PA_SEEK_RELATIVE_ON_READ: c_int = 2;
pub const PA_SEEK_RELATIVE_END: c_int = 3;
pub type pa_seek_mode_t = c_int;
pub const PA_SINK_NOFLAGS: c_int = 0;
pub const PA_SINK_HW_VOLUME_CTRL: c_int = 1;
pub const PA_SINK_LATENCY: c_int = 2;
pub const PA_SINK_HARDWARE: c_int = 4;
pub const PA_SINK_NETWORK: c_int = 8;
pub const PA_SINK_HW_MUTE_CTRL: c_int = 16;
pub const PA_SINK_DECIBEL_VOLUME: c_int = 32;
pub const PA_SINK_FLAT_VOLUME: c_int = 64;
pub const PA_SINK_DYNAMIC_LATENCY: c_int = 128;
pub const PA_SINK_SET_FORMATS: c_int = 256;
pub type pa_sink_flags_t = c_int;
pub const PA_SINK_INVALID_STATE: c_int = -1;
pub const PA_SINK_RUNNING: c_int = 0;
pub const PA_SINK_IDLE: c_int = 1;
pub const PA_SINK_SUSPENDED: c_int = 2;
pub const PA_SINK_INIT: c_int = -2;
pub const PA_SINK_UNLINKED: c_int = -3;
pub type pa_sink_state_t = c_int;
pub const PA_SOURCE_NOFLAGS: c_int = 0x00;
pub const PA_SOURCE_HW_VOLUME_CTRL: c_int = 0x01;
pub const PA_SOURCE_LATENCY: c_int = 0x02;
pub const PA_SOURCE_HARDWARE: c_int = 0x04;
pub const PA_SOURCE_NETWORK: c_int = 0x08;
pub const PA_SOURCE_HW_MUTE_CTRL: c_int = 0x10;
pub const PA_SOURCE_DECIBEL_VOLUME: c_int = 0x20;
pub const PA_SOURCE_DYNAMIC_LATENCY: c_int = 0x40;
pub const PA_SOURCE_FLAT_VOLUME: c_int = 0x80;
pub type pa_source_flags_t = c_int;
pub const PA_SOURCE_INVALID_STATE: c_int = -1;
pub const PA_SOURCE_RUNNING: c_int = 0;
pub const PA_SOURCE_IDLE: c_int = 1;
pub const PA_SOURCE_SUSPENDED: c_int = 2;
pub const PA_SOURCE_INIT: c_int = -2;
pub const PA_SOURCE_UNLINKED: c_int = -3;
pub type pa_source_state_t = c_int;
pub type pa_free_cb_t = Option<unsafe extern "C" fn(p: *mut c_void) -> ()>;
pub const PA_PORT_AVAILABLE_UNKNOWN: c_int = 0;
pub const PA_PORT_AVAILABLE_NO: c_int = 1;
pub const PA_PORT_AVAILABLE_YES: c_int = 2;
pub type pa_port_available_t = c_int;
pub const PA_IO_EVENT_NULL: c_int = 0;
pub const PA_IO_EVENT_INPUT: c_int = 1;
pub const PA_IO_EVENT_OUTPUT: c_int = 2;
pub const PA_IO_EVENT_HANGUP: c_int = 4;
pub const PA_IO_EVENT_ERROR: c_int = 8;
pub type pa_io_event_flags_t = c_int;
pub enum pa_io_event { }
pub type pa_io_event_cb_t = Option<unsafe extern "C" fn(ea: *mut pa_mainloop_api,
e: *mut pa_io_event,
fd: c_int,
events: pa_io_event_flags_t,
userdata: *mut c_void)>;
pub type pa_io_event_destroy_cb_t = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
e: *mut pa_io_event,
userdata: *mut c_void)>;
pub enum pa_time_event { }
pub type pa_time_event_cb_t = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
e: *mut pa_time_event,
tv: *const timeval,
userdata: *mut c_void)>;
pub type pa_time_event_destroy_cb_t = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
e: *mut pa_time_event,
userdata: *mut c_void)>;
pub enum pa_defer_event { }
pub type pa_defer_event_cb_t = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
e: *mut pa_defer_event,
userdata: *mut c_void)>;
pub type pa_defer_event_destroy_cb_t = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
e: *mut pa_defer_event,
userdata: *mut c_void)>;
pub type IoNewFn = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
fd: c_int,
events: pa_io_event_flags_t,
cb: pa_io_event_cb_t,
userdata: *mut c_void)
-> *mut pa_io_event>;
pub type TimeNewFn = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
tv: *const timeval,
cb: pa_time_event_cb_t,
userdata: *mut c_void)
-> *mut pa_time_event>;
pub type DeferNewFn = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
cb: pa_defer_event_cb_t,
userdata: *mut c_void)
-> *mut pa_defer_event>;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_mainloop_api {
pub userdata: *mut c_void,
pub io_new: IoNewFn,
pub io_enable: Option<unsafe extern "C" fn(e: *mut pa_io_event, events: pa_io_event_flags_t)>,
pub io_free: Option<unsafe extern "C" fn(e: *mut pa_io_event)>,
pub io_set_destroy: Option<unsafe extern "C" fn(e: *mut pa_io_event, cb: pa_io_event_destroy_cb_t)>,
pub time_new: TimeNewFn,
pub time_restart: Option<unsafe extern "C" fn(e: *mut pa_time_event, tv: *const timeval)>,
pub time_free: Option<unsafe extern "C" fn(e: *mut pa_time_event)>,
pub time_set_destroy: Option<unsafe extern "C" fn(e: *mut pa_time_event, cb: pa_time_event_destroy_cb_t)>,
pub defer_new: DeferNewFn,
pub defer_enable: Option<unsafe extern "C" fn(e: *mut pa_defer_event, b: c_int)>,
pub defer_free: Option<unsafe extern "C" fn(e: *mut pa_defer_event)>,
pub defer_set_destroy: Option<unsafe extern "C" fn(e: *mut pa_defer_event, cb: pa_defer_event_destroy_cb_t)>,
pub quit: Option<unsafe extern "C" fn(a: *mut pa_mainloop_api, retval: c_int)>,
}
impl ::std::default::Default for pa_mainloop_api {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub type pa_mainloop_api_once_cb_t = Option<unsafe extern "C" fn(m: *mut pa_mainloop_api, userdata: *mut c_void)>;
pub enum pa_proplist { }
pub const PA_UPDATE_SET: c_int = 0;
pub const PA_UPDATE_MERGE: c_int = 1;
pub const PA_UPDATE_REPLACE: c_int = 2;
pub type pa_update_mode_t = c_int;
pub const PA_CHANNEL_POSITION_INVALID: c_int = -1;
pub const PA_CHANNEL_POSITION_MONO: c_int = 0;
pub const PA_CHANNEL_POSITION_FRONT_LEFT: c_int = 1;
pub const PA_CHANNEL_POSITION_FRONT_RIGHT: c_int = 2;
pub const PA_CHANNEL_POSITION_FRONT_CENTER: c_int = 3;
pub const PA_CHANNEL_POSITION_LEFT: c_int = 1;
pub const PA_CHANNEL_POSITION_RIGHT: c_int = 2;
pub const PA_CHANNEL_POSITION_CENTER: c_int = 3;
pub const PA_CHANNEL_POSITION_REAR_CENTER: c_int = 4;
pub const PA_CHANNEL_POSITION_REAR_LEFT: c_int = 5;
pub const PA_CHANNEL_POSITION_REAR_RIGHT: c_int = 6;
pub const PA_CHANNEL_POSITION_LFE: c_int = 7;
pub const PA_CHANNEL_POSITION_SUBWOOFER: c_int = 7;
pub const PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: c_int = 8;
pub const PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: c_int = 9;
pub const PA_CHANNEL_POSITION_SIDE_LEFT: c_int = 10;
pub const PA_CHANNEL_POSITION_SIDE_RIGHT: c_int = 11;
pub const PA_CHANNEL_POSITION_AUX0: c_int = 12;
pub const PA_CHANNEL_POSITION_AUX1: c_int = 13;
pub const PA_CHANNEL_POSITION_AUX2: c_int = 14;
pub const PA_CHANNEL_POSITION_AUX3: c_int = 15;
pub const PA_CHANNEL_POSITION_AUX4: c_int = 16;
pub const PA_CHANNEL_POSITION_AUX5: c_int = 17;
pub const PA_CHANNEL_POSITION_AUX6: c_int = 18;
pub const PA_CHANNEL_POSITION_AUX7: c_int = 19;
pub const PA_CHANNEL_POSITION_AUX8: c_int = 20;
pub const PA_CHANNEL_POSITION_AUX9: c_int = 21;
pub const PA_CHANNEL_POSITION_AUX10: c_int = 22;
pub const PA_CHANNEL_POSITION_AUX11: c_int = 23;
pub const PA_CHANNEL_POSITION_AUX12: c_int = 24;
pub const PA_CHANNEL_POSITION_AUX13: c_int = 25;
pub const PA_CHANNEL_POSITION_AUX14: c_int = 26;
pub const PA_CHANNEL_POSITION_AUX15: c_int = 27;
pub const PA_CHANNEL_POSITION_AUX16: c_int = 28;
pub const PA_CHANNEL_POSITION_AUX17: c_int = 29;
pub const PA_CHANNEL_POSITION_AUX18: c_int = 30;
pub const PA_CHANNEL_POSITION_AUX19: c_int = 31;
pub const PA_CHANNEL_POSITION_AUX20: c_int = 32;
pub const PA_CHANNEL_POSITION_AUX21: c_int = 33;
pub const PA_CHANNEL_POSITION_AUX22: c_int = 34;
pub const PA_CHANNEL_POSITION_AUX23: c_int = 35;
pub const PA_CHANNEL_POSITION_AUX24: c_int = 36;
pub const PA_CHANNEL_POSITION_AUX25: c_int = 37;
pub const PA_CHANNEL_POSITION_AUX26: c_int = 38;
pub const PA_CHANNEL_POSITION_AUX27: c_int = 39;
pub const PA_CHANNEL_POSITION_AUX28: c_int = 40;
pub const PA_CHANNEL_POSITION_AUX29: c_int = 41;
pub const PA_CHANNEL_POSITION_AUX30: c_int = 42;
pub const PA_CHANNEL_POSITION_AUX31: c_int = 43;
pub const PA_CHANNEL_POSITION_TOP_CENTER: c_int = 44;
pub const PA_CHANNEL_POSITION_TOP_FRONT_LEFT: c_int = 45;
pub const PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: c_int = 46;
pub const PA_CHANNEL_POSITION_TOP_FRONT_CENTER: c_int = 47;
pub const PA_CHANNEL_POSITION_TOP_REAR_LEFT: c_int = 48;
pub const PA_CHANNEL_POSITION_TOP_REAR_RIGHT: c_int = 49;
pub const PA_CHANNEL_POSITION_TOP_REAR_CENTER: c_int = 50;
pub const PA_CHANNEL_POSITION_MAX: c_int = 51;
pub type pa_channel_position_t = c_int;
pub type pa_channel_position_mask_t = u64;
pub const PA_CHANNEL_MAP_AIFF: c_int = 0;
pub const PA_CHANNEL_MAP_ALSA: c_int = 1;
pub const PA_CHANNEL_MAP_AUX: c_int = 2;
pub const PA_CHANNEL_MAP_WAVEEX: c_int = 3;
pub const PA_CHANNEL_MAP_OSS: c_int = 4;
pub const PA_CHANNEL_MAP_DEF_MAX: c_int = 5;
pub const PA_CHANNEL_MAP_DEFAULT: c_int = 0;
pub type pa_channel_map_def_t = c_int;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_channel_map {
pub channels: u8,
pub map: [pa_channel_position_t; 32usize],
}
impl ::std::default::Default for pa_channel_map {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub const PA_ENCODING_ANY: c_int = 0;
pub const PA_ENCODING_PCM: c_int = 1;
pub const PA_ENCODING_AC3_IEC61937: c_int = 2;
pub const PA_ENCODING_EAC3_IEC61937: c_int = 3;
pub const PA_ENCODING_MPEG_IEC61937: c_int = 4;
pub const PA_ENCODING_DTS_IEC61937: c_int = 5;
pub const PA_ENCODING_MPEG2_AAC_IEC61937: c_int = 6;
pub const PA_ENCODING_MAX: c_int = 7;
pub const PA_ENCODING_INVALID: c_int = -1;
pub type pa_encoding_t = c_int;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_format_info {
pub encoding: pa_encoding_t,
pub plist: *mut pa_proplist,
}
impl ::std::default::Default for pa_format_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub const PA_PROP_TYPE_INT: c_int = 0;
pub const PA_PROP_TYPE_INT_RANGE: c_int = 1;
pub const PA_PROP_TYPE_INT_ARRAY: c_int = 2;
pub const PA_PROP_TYPE_STRING: c_int = 3;
pub const PA_PROP_TYPE_STRING_ARRAY: c_int = 4;
pub const PA_PROP_TYPE_INVALID: c_int = -1;
pub type pa_prop_type_t = c_int;
pub enum pa_operation { }
pub type pa_operation_notify_cb_t = Option<unsafe extern "C" fn(o: *mut pa_operation, userdata: *mut c_void)>;
pub enum pa_context { }
pub type pa_context_notify_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context, userdata: *mut c_void)>;
pub type pa_context_success_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
success: c_int,
userdata: *mut c_void)>;
pub type pa_context_event_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
name: *const c_char,
p: *mut pa_proplist,
userdata: *mut c_void)>;
pub type pa_volume_t = u32;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_cvolume {
pub channels: u8,
pub values: [pa_volume_t; 32usize],
}
impl ::std::default::Default for pa_cvolume {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub enum pa_stream { }
pub type pa_stream_success_cb_t = Option<unsafe extern "C" fn(s: *mut pa_stream,
success: c_int,
userdata: *mut c_void)>;
pub type pa_stream_request_cb_t = Option<unsafe extern "C" fn(p: *mut pa_stream, nbytes: usize, userdata: *mut c_void)>;
pub type pa_stream_notify_cb_t = Option<unsafe extern "C" fn(p: *mut pa_stream, userdata: *mut c_void)>;
pub type pa_stream_event_cb_t = Option<unsafe extern "C" fn(p: *mut pa_stream,
name: *const c_char,
pl: *mut pa_proplist,
userdata: *mut c_void)>;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_port_info {
pub name: *const c_char,
pub description: *const c_char,
pub priority: u32,
pub available: c_int,
}
impl ::std::default::Default for pa_port_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_sink_info {
pub name: *const c_char,
pub index: u32,
pub description: *const c_char,
pub sample_spec: pa_sample_spec,
pub channel_map: pa_channel_map,
pub owner_module: u32,
pub volume: pa_cvolume,
pub mute: c_int,
pub monitor_source: u32,
pub monitor_source_name: *const c_char,
pub latency: pa_usec_t,
pub driver: *const c_char,
pub flags: pa_sink_flags_t,
pub proplist: *mut pa_proplist,
pub configured_latency: pa_usec_t,
pub base_volume: pa_volume_t,
pub state: pa_sink_state_t,
pub n_volume_steps: u32,
pub card: u32,
pub n_ports: u32,
pub ports: *mut *mut pa_port_info,
pub active_port: *mut pa_port_info,
pub n_formats: u8,
pub formats: *mut *mut pa_format_info,
}
impl ::std::default::Default for pa_sink_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub type pa_sink_info_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
i: *const pa_sink_info,
eol: c_int,
userdata: *mut c_void)>;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_source_info {
pub name: *const c_char,
pub index: u32,
pub description: *const c_char,
pub sample_spec: pa_sample_spec,
pub channel_map: pa_channel_map,
pub owner_module: u32,
pub volume: pa_cvolume,
pub mute: c_int,
pub monitor_of_sink: u32,
pub monitor_of_sink_name: *const c_char,
pub latency: pa_usec_t,
pub driver: *const c_char,
pub flags: pa_source_flags_t,
pub proplist: *mut pa_proplist,
pub configured_latency: pa_usec_t,
pub base_volume: pa_volume_t,
pub state: pa_source_state_t,
pub n_volume_steps: u32,
pub card: u32,
pub n_ports: u32,
pub ports: *mut *mut pa_port_info,
pub active_port: *mut pa_port_info,
pub n_formats: u8,
pub formats: *mut *mut pa_format_info,
}
impl ::std::default::Default for pa_source_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub type pa_source_info_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
i: *const pa_source_info,
eol: c_int,
userdata: *mut c_void)>;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_server_info {
pub user_name: *const c_char,
pub host_name: *const c_char,
pub server_version: *const c_char,
pub server_name: *const c_char,
pub sample_spec: pa_sample_spec,
pub default_sink_name: *const c_char,
pub default_source_name: *const c_char,
pub cookie: u32,
pub channel_map: pa_channel_map,
}
impl ::std::default::Default for pa_server_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub type pa_server_info_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
i: *const pa_server_info,
userdata: *mut c_void)>;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_module_info {
pub index: u32,
pub name: *const c_char,
pub argument: *const c_char,
pub n_used: u32,
pub auto_unload: c_int,
pub proplist: *mut pa_proplist,
}
impl ::std::default::Default for pa_module_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub type pa_module_info_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
i: *const pa_module_info,
eol: c_int,
userdata: *mut c_void)>;
pub type pa_context_index_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context, idx: u32, userdata: *mut c_void)>;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_client_info {
pub index: u32,
pub name: *const c_char,
pub owner_module: u32,
pub driver: *const c_char,
pub proplist: *mut pa_proplist,
}
impl ::std::default::Default for pa_client_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub type pa_client_info_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
i: *const pa_client_info,
eol: c_int,
userdata: *mut c_void)>;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_card_profile_info {
pub name: *const c_char,
pub description: *const c_char,
pub n_sinks: u32,
pub n_sources: u32,
pub priority: u32,
}
impl ::std::default::Default for pa_card_profile_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_card_profile_info2 {
pub name: *const c_char,
pub description: *const c_char,
pub n_sinks: u32,
pub n_sources: u32,
pub priority: u32,
pub available: c_int,
}
impl ::std::default::Default for pa_card_profile_info2 {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_card_port_info {
pub name: *const c_char,
pub description: *const c_char,
pub priority: u32,
pub available: c_int,
pub direction: c_int,
pub n_profiles: u32,
pub profiles: *mut *mut pa_card_profile_info,
pub proplist: *mut pa_proplist,
pub latency_offset: i64,
pub profiles2: *mut *mut pa_card_profile_info2,
}
impl ::std::default::Default for pa_card_port_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_card_info {
pub index: u32,
pub name: *const c_char,
pub owner_module: u32,
pub driver: *const c_char,
pub n_profiles: u32,
pub profiles: *mut pa_card_profile_info,
pub active_profile: *mut pa_card_profile_info,
pub proplist: *mut pa_proplist,
pub n_ports: u32,
pub ports: *mut *mut pa_card_port_info,
pub profiles2: *mut *mut pa_card_profile_info2,
pub active_profile2: *mut pa_card_profile_info2,
}
impl ::std::default::Default for pa_card_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub type pa_card_info_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
i: *const pa_card_info,
eol: c_int,
userdata: *mut c_void)>;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_sink_input_info {
pub index: u32,
pub name: *const c_char,
pub owner_module: u32,
pub client: u32,
pub sink: u32,
pub sample_spec: pa_sample_spec,
pub channel_map: pa_channel_map,
pub volume: pa_cvolume,
pub buffer_usec: pa_usec_t,
pub sink_usec: pa_usec_t,
pub resample_method: *const c_char,
pub driver: *const c_char,
pub mute: c_int,
pub proplist: *mut pa_proplist,
pub corked: c_int,
pub has_volume: c_int,
pub volume_writable: c_int,
pub format: *mut pa_format_info,
}
impl ::std::default::Default for pa_sink_input_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub type pa_sink_input_info_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
i: *const pa_sink_input_info,
eol: c_int,
userdata: *mut c_void)>;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_source_output_info {
pub index: u32,
pub name: *const c_char,
pub owner_module: u32,
pub client: u32,
pub source: u32,
pub sample_spec: pa_sample_spec,
pub channel_map: pa_channel_map,
pub buffer_usec: pa_usec_t,
pub source_usec: pa_usec_t,
pub resample_method: *const c_char,
pub driver: *const c_char,
pub proplist: *mut pa_proplist,
pub corked: c_int,
pub volume: pa_cvolume,
pub mute: c_int,
pub has_volume: c_int,
pub volume_writable: c_int,
pub format: *mut pa_format_info,
}
impl ::std::default::Default for pa_source_output_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub type pa_source_output_info_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
i: *const pa_source_output_info,
eol: c_int,
userdata: *mut c_void)>;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_stat_info {
pub memblock_total: u32,
pub memblock_total_size: u32,
pub memblock_allocated: u32,
pub memblock_allocated_size: u32,
pub scache_size: u32,
}
impl ::std::default::Default for pa_stat_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub type pa_stat_info_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
i: *const pa_stat_info,
userdata: *mut c_void)>;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_sample_info {
pub index: u32,
pub name: *const c_char,
pub volume: pa_cvolume,
pub sample_spec: pa_sample_spec,
pub channel_map: pa_channel_map,
pub duration: pa_usec_t,
pub bytes: u32,
pub lazy: c_int,
pub filename: *const c_char,
pub proplist: *mut pa_proplist,
}
impl ::std::default::Default for pa_sample_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub type pa_sample_info_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
i: *const pa_sample_info,
eol: c_int,
userdata: *mut c_void)>;
pub const PA_AUTOLOAD_SINK: c_int = 0;
pub const PA_AUTOLOAD_SOURCE: c_int = 1;
pub type pa_autoload_type_t = c_int;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct pa_autoload_info {
pub index: u32,
pub name: *const c_char,
pub _type: pa_autoload_type_t,
pub module: *const c_char,
pub argument: *const c_char,
}
impl ::std::default::Default for pa_autoload_info {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
pub type pa_autoload_info_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
i: *const pa_autoload_info,
eol: c_int,
userdata: *mut c_void)>;
pub type pa_context_subscribe_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
t: pa_subscription_event_type_t,
idx: u32,
userdata: *mut c_void)>;
pub type pa_context_play_sample_cb_t = Option<unsafe extern "C" fn(c: *mut pa_context,
idx: u32,
userdata: *mut c_void)>;
pub enum pa_threaded_mainloop { }
pub enum pollfd { }
pub enum pa_mainloop { }
pub type pa_poll_func = Option<unsafe extern "C" fn(ufds: *mut pollfd,
nfds: c_ulong,
timeout: c_int,
userdata: *mut c_void)
-> c_int>;
pub enum pa_signal_event { }
pub type pa_signal_cb_t = Option<unsafe extern "C" fn(api: *mut pa_mainloop_api,
e: *mut pa_signal_event,
sig: c_int,
userdata: *mut c_void)>;
pub type pa_signal_destroy_cb_t = Option<unsafe extern "C" fn(api: *mut pa_mainloop_api,
e: *mut pa_signal_event,
userdata: *mut c_void)>;

View File

@ -0,0 +1,14 @@
// Required for dlopen, et al.
#[cfg(feature = "dlopen")]
extern crate libc;
mod ffi_types;
mod ffi_funcs;
pub use ffi_types::*;
pub use ffi_funcs::*;
#[cfg(feature = "dlopen")]
pub unsafe fn open() -> Option<LibLoader> {
return LibLoader::open();
}

View File

@ -0,0 +1,725 @@
// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use backend::*;
use backend::cork_state::CorkState;
use backend::var_array::VarArray;
use capi::PULSE_OPS;
use cubeb;
use pulse_ffi::*;
use semver;
use std::default::Default;
use std::ffi::CStr;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
macro_rules! dup_str {
($Dst: expr, $Src: expr) => {
if !$Dst.is_null() {
pa_xfree($Dst as *mut _);
}
$Dst = pa_xstrdup($Src);
}
}
fn pa_channel_to_cubeb_channel(channel: pa_channel_position_t) -> cubeb::Channel {
assert_ne!(channel, PA_CHANNEL_POSITION_INVALID);
match channel {
PA_CHANNEL_POSITION_MONO => cubeb::CHANNEL_MONO,
PA_CHANNEL_POSITION_FRONT_LEFT => cubeb::CHANNEL_LEFT,
PA_CHANNEL_POSITION_FRONT_RIGHT => cubeb::CHANNEL_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER => cubeb::CHANNEL_CENTER,
PA_CHANNEL_POSITION_SIDE_LEFT => cubeb::CHANNEL_LS,
PA_CHANNEL_POSITION_SIDE_RIGHT => cubeb::CHANNEL_RS,
PA_CHANNEL_POSITION_REAR_LEFT => cubeb::CHANNEL_RLS,
PA_CHANNEL_POSITION_REAR_CENTER => cubeb::CHANNEL_RCENTER,
PA_CHANNEL_POSITION_REAR_RIGHT => cubeb::CHANNEL_RRS,
PA_CHANNEL_POSITION_LFE => cubeb::CHANNEL_LFE,
_ => cubeb::CHANNEL_INVALID,
}
}
fn channel_map_to_layout(cm: &pa_channel_map) -> cubeb::ChannelLayout {
let mut cubeb_map: cubeb::ChannelMap = Default::default();
cubeb_map.channels = cm.channels as u32;
for i in 0usize..cm.channels as usize {
cubeb_map.map[i] = pa_channel_to_cubeb_channel(cm.map[i]);
}
unsafe { cubeb::cubeb_channel_map_to_layout(&cubeb_map) }
}
#[derive(Debug)]
pub struct Context {
pub ops: *const cubeb::Ops,
pub mainloop: *mut pa_threaded_mainloop,
pub context: *mut pa_context,
pub default_sink_info: *mut pa_sink_info,
pub context_name: *const c_char,
pub collection_changed_callback: cubeb::DeviceCollectionChangedCallback,
pub collection_changed_user_ptr: *mut c_void,
pub error: bool,
pub version_2_0_0: bool,
pub version_0_9_8: bool,
#[cfg(feature = "pulse-dlopen")]
pub libpulse: LibLoader,
}
impl Context {
#[cfg(feature = "pulse-dlopen")]
fn _new(name: *const i8) -> Result<Box<Self>> {
let libpulse = unsafe { open() };
if libpulse.is_none() {
return Err(cubeb::ERROR);
}
let ctx = Box::new(Context {
ops: &PULSE_OPS,
libpulse: libpulse.unwrap(),
mainloop: unsafe { pa_threaded_mainloop_new() },
context: 0 as *mut _,
default_sink_info: 0 as *mut _,
context_name: name,
collection_changed_callback: None,
collection_changed_user_ptr: 0 as *mut _,
error: true,
version_0_9_8: false,
version_2_0_0: false,
});
Ok(ctx)
}
#[cfg(not(feature = "pulse-dlopen"))]
fn _new(name: *const i8) -> Result<Box<Self>> {
Ok(Box::new(Context {
ops: &PULSE_OPS,
mainloop: unsafe { pa_threaded_mainloop_new() },
context: 0 as *mut _,
default_sink_info: 0 as *mut _,
context_name: name,
collection_changed_callback: None,
collection_changed_user_ptr: 0 as *mut _,
error: true,
version_0_9_8: false,
version_2_0_0: false,
}))
}
pub fn new(name: *const c_char) -> Result<Box<Self>> {
let mut ctx = try!(Context::_new(name));
unsafe { pa_threaded_mainloop_start(ctx.mainloop) };
if ctx.pulse_context_init() != cubeb::OK {
ctx.destroy();
return Err(cubeb::ERROR);
}
unsafe {
pa_threaded_mainloop_lock(ctx.mainloop);
pa_context_get_server_info(ctx.context,
Some(server_info_callback),
ctx.as_mut() as *mut Context as *mut _);
pa_threaded_mainloop_unlock(ctx.mainloop);
}
// Return the result.
Ok(ctx)
}
pub fn destroy(&mut self) {
if !self.default_sink_info.is_null() {
let _ = unsafe { Box::from_raw(self.default_sink_info) };
}
if !self.context.is_null() {
unsafe { self.pulse_context_destroy() };
}
if !self.mainloop.is_null() {
unsafe {
pa_threaded_mainloop_stop(self.mainloop);
pa_threaded_mainloop_free(self.mainloop);
}
}
}
pub fn new_stream(&mut self,
stream_name: *const c_char,
input_device: cubeb::DeviceId,
input_stream_params: Option<cubeb::StreamParams>,
output_device: cubeb::DeviceId,
output_stream_params: Option<cubeb::StreamParams>,
latency_frames: u32,
data_callback: cubeb::DataCallback,
state_callback: cubeb::StateCallback,
user_ptr: *mut c_void)
-> Result<Box<Stream>> {
if self.error && self.pulse_context_init() != 0 {
return Err(cubeb::ERROR);
}
Stream::new(self,
stream_name,
input_device,
input_stream_params,
output_device,
output_stream_params,
latency_frames,
data_callback,
state_callback,
user_ptr)
}
pub fn max_channel_count(&self) -> Result<u32> {
unsafe {
pa_threaded_mainloop_lock(self.mainloop);
while self.default_sink_info.is_null() {
pa_threaded_mainloop_wait(self.mainloop);
}
pa_threaded_mainloop_unlock(self.mainloop);
Ok((*self.default_sink_info).channel_map.channels as u32)
}
}
pub fn preferred_sample_rate(&self) -> Result<u32> {
unsafe {
pa_threaded_mainloop_lock(self.mainloop);
while self.default_sink_info.is_null() {
pa_threaded_mainloop_wait(self.mainloop);
}
pa_threaded_mainloop_unlock(self.mainloop);
Ok((*self.default_sink_info).sample_spec.rate)
}
}
pub fn min_latency(&self, params: &cubeb::StreamParams) -> Result<u32> {
// According to PulseAudio developers, this is a safe minimum.
Ok(25 * params.rate / 1000)
}
pub fn preferred_channel_layout(&self) -> Result<cubeb::ChannelLayout> {
unsafe {
pa_threaded_mainloop_lock(self.mainloop);
while self.default_sink_info.is_null() {
pa_threaded_mainloop_wait(self.mainloop);
}
pa_threaded_mainloop_unlock(self.mainloop);
Ok(channel_map_to_layout(&(*self.default_sink_info).channel_map))
}
}
pub fn enumerate_devices(&self, devtype: cubeb::DeviceType) -> Result<*mut cubeb::DeviceCollection> {
let mut user_data: PulseDevListData = Default::default();
user_data.context = self as *const _ as *mut _;
unsafe {
pa_threaded_mainloop_lock(self.mainloop);
let o = pa_context_get_server_info(self.context,
Some(pulse_server_info_cb),
&mut user_data as *mut _ as *mut _);
if !o.is_null() {
self.operation_wait(ptr::null_mut(), o);
pa_operation_unref(o);
}
if devtype == cubeb::DEVICE_TYPE_OUTPUT {
let o = pa_context_get_sink_info_list(self.context,
Some(pulse_sink_info_cb),
&mut user_data as *mut _ as *mut _);
if !o.is_null() {
self.operation_wait(ptr::null_mut(), o);
pa_operation_unref(o);
}
}
if devtype == cubeb::DEVICE_TYPE_INPUT {
let o = pa_context_get_source_info_list(self.context,
Some(pulse_source_info_cb),
&mut user_data as *mut _ as *mut _);
if !o.is_null() {
self.operation_wait(ptr::null_mut(), o);
pa_operation_unref(o);
}
}
pa_threaded_mainloop_unlock(self.mainloop);
}
// TODO: This is dodgy - Need to account for padding between count
// and device array in C code on 64-bit platforms. Using an extra
// pointer instead of the header size to achieve this.
let mut coll: Box<VarArray<*const cubeb::DeviceInfo>> = VarArray::with_length(user_data.devinfo.len());
for (e1, e2) in user_data
.devinfo
.drain(..)
.zip(coll.as_mut_slice().iter_mut()) {
*e2 = e1;
}
Ok(Box::into_raw(coll) as *mut cubeb::DeviceCollection)
}
pub fn register_device_collection_changed(&mut self,
devtype: cubeb::DeviceType,
cb: cubeb::DeviceCollectionChangedCallback,
user_ptr: *mut c_void)
-> i32 {
unsafe extern "C" fn subscribe_success(_: *mut pa_context, success: i32, user_data: *mut c_void) {
let ctx = &*(user_data as *mut Context);
debug_assert_ne!(success, 0);
pa_threaded_mainloop_signal(ctx.mainloop, 0);
}
self.collection_changed_callback = cb;
self.collection_changed_user_ptr = user_ptr;
unsafe {
pa_threaded_mainloop_lock(self.mainloop);
let mut mask: pa_subscription_mask_t = PA_SUBSCRIPTION_MASK_NULL;
if self.collection_changed_callback.is_none() {
// Unregister subscription
pa_context_set_subscribe_callback(self.context, None, ptr::null_mut());
} else {
pa_context_set_subscribe_callback(self.context,
Some(pulse_subscribe_callback),
self as *mut _ as *mut _);
if devtype == cubeb::DEVICE_TYPE_INPUT {
mask |= PA_SUBSCRIPTION_MASK_SOURCE
};
if devtype == cubeb::DEVICE_TYPE_OUTPUT {
mask |= PA_SUBSCRIPTION_MASK_SOURCE
};
}
let o = pa_context_subscribe(self.context,
mask,
Some(subscribe_success),
self as *const _ as *mut _);
if o.is_null() {
log!("Context subscribe failed");
return cubeb::ERROR;
}
self.operation_wait(ptr::null_mut(), o);
pa_operation_unref(o);
pa_threaded_mainloop_unlock(self.mainloop);
}
cubeb::OK
}
//
pub fn pulse_stream_cork(&self, stream: *mut pa_stream, state: CorkState) {
unsafe extern "C" fn cork_success(_: *mut pa_stream, _: i32, u: *mut c_void) {
let mainloop = u as *mut pa_threaded_mainloop;
pa_threaded_mainloop_signal(mainloop, 0);
}
if stream.is_null() {
return;
}
let o = unsafe {
pa_stream_cork(stream,
state.is_cork() as i32,
Some(cork_success),
self.mainloop as *mut _)
};
if !o.is_null() {
self.operation_wait(stream, o);
unsafe { pa_operation_unref(o) };
}
}
pub fn pulse_context_init(&mut self) -> i32 {
unsafe extern "C" fn error_state(c: *mut pa_context, u: *mut c_void) {
let mut ctx = &mut *(u as *mut Context);
if !PA_CONTEXT_IS_GOOD(pa_context_get_state(c)) {
ctx.error = true;
}
pa_threaded_mainloop_signal(ctx.mainloop, 0);
}
if !self.context.is_null() {
debug_assert!(self.error);
unsafe { self.pulse_context_destroy() };
}
unsafe {
self.context = pa_context_new(pa_threaded_mainloop_get_api(self.mainloop),
self.context_name);
if self.context.is_null() {
return cubeb::ERROR;
}
pa_context_set_state_callback(self.context, Some(error_state), self as *mut _ as *mut _);
pa_threaded_mainloop_lock(self.mainloop);
pa_context_connect(self.context, ptr::null(), 0, ptr::null());
if !self.wait_until_context_ready() {
pa_threaded_mainloop_unlock(self.mainloop);
self.pulse_context_destroy();
self.context = ptr::null_mut();
return cubeb::ERROR;
}
pa_threaded_mainloop_unlock(self.mainloop);
}
let version_str = unsafe { CStr::from_ptr(pa_get_library_version()) };
if let Ok(version) = semver::Version::parse(version_str.to_string_lossy().as_ref()) {
self.version_0_9_8 = version >= semver::Version::parse("0.9.8").expect("Failed to parse version");
self.version_2_0_0 = version >= semver::Version::parse("2.0.0").expect("Failed to parse version");
}
self.error = false;
cubeb::OK
}
unsafe fn pulse_context_destroy(&mut self) {
unsafe extern "C" fn drain_complete(_c: *mut pa_context, u: *mut c_void) {
let mainloop = u as *mut pa_threaded_mainloop;
pa_threaded_mainloop_signal(mainloop, 0);
}
pa_threaded_mainloop_lock(self.mainloop);
let o = pa_context_drain(self.context, Some(drain_complete), self.mainloop as *mut _);
if !o.is_null() {
self.operation_wait(ptr::null_mut(), o);
pa_operation_unref(o);
}
pa_context_set_state_callback(self.context, None, ptr::null_mut());
pa_context_disconnect(self.context);
pa_context_unref(self.context);
pa_threaded_mainloop_unlock(self.mainloop);
}
pub fn operation_wait(&self, stream: *mut pa_stream, o: *mut pa_operation) -> bool {
unsafe {
while pa_operation_get_state(o) == PA_OPERATION_RUNNING {
pa_threaded_mainloop_wait(self.mainloop);
if !PA_CONTEXT_IS_GOOD(pa_context_get_state(self.context)) {
return false;
}
if !stream.is_null() && !PA_STREAM_IS_GOOD(pa_stream_get_state(stream)) {
return false;
}
}
}
true
}
pub fn wait_until_context_ready(&self) -> bool {
loop {
let state = unsafe { pa_context_get_state(self.context) };
if !PA_CONTEXT_IS_GOOD(state) {
return false;
}
if state == PA_CONTEXT_READY {
break;
}
unsafe {
pa_threaded_mainloop_wait(self.mainloop);
}
}
true
}
fn state_from_sink_port(&self, i: *const pa_port_info) -> cubeb::DeviceState {
if !i.is_null() {
let info = unsafe { *i };
if self.version_2_0_0 && info.available == PA_PORT_AVAILABLE_NO {
cubeb::DeviceState::Unplugged
} else {
cubeb::DeviceState::Enabled
}
} else {
cubeb::DeviceState::Enabled
}
}
fn state_from_source_port(&self, i: *mut pa_port_info) -> cubeb::DeviceState {
if !i.is_null() {
let info = unsafe { *i };
if self.version_2_0_0 && info.available == PA_PORT_AVAILABLE_NO {
cubeb::DeviceState::Unplugged
} else {
cubeb::DeviceState::Enabled
}
} else {
cubeb::DeviceState::Enabled
}
}
}
// Callbacks
unsafe extern "C" fn server_info_callback(context: *mut pa_context, info: *const pa_server_info, u: *mut c_void) {
unsafe extern "C" fn sink_info_callback(_context: *mut pa_context,
info: *const pa_sink_info,
eol: i32,
u: *mut c_void) {
let mut ctx = &mut *(u as *mut Context);
if eol == 0 {
if !ctx.default_sink_info.is_null() {
let _ = Box::from_raw(ctx.default_sink_info);
}
ctx.default_sink_info = Box::into_raw(Box::new(*info));
}
pa_threaded_mainloop_signal(ctx.mainloop, 0);
}
pa_context_get_sink_info_by_name(context,
(*info).default_sink_name,
Some(sink_info_callback),
u);
}
struct PulseDevListData {
default_sink_name: *mut c_char,
default_source_name: *mut c_char,
devinfo: Vec<*const cubeb::DeviceInfo>,
context: *mut Context,
}
impl Drop for PulseDevListData {
fn drop(&mut self) {
for elem in &mut self.devinfo {
let _ = unsafe { Box::from_raw(elem) };
}
if !self.default_sink_name.is_null() {
unsafe {
pa_xfree(self.default_sink_name as *mut _);
}
}
if !self.default_source_name.is_null() {
unsafe {
pa_xfree(self.default_source_name as *mut _);
}
}
}
}
impl Default for PulseDevListData {
fn default() -> Self {
PulseDevListData {
default_sink_name: ptr::null_mut(),
default_source_name: ptr::null_mut(),
devinfo: Vec::new(),
context: ptr::null_mut(),
}
}
}
fn pulse_format_to_cubeb_format(format: pa_sample_format_t) -> cubeb::DeviceFmt {
match format {
PA_SAMPLE_S16LE => cubeb::DEVICE_FMT_S16LE,
PA_SAMPLE_S16BE => cubeb::DEVICE_FMT_S16BE,
PA_SAMPLE_FLOAT32LE => cubeb::DEVICE_FMT_F32LE,
PA_SAMPLE_FLOAT32BE => cubeb::DEVICE_FMT_F32BE,
_ => {
panic!("Invalid format");
},
}
}
unsafe extern "C" fn pulse_sink_info_cb(_context: *mut pa_context,
i: *const pa_sink_info,
eol: i32,
user_data: *mut c_void) {
if eol != 0 || i.is_null() {
return;
}
debug_assert!(!user_data.is_null());
let info = *i;
let mut list_data = &mut *(user_data as *mut PulseDevListData);
let device_id = pa_xstrdup(info.name);
let group_id = {
let prop = pa_proplist_gets(info.proplist, b"sysfs.path\0".as_ptr() as *const c_char);
if !prop.is_null() {
pa_xstrdup(prop)
} else {
ptr::null_mut()
}
};
let vendor_name = {
let prop = pa_proplist_gets(info.proplist,
b"device.vendor.name\0".as_ptr() as *const c_char);
if !prop.is_null() {
pa_xstrdup(prop)
} else {
ptr::null_mut()
}
};
let preferred = if strcmp(info.name, list_data.default_sink_name) == 0 {
cubeb::DEVICE_PREF_ALL
} else {
cubeb::DevicePref::empty()
};
let ctx = &(*list_data.context);
let devinfo = cubeb::DeviceInfo {
device_id: device_id,
devid: device_id as cubeb::DeviceId,
friendly_name: pa_xstrdup(info.description),
group_id: group_id,
vendor_name: vendor_name,
devtype: cubeb::DEVICE_TYPE_OUTPUT,
state: ctx.state_from_sink_port(info.active_port),
preferred: preferred,
format: cubeb::DeviceFmt::all(),
default_format: pulse_format_to_cubeb_format(info.sample_spec.format),
max_channels: info.channel_map.channels as u32,
min_rate: 1,
max_rate: PA_RATE_MAX,
default_rate: info.sample_spec.rate,
latency_lo: 0,
latency_hi: 0,
};
list_data.devinfo.push(Box::into_raw(Box::new(devinfo)));
pa_threaded_mainloop_signal(ctx.mainloop, 0);
}
unsafe extern "C" fn pulse_source_info_cb(_context: *mut pa_context,
i: *const pa_source_info,
eol: i32,
user_data: *mut c_void) {
if eol != 0 || i.is_null() {
return;
}
debug_assert!(!user_data.is_null());
let info = *i;
let mut list_data = &mut *(user_data as *mut PulseDevListData);
let device_id = pa_xstrdup(info.name);
let group_id = {
let prop = pa_proplist_gets(info.proplist, b"sysfs.path\0".as_ptr() as *mut c_char);
if !prop.is_null() {
pa_xstrdup(prop)
} else {
ptr::null_mut()
}
};
let vendor_name = {
let prop = pa_proplist_gets(info.proplist,
b"device.vendor.name\0".as_ptr() as *mut c_char);
if !prop.is_null() {
pa_xstrdup(prop)
} else {
ptr::null_mut()
}
};
let preferred = if strcmp(info.name, list_data.default_source_name) == 0 {
cubeb::DEVICE_PREF_ALL
} else {
cubeb::DevicePref::empty()
};
let ctx = &(*list_data.context);
let devinfo = cubeb::DeviceInfo {
device_id: device_id,
devid: device_id as cubeb::DeviceId,
friendly_name: pa_xstrdup(info.description),
group_id: group_id,
vendor_name: vendor_name,
devtype: cubeb::DEVICE_TYPE_INPUT,
state: ctx.state_from_source_port(info.active_port),
preferred: preferred,
format: cubeb::DeviceFmt::all(),
default_format: pulse_format_to_cubeb_format(info.sample_spec.format),
max_channels: info.channel_map.channels as u32,
min_rate: 1,
max_rate: PA_RATE_MAX,
default_rate: info.sample_spec.rate,
latency_lo: 0,
latency_hi: 0,
};
list_data.devinfo.push(Box::into_raw(Box::new(devinfo)));
pa_threaded_mainloop_signal(ctx.mainloop, 0);
}
unsafe extern "C" fn pulse_server_info_cb(_context: *mut pa_context,
i: *const pa_server_info,
user_data: *mut c_void) {
assert!(!i.is_null());
let info = *i;
let list_data = &mut *(user_data as *mut PulseDevListData);
dup_str!(list_data.default_sink_name, info.default_sink_name);
dup_str!(list_data.default_source_name, info.default_source_name);
pa_threaded_mainloop_signal((*list_data.context).mainloop, 0);
}
unsafe extern "C" fn pulse_subscribe_callback(_ctx: *mut pa_context,
t: pa_subscription_event_type_t,
index: u32,
user_data: *mut c_void) {
let mut ctx = &mut *(user_data as *mut Context);
match t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK {
PA_SUBSCRIPTION_EVENT_SOURCE |
PA_SUBSCRIPTION_EVENT_SINK => {
if cubeb::g_cubeb_log_level != cubeb::LogLevel::Disabled {
if (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE {
log!("Removing sink index %d", index);
} else if (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW {
log!("Adding sink index %d", index);
}
if (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE {
log!("Removing source index %d", index);
} else if (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW {
log!("Adding source index %d", index);
}
}
if (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE ||
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW {
ctx.collection_changed_callback.unwrap()(ctx as *mut _ as *mut _, ctx.collection_changed_user_ptr);
}
},
_ => {},
}
}
extern "C" {
pub fn strcmp(cs: *const c_char, ct: *const c_char) -> c_int;
}

View File

@ -0,0 +1,89 @@
// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use std::ops;
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CorkState(u32);
const UNCORK: u32 = 0b00;
const CORK: u32 = 0b01;
const NOTIFY: u32 = 0b10;
const ALL: u32 = 0b11;
impl CorkState {
#[inline]
pub fn uncork() -> Self {
CorkState(UNCORK)
}
#[inline]
pub fn cork() -> Self {
CorkState(CORK)
}
#[inline]
pub fn notify() -> Self {
CorkState(NOTIFY)
}
#[inline]
pub fn is_cork(&self) -> bool {
self.contains(CorkState::cork())
}
#[inline]
pub fn is_notify(&self) -> bool {
self.contains(CorkState::notify())
}
#[inline]
pub fn contains(&self, other: Self) -> bool {
(*self & other) == other
}
}
impl ops::BitOr for CorkState {
type Output = CorkState;
#[inline]
fn bitor(self, other: Self) -> Self {
CorkState(self.0 | other.0)
}
}
impl ops::BitXor for CorkState {
type Output = CorkState;
#[inline]
fn bitxor(self, other: Self) -> Self {
CorkState(self.0 ^ other.0)
}
}
impl ops::BitAnd for CorkState {
type Output = CorkState;
#[inline]
fn bitand(self, other: Self) -> Self {
CorkState(self.0 & other.0)
}
}
impl ops::Sub for CorkState {
type Output = CorkState;
#[inline]
fn sub(self, other: Self) -> Self {
CorkState(self.0 & !other.0)
}
}
impl ops::Not for CorkState {
type Output = CorkState;
#[inline]
fn not(self) -> Self {
CorkState(!self.0 & ALL)
}
}

View File

@ -0,0 +1,15 @@
// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
mod context;
mod cork_state;
mod stream;
mod var_array;
pub type Result<T> = ::std::result::Result<T, i32>;
pub use self::context::Context;
pub use self::stream::Device;
pub use self::stream::Stream;

View File

@ -0,0 +1,814 @@
// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use backend::*;
use backend::cork_state::CorkState;
use cubeb;
use pulse_ffi::*;
use std::os::raw::{c_char, c_long, c_void};
use std::ptr;
const PULSE_NO_GAIN: f32 = -1.0;
fn cubeb_channel_to_pa_channel(channel: cubeb::Channel) -> pa_channel_position_t {
assert_ne!(channel, cubeb::CHANNEL_INVALID);
// This variable may be used for multiple times, so we should avoid to
// allocate it in stack, or it will be created and removed repeatedly.
// Use static to allocate this local variable in data space instead of stack.
static MAP: [pa_channel_position_t; 10] = [
// PA_CHANNEL_POSITION_INVALID, // CHANNEL_INVALID
PA_CHANNEL_POSITION_MONO, // CHANNEL_MONO
PA_CHANNEL_POSITION_FRONT_LEFT, // CHANNEL_LEFT
PA_CHANNEL_POSITION_FRONT_RIGHT, // CHANNEL_RIGHT
PA_CHANNEL_POSITION_FRONT_CENTER, // CHANNEL_CENTER
PA_CHANNEL_POSITION_SIDE_LEFT, // CHANNEL_LS
PA_CHANNEL_POSITION_SIDE_RIGHT, // CHANNEL_RS
PA_CHANNEL_POSITION_REAR_LEFT, // CHANNEL_RLS
PA_CHANNEL_POSITION_REAR_CENTER, // CHANNEL_RCENTER
PA_CHANNEL_POSITION_REAR_RIGHT, // CHANNEL_RRS
PA_CHANNEL_POSITION_LFE // CHANNEL_LFE
];
let idx: i32 = channel.into();
MAP[idx as usize]
}
fn layout_to_channel_map(layout: cubeb::ChannelLayout) -> pa_channel_map {
assert_ne!(layout, cubeb::LAYOUT_UNDEFINED);
let order = cubeb::mixer::channel_index_to_order(layout);
let mut cm: pa_channel_map = Default::default();
unsafe {
pa_channel_map_init(&mut cm);
}
cm.channels = order.len() as u8;
for (s, d) in order.iter().zip(cm.map.iter_mut()) {
*d = cubeb_channel_to_pa_channel(*s);
}
cm
}
pub struct Device(cubeb::Device);
impl Drop for Device {
fn drop(&mut self) {
unsafe {
pa_xfree(self.0.input_name as *mut _);
pa_xfree(self.0.output_name as *mut _);
}
}
}
#[derive(Debug)]
pub struct Stream<'ctx> {
context: &'ctx Context,
output_stream: *mut pa_stream,
input_stream: *mut pa_stream,
data_callback: cubeb::DataCallback,
state_callback: cubeb::StateCallback,
user_ptr: *mut c_void,
drain_timer: *mut pa_time_event,
output_sample_spec: pa_sample_spec,
input_sample_spec: pa_sample_spec,
shutdown: bool,
volume: f32,
state: cubeb::State,
}
impl<'ctx> Drop for Stream<'ctx> {
fn drop(&mut self) {
self.destroy();
}
}
impl<'ctx> Stream<'ctx> {
pub fn new(context: &'ctx Context,
stream_name: *const c_char,
input_device: cubeb::DeviceId,
input_stream_params: Option<cubeb::StreamParams>,
output_device: cubeb::DeviceId,
output_stream_params: Option<cubeb::StreamParams>,
latency_frames: u32,
data_callback: cubeb::DataCallback,
state_callback: cubeb::StateCallback,
user_ptr: *mut c_void)
-> Result<Box<Stream<'ctx>>> {
let mut stm = Box::new(Stream {
context: context,
output_stream: ptr::null_mut(),
input_stream: ptr::null_mut(),
data_callback: data_callback,
state_callback: state_callback,
user_ptr: user_ptr,
drain_timer: ptr::null_mut(),
output_sample_spec: pa_sample_spec::default(),
input_sample_spec: pa_sample_spec::default(),
shutdown: false,
volume: PULSE_NO_GAIN,
state: cubeb::STATE_ERROR,
});
unsafe {
pa_threaded_mainloop_lock(stm.context.mainloop);
if let Some(ref stream_params) = output_stream_params {
match stm.pulse_stream_init(stream_params, stream_name) {
Ok(s) => stm.output_stream = s,
Err(e) => {
pa_threaded_mainloop_unlock(stm.context.mainloop);
stm.destroy();
return Err(e);
},
}
stm.output_sample_spec = *pa_stream_get_sample_spec(stm.output_stream);
pa_stream_set_state_callback(stm.output_stream,
Some(stream_state_callback),
stm.as_mut() as *mut _ as *mut _);
pa_stream_set_write_callback(stm.output_stream,
Some(stream_write_callback),
stm.as_mut() as *mut _ as *mut _);
let battr = set_buffering_attribute(latency_frames, &stm.output_sample_spec);
pa_stream_connect_playback(stm.output_stream,
output_device as *mut c_char,
&battr,
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
PA_STREAM_START_CORKED |
PA_STREAM_ADJUST_LATENCY,
ptr::null(),
ptr::null_mut());
}
// Set up input stream
if let Some(ref stream_params) = input_stream_params {
match stm.pulse_stream_init(stream_params, stream_name) {
Ok(s) => stm.input_stream = s,
Err(e) => {
pa_threaded_mainloop_unlock(stm.context.mainloop);
stm.destroy();
return Err(e);
},
}
stm.input_sample_spec = *(pa_stream_get_sample_spec(stm.input_stream));
pa_stream_set_state_callback(stm.input_stream,
Some(stream_state_callback),
stm.as_mut() as *mut _ as *mut _);
pa_stream_set_read_callback(stm.input_stream,
Some(stream_read_callback),
stm.as_mut() as *mut _ as *mut _);
let battr = set_buffering_attribute(latency_frames, &stm.input_sample_spec);
pa_stream_connect_record(stm.input_stream,
input_device as *mut c_char,
&battr,
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
PA_STREAM_START_CORKED |
PA_STREAM_ADJUST_LATENCY);
}
let r = if stm.wait_until_stream_ready() {
/* force a timing update now, otherwise timing info does not become valid
until some point after initialization has completed. */
stm.update_timing_info()
} else {
false
};
pa_threaded_mainloop_unlock(stm.context.mainloop);
if !r {
stm.destroy();
return Err(cubeb::ERROR);
}
if cubeb::g_cubeb_log_level != cubeb::LogLevel::Disabled {
if output_stream_params.is_some() {
let output_att = *pa_stream_get_buffer_attr(stm.output_stream);
log!("Output buffer attributes maxlength %u, tlength %u, \
prebuf %u, minreq %u, fragsize %u",
output_att.maxlength,
output_att.tlength,
output_att.prebuf,
output_att.minreq,
output_att.fragsize);
}
if input_stream_params.is_some() {
let input_att = *pa_stream_get_buffer_attr(stm.input_stream);
log!("Input buffer attributes maxlength %u, tlength %u, \
prebuf %u, minreq %u, fragsize %u",
input_att.maxlength,
input_att.tlength,
input_att.prebuf,
input_att.minreq,
input_att.fragsize);
}
}
}
Ok(stm)
}
fn destroy(&mut self) {
self.stream_cork(CorkState::cork());
unsafe {
pa_threaded_mainloop_lock(self.context.mainloop);
if !self.output_stream.is_null() {
if !self.drain_timer.is_null() {
/* there's no pa_rttime_free, so use this instead. */
let ma = pa_threaded_mainloop_get_api(self.context.mainloop);
if !ma.is_null() {
(*ma).time_free.unwrap()(self.drain_timer);
}
}
pa_stream_set_state_callback(self.output_stream, None, ptr::null_mut());
pa_stream_set_write_callback(self.output_stream, None, ptr::null_mut());
pa_stream_disconnect(self.output_stream);
pa_stream_unref(self.output_stream);
}
if !self.input_stream.is_null() {
pa_stream_set_state_callback(self.input_stream, None, ptr::null_mut());
pa_stream_set_read_callback(self.input_stream, None, ptr::null_mut());
pa_stream_disconnect(self.input_stream);
pa_stream_unref(self.input_stream);
}
pa_threaded_mainloop_unlock(self.context.mainloop);
}
}
pub fn start(&mut self) -> i32 {
self.shutdown = false;
self.stream_cork(CorkState::uncork() | CorkState::notify());
if !self.output_stream.is_null() && self.input_stream.is_null() {
unsafe {
/* On output only case need to manually call user cb once in order to make
* things roll. This is done via a defer event in order to execute it
* from PA server thread. */
pa_threaded_mainloop_lock(self.context.mainloop);
pa_mainloop_api_once(pa_threaded_mainloop_get_api(self.context.mainloop),
Some(pulse_defer_event_cb),
self as *mut _ as *mut _);
pa_threaded_mainloop_unlock(self.context.mainloop);
}
}
cubeb::OK
}
pub fn stop(&mut self) -> i32 {
unsafe {
pa_threaded_mainloop_lock(self.context.mainloop);
self.shutdown = true;
// If draining is taking place wait to finish
while !self.drain_timer.is_null() {
pa_threaded_mainloop_wait(self.context.mainloop);
}
pa_threaded_mainloop_unlock(self.context.mainloop);
}
self.stream_cork(CorkState::cork() | CorkState::notify());
cubeb::OK
}
pub fn position(&self) -> Result<u64> {
if self.output_stream.is_null() {
return Err(cubeb::ERROR);
}
let position = unsafe {
let in_thread = pa_threaded_mainloop_in_thread(self.context.mainloop);
if in_thread == 0 {
pa_threaded_mainloop_lock(self.context.mainloop);
}
let mut r_usec: pa_usec_t = Default::default();
let r = pa_stream_get_time(self.output_stream, &mut r_usec);
if in_thread == 0 {
pa_threaded_mainloop_unlock(self.context.mainloop);
}
if r != 0 {
return Err(cubeb::ERROR);
}
let bytes = pa_usec_to_bytes(r_usec, &self.output_sample_spec);
(bytes / pa_frame_size(&self.output_sample_spec)) as u64
};
Ok(position)
}
pub fn latency(&self) -> Result<u32> {
if self.output_stream.is_null() {
return Err(cubeb::ERROR);
}
let mut r_usec: pa_usec_t = 0;
let mut negative: i32 = 0;
let r = unsafe { pa_stream_get_latency(self.output_stream, &mut r_usec, &mut negative) };
if r != 0 {
return Err(cubeb::ERROR);
}
debug_assert_eq!(negative, 0);
let latency = (r_usec * self.output_sample_spec.rate as pa_usec_t / PA_USEC_PER_SEC) as u32;
Ok(latency)
}
pub fn set_volume(&mut self, volume: f32) -> i32 {
if self.output_stream.is_null() {
return cubeb::ERROR;
}
unsafe {
pa_threaded_mainloop_lock(self.context.mainloop);
while self.context.default_sink_info.is_null() {
pa_threaded_mainloop_wait(self.context.mainloop);
}
let mut cvol: pa_cvolume = Default::default();
/* if the pulse daemon is configured to use flat volumes,
* apply our own gain instead of changing the input volume on the sink. */
if ((*self.context.default_sink_info).flags & PA_SINK_FLAT_VOLUME) != 0 {
self.volume = volume;
} else {
let ss = pa_stream_get_sample_spec(self.output_stream);
let vol = pa_sw_volume_from_linear(volume as f64);
pa_cvolume_set(&mut cvol, (*ss).channels as u32, vol);
let index = pa_stream_get_index(self.output_stream);
let op = pa_context_set_sink_input_volume(self.context.context,
index,
&cvol,
Some(volume_success),
self as *mut _ as *mut _);
if !op.is_null() {
self.context.operation_wait(self.output_stream, op);
pa_operation_unref(op);
}
}
pa_threaded_mainloop_unlock(self.context.mainloop);
}
cubeb::OK
}
pub fn set_panning(&mut self, panning: f32) -> i32 {
if self.output_stream.is_null() {
return cubeb::ERROR;
}
unsafe {
pa_threaded_mainloop_lock(self.context.mainloop);
let map = pa_stream_get_channel_map(self.output_stream);
if pa_channel_map_can_balance(map) == 0 {
pa_threaded_mainloop_unlock(self.context.mainloop);
return cubeb::ERROR;
}
let index = pa_stream_get_index(self.output_stream);
let mut cvol: pa_cvolume = Default::default();
let mut r = SinkInputInfoResult {
cvol: &mut cvol,
mainloop: self.context.mainloop,
};
let op = pa_context_get_sink_input_info(self.context.context,
index,
Some(sink_input_info_cb),
&mut r as *mut _ as *mut _);
if !op.is_null() {
self.context.operation_wait(self.output_stream, op);
pa_operation_unref(op);
}
pa_cvolume_set_balance(&mut cvol, map, panning);
let op = pa_context_set_sink_input_volume(self.context.context,
index,
&cvol,
Some(volume_success),
self as *mut _ as *mut _);
if !op.is_null() {
self.context.operation_wait(self.output_stream, op);
pa_operation_unref(op);
}
pa_threaded_mainloop_unlock(self.context.mainloop);
}
cubeb::OK
}
pub fn current_device(&self) -> Result<Box<cubeb::Device>> {
if self.context.version_0_9_8 {
let mut dev = Box::new(cubeb::Device::default());
if !self.input_stream.is_null() {
dev.input_name = unsafe { pa_xstrdup(pa_stream_get_device_name(self.input_stream)) };
}
if !self.output_stream.is_null() {
dev.output_name = unsafe { pa_xstrdup(pa_stream_get_device_name(self.output_stream)) };
}
Ok(dev)
} else {
Err(cubeb::ERROR_NOT_SUPPORTED)
}
}
fn pulse_stream_init(&mut self,
stream_params: &cubeb::StreamParams,
stream_name: *const c_char)
-> Result<*mut pa_stream> {
fn to_pulse_format(format: cubeb::SampleFormat) -> pa_sample_format_t {
match format {
cubeb::SAMPLE_S16LE => PA_SAMPLE_S16LE,
cubeb::SAMPLE_S16BE => PA_SAMPLE_S16BE,
cubeb::SAMPLE_FLOAT32LE => PA_SAMPLE_FLOAT32LE,
cubeb::SAMPLE_FLOAT32BE => PA_SAMPLE_FLOAT32BE,
_ => panic!("Invalid format: {:?}", format),
}
}
let fmt = to_pulse_format(stream_params.format);
if fmt == PA_SAMPLE_INVALID {
return Err(cubeb::ERROR_INVALID_FORMAT);
}
let ss = pa_sample_spec {
channels: stream_params.channels as u8,
format: fmt,
rate: stream_params.rate,
};
let cm = layout_to_channel_map(stream_params.layout);
let stream = unsafe { pa_stream_new(self.context.context, stream_name, &ss, &cm) };
if !stream.is_null() {
Ok(stream)
} else {
Err(cubeb::ERROR)
}
}
fn stream_cork(&mut self, state: CorkState) {
unsafe { pa_threaded_mainloop_lock(self.context.mainloop) };
self.context.pulse_stream_cork(self.output_stream, state);
self.context.pulse_stream_cork(self.input_stream, state);
unsafe { pa_threaded_mainloop_unlock(self.context.mainloop) };
if state.is_notify() {
self.state_change_callback(if state.is_cork() {
cubeb::STATE_STOPPED
} else {
cubeb::STATE_STARTED
});
}
}
fn update_timing_info(&self) -> bool {
let mut r = false;
if !self.output_stream.is_null() {
let o = unsafe {
pa_stream_update_timing_info(self.output_stream,
Some(stream_success_callback),
self as *const _ as *mut _)
};
if !o.is_null() {
r = self.context.operation_wait(self.output_stream, o);
unsafe {
pa_operation_unref(o);
}
}
if !r {
return r;
}
}
if !self.input_stream.is_null() {
let o = unsafe {
pa_stream_update_timing_info(self.input_stream,
Some(stream_success_callback),
self as *const _ as *mut _)
};
if !o.is_null() {
r = self.context.operation_wait(self.input_stream, o);
unsafe {
pa_operation_unref(o);
}
}
}
r
}
pub fn state_change_callback(&mut self, s: cubeb::State) {
self.state = s;
unsafe {
(self.state_callback.unwrap())(self as *mut Stream as *mut cubeb::Stream, self.user_ptr, s);
}
}
fn wait_until_stream_ready(&self) -> bool {
if !self.output_stream.is_null() && !wait_until_io_stream_ready(self.output_stream, self.context.mainloop) {
return false;
}
if !self.input_stream.is_null() && !wait_until_io_stream_ready(self.input_stream, self.context.mainloop) {
return false;
}
true
}
fn trigger_user_callback(&mut self, s: *mut pa_stream, input_data: *const c_void, nbytes: usize) {
let frame_size = unsafe { pa_frame_size(&self.output_sample_spec) };
debug_assert_eq!(nbytes % frame_size, 0);
let mut buffer: *mut c_void = ptr::null_mut();
let mut r: i32;
let mut towrite = nbytes;
let mut read_offset = 0usize;
while towrite > 0 {
let mut size = towrite;
r = unsafe { pa_stream_begin_write(s, &mut buffer, &mut size) };
// Note: this has failed running under rr on occassion - needs investigation.
debug_assert_eq!(r, 0);
debug_assert!(size > 0);
debug_assert_eq!(size % frame_size, 0);
logv!("Trigger user callback with output buffer size={}, read_offset={}",
size,
read_offset);
let read_ptr = unsafe { (input_data as *const u8).offset(read_offset as isize) };
let got = unsafe {
self.data_callback.unwrap()(self as *const _ as *mut _,
self.user_ptr,
read_ptr as *const _ as *mut _,
buffer,
(size / frame_size) as c_long)
};
if got < 0 {
unsafe {
pa_stream_cancel_write(s);
}
self.shutdown = true;
return;
}
// If more iterations move offset of read buffer
if !input_data.is_null() {
let in_frame_size = unsafe { pa_frame_size(&self.input_sample_spec) };
read_offset += (size / frame_size) * in_frame_size;
}
if self.volume != PULSE_NO_GAIN {
let samples = (self.output_sample_spec.channels as usize * size / frame_size) as isize;
if self.output_sample_spec.format == PA_SAMPLE_S16BE ||
self.output_sample_spec.format == PA_SAMPLE_S16LE {
let b = buffer as *mut i16;
for i in 0..samples {
unsafe { *b.offset(i) *= self.volume as i16 };
}
} else {
let b = buffer as *mut f32;
for i in 0..samples {
unsafe { *b.offset(i) *= self.volume };
}
}
}
r = unsafe {
pa_stream_write(s,
buffer,
got as usize * frame_size,
None,
0,
PA_SEEK_RELATIVE)
};
debug_assert_eq!(r, 0);
if (got as usize) < size / frame_size {
let mut latency: pa_usec_t = 0;
let rr: i32 = unsafe { pa_stream_get_latency(s, &mut latency, ptr::null_mut()) };
if rr == -(PA_ERR_NODATA as i32) {
/* this needs a better guess. */
latency = 100 * PA_USEC_PER_MSEC;
}
debug_assert!(r == 0 || r == -(PA_ERR_NODATA as i32));
/* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */
/* arbitrary safety margin: double the current latency. */
debug_assert!(self.drain_timer.is_null());
self.drain_timer = unsafe {
pa_context_rttime_new(self.context.context,
pa_rtclock_now() + 2 * latency,
Some(stream_drain_callback),
self as *const _ as *mut _)
};
self.shutdown = true;
return;
}
towrite -= size;
}
debug_assert_eq!(towrite, 0);
}
}
unsafe extern "C" fn stream_success_callback(_s: *mut pa_stream, _success: i32, u: *mut c_void) {
let stm = &*(u as *mut Stream);
pa_threaded_mainloop_signal(stm.context.mainloop, 0);
}
unsafe extern "C" fn stream_drain_callback(a: *mut pa_mainloop_api,
e: *mut pa_time_event,
_tv: *const timeval,
u: *mut c_void) {
let mut stm = &mut *(u as *mut Stream);
debug_assert_eq!(stm.drain_timer, e);
stm.state_change_callback(cubeb::STATE_DRAINED);
/* there's no pa_rttime_free, so use this instead. */
(*a).time_free.unwrap()(stm.drain_timer);
stm.drain_timer = ptr::null_mut();
pa_threaded_mainloop_signal(stm.context.mainloop, 0);
}
unsafe extern "C" fn stream_state_callback(s: *mut pa_stream, u: *mut c_void) {
let stm = &mut *(u as *mut Stream);
if !PA_STREAM_IS_GOOD(pa_stream_get_state(s)) {
stm.state_change_callback(cubeb::STATE_ERROR);
}
pa_threaded_mainloop_signal(stm.context.mainloop, 0);
}
fn read_from_input(s: *mut pa_stream, buffer: *mut *const c_void, size: *mut usize) -> i32 {
let readable_size = unsafe { pa_stream_readable_size(s) };
if readable_size > 0 && unsafe { pa_stream_peek(s, buffer, size) } < 0 {
return -1;
}
readable_size as i32
}
unsafe extern "C" fn stream_write_callback(s: *mut pa_stream, nbytes: usize, u: *mut c_void) {
logv!("Output callback to be written buffer size {}", nbytes);
let mut stm = &mut *(u as *mut Stream);
if stm.shutdown || stm.state != cubeb::STATE_STARTED {
return;
}
if stm.input_stream.is_null() {
// Output/playback only operation.
// Write directly to output
debug_assert!(!stm.output_stream.is_null());
stm.trigger_user_callback(s, ptr::null(), nbytes);
}
}
unsafe extern "C" fn stream_read_callback(s: *mut pa_stream, nbytes: usize, u: *mut c_void) {
logv!("Input callback buffer size {}", nbytes);
let mut stm = &mut *(u as *mut Stream);
if stm.shutdown {
return;
}
let mut read_data: *const c_void = ptr::null();
let mut read_size: usize = 0;
while read_from_input(s, &mut read_data, &mut read_size) > 0 {
/* read_data can be NULL in case of a hole. */
if !read_data.is_null() {
let in_frame_size = pa_frame_size(&stm.input_sample_spec);
let read_frames = read_size / in_frame_size;
if !stm.output_stream.is_null() {
// input/capture + output/playback operation
let out_frame_size = pa_frame_size(&stm.output_sample_spec);
let write_size = read_frames * out_frame_size;
// Offer full duplex data for writing
let stream = stm.output_stream;
stm.trigger_user_callback(stream, read_data, write_size);
} else {
// input/capture only operation. Call callback directly
let got = stm.data_callback.unwrap()(stm as *mut _ as *mut _,
stm.user_ptr,
read_data,
ptr::null_mut(),
read_frames as c_long);
if got < 0 || got as usize != read_frames {
pa_stream_cancel_write(s);
stm.shutdown = true;
break;
}
}
}
if read_size > 0 {
pa_stream_drop(s);
}
if stm.shutdown {
return;
}
}
}
fn wait_until_io_stream_ready(stream: *mut pa_stream, mainloop: *mut pa_threaded_mainloop) -> bool {
if stream.is_null() || mainloop.is_null() {
return false;
}
loop {
let state = unsafe { pa_stream_get_state(stream) };
if !PA_STREAM_IS_GOOD(state) {
return false;
}
if state == PA_STREAM_READY {
break;
}
unsafe { pa_threaded_mainloop_wait(mainloop) };
}
true
}
fn set_buffering_attribute(latency_frames: u32, sample_spec: &pa_sample_spec) -> pa_buffer_attr {
let tlength = latency_frames * unsafe { pa_frame_size(sample_spec) } as u32;
let minreq = tlength / 4;
let battr = pa_buffer_attr {
maxlength: u32::max_value(),
prebuf: u32::max_value(),
tlength: tlength,
minreq: minreq,
fragsize: minreq,
};
log!("Requested buffer attributes maxlength {}, tlength {}, prebuf {}, minreq {}, fragsize {}",
battr.maxlength,
battr.tlength,
battr.prebuf,
battr.minreq,
battr.fragsize);
battr
}
unsafe extern "C" fn pulse_defer_event_cb(_a: *mut pa_mainloop_api, u: *mut c_void) {
let mut stm = &mut *(u as *mut Stream);
if stm.shutdown {
return;
}
let writable_size = pa_stream_writable_size(stm.output_stream);
let stream = stm.output_stream;
stm.trigger_user_callback(stream, ptr::null_mut(), writable_size);
}
#[repr(C)]
struct SinkInputInfoResult {
pub cvol: *mut pa_cvolume,
pub mainloop: *mut pa_threaded_mainloop,
}
unsafe extern "C" fn sink_input_info_cb(_c: *mut pa_context, i: *const pa_sink_input_info, eol: i32, u: *mut c_void) {
let info = &*i;
let mut r = &mut *(u as *mut SinkInputInfoResult);
if eol == 0 {
*r.cvol = info.volume;
}
pa_threaded_mainloop_signal(r.mainloop, 0);
}
unsafe extern "C" fn volume_success(_c: *mut pa_context, success: i32, u: *mut c_void) {
let stm = &*(u as *mut Stream);
debug_assert_ne!(success, 0);
pa_threaded_mainloop_signal(stm.context.mainloop, 0);
}

View File

@ -0,0 +1,56 @@
/// A C-style variable length array implemented as one allocation with
/// a prepended header.
// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use pulse_ffi::pa_xrealloc;
use std::ptr;
#[repr(C)]
#[derive(Debug)]
pub struct VarArray<T> {
len: u32,
data: [T; 0],
}
impl<T> VarArray<T> {
pub fn len(&self) -> usize {
self.len as usize
}
unsafe fn _realloc(ptr: Option<Box<Self>>, count: usize) -> Box<Self> {
use std::mem::{size_of, transmute};
let size = size_of::<Self>() + count * size_of::<T>();
let raw_ptr = match ptr {
Some(box_ptr) => Box::into_raw(box_ptr) as *mut u8,
None => ptr::null_mut(),
};
let mem = pa_xrealloc(raw_ptr as *mut _, size);
let mut result: Box<Self> = transmute(mem);
result.len = count as u32;
result
}
pub fn with_length(len: usize) -> Box<VarArray<T>> {
unsafe { Self::_realloc(None, len) }
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
use std::slice::from_raw_parts_mut;
unsafe { from_raw_parts_mut(&self.data as *const _ as *mut _, self.len()) }
}
}
impl<T> Drop for VarArray<T> {
fn drop(&mut self) {
let ptr = self as *mut Self;
unsafe {
Self::_realloc(Some(Box::from_raw(ptr)), 0);
}
}
}

View File

@ -0,0 +1,240 @@
// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use backend;
use cubeb;
use std::os::raw::{c_char, c_void};
unsafe extern "C" fn capi_init(c: *mut *mut cubeb::Context, context_name: *const c_char) -> i32 {
match backend::Context::new(context_name) {
Ok(ctx) => {
*c = Box::into_raw(ctx) as *mut _;
cubeb::OK
},
Err(e) => e,
}
}
extern "C" fn capi_get_backend_id(_: *mut cubeb::Context) -> *const c_char {
"pulse-rust\0".as_ptr() as *const c_char
}
unsafe extern "C" fn capi_get_max_channel_count(c: *mut cubeb::Context, max_channels: *mut u32) -> i32 {
let ctx = &*(c as *mut backend::Context);
match ctx.max_channel_count() {
Ok(mc) => {
*max_channels = mc;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_get_min_latency(c: *mut cubeb::Context,
param: cubeb::StreamParams,
latency_frames: *mut u32)
-> i32 {
let ctx = &*(c as *mut backend::Context);
match ctx.min_latency(&param) {
Ok(l) => {
*latency_frames = l;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_get_preferred_sample_rate(c: *mut cubeb::Context, rate: *mut u32) -> i32 {
let ctx = &*(c as *mut backend::Context);
match ctx.preferred_sample_rate() {
Ok(r) => {
*rate = r;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_get_preferred_channel_layout(c: *mut cubeb::Context,
layout: *mut cubeb::ChannelLayout)
-> i32 {
let ctx = &*(c as *mut backend::Context);
match ctx.preferred_channel_layout() {
Ok(l) => {
*layout = l;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_enumerate_devices(c: *mut cubeb::Context,
devtype: cubeb::DeviceType,
collection: *mut *mut cubeb::DeviceCollection)
-> i32 {
let ctx = &*(c as *mut backend::Context);
match ctx.enumerate_devices(devtype) {
Ok(dc) => {
*collection = dc;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_destroy(c: *mut cubeb::Context) {
let _: Box<backend::Context> = Box::from_raw(c as *mut _);
}
unsafe extern "C" fn capi_stream_init(c: *mut cubeb::Context,
s: *mut *mut cubeb::Stream,
stream_name: *const c_char,
input_device: cubeb::DeviceId,
input_stream_params: *mut cubeb::StreamParams,
output_device: cubeb::DeviceId,
output_stream_params: *mut cubeb::StreamParams,
latency_frames: u32,
data_callback: cubeb::DataCallback,
state_callback: cubeb::StateCallback,
user_ptr: *mut c_void)
-> i32 {
let mut ctx = &mut *(c as *mut backend::Context);
match ctx.new_stream(stream_name,
input_device,
if input_stream_params.is_null() {
None
} else {
Some(*input_stream_params)
},
output_device,
if output_stream_params.is_null() {
None
} else {
Some(*output_stream_params)
},
latency_frames,
data_callback,
state_callback,
user_ptr as _) {
Ok(stm) => {
*s = Box::into_raw(stm) as *mut _;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_stream_destroy(s: *mut cubeb::Stream) {
let _ = Box::from_raw(s as *mut backend::Stream);
}
unsafe extern "C" fn capi_stream_start(s: *mut cubeb::Stream) -> i32 {
let mut stm = &mut *(s as *mut backend::Stream);
stm.start()
}
unsafe extern "C" fn capi_stream_stop(s: *mut cubeb::Stream) -> i32 {
let mut stm = &mut *(s as *mut backend::Stream);
stm.stop()
}
unsafe extern "C" fn capi_stream_get_position(s: *mut cubeb::Stream, position: *mut u64) -> i32 {
let stm = &*(s as *mut backend::Stream);
match stm.position() {
Ok(pos) => {
*position = pos;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_stream_get_latency(s: *mut cubeb::Stream, latency: *mut u32) -> i32 {
let stm = &*(s as *mut backend::Stream);
match stm.latency() {
Ok(lat) => {
*latency = lat;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_stream_set_volume(s: *mut cubeb::Stream, volume: f32) -> i32 {
let stm = &mut *(s as *mut backend::Stream);
stm.set_volume(volume)
}
unsafe extern "C" fn capi_stream_set_panning(s: *mut cubeb::Stream, panning: f32) -> i32 {
let stm = &mut *(s as *mut backend::Stream);
stm.set_panning(panning)
}
unsafe extern "C" fn capi_stream_get_current_device(s: *mut cubeb::Stream, device: *mut *const cubeb::Device) -> i32 {
let stm = &*(s as *mut backend::Stream);
match stm.current_device() {
Ok(d) => {
*device = Box::into_raw(d) as *mut _;
cubeb::OK
},
Err(e) => e,
}
}
unsafe extern "C" fn capi_stream_device_destroy(_: *mut cubeb::Stream, device: *mut cubeb::Device) -> i32 {
let _: Box<backend::Device> = Box::from_raw(device as *mut backend::Device);
cubeb::OK
}
unsafe extern "C" fn capi_register_device_collection_changed(c: *mut cubeb::Context,
devtype: cubeb::DeviceType,
collection_changed_callback:
cubeb::DeviceCollectionChangedCallback,
user_ptr: *mut c_void) -> i32
{
let mut ctx = &mut *(c as *mut backend::Context);
ctx.register_device_collection_changed(devtype, collection_changed_callback, user_ptr as _)
}
pub const PULSE_OPS: cubeb::Ops = cubeb::Ops {
init: Some(capi_init),
get_backend_id: Some(capi_get_backend_id),
get_max_channel_count: Some(capi_get_max_channel_count),
get_min_latency: Some(capi_get_min_latency),
get_preferred_sample_rate: Some(capi_get_preferred_sample_rate),
get_preferred_channel_layout: Some(capi_get_preferred_channel_layout),
enumerate_devices: Some(capi_enumerate_devices),
destroy: Some(capi_destroy),
stream_init: Some(capi_stream_init),
stream_destroy: Some(capi_stream_destroy),
stream_start: Some(capi_stream_start),
stream_stop: Some(capi_stream_stop),
stream_get_position: Some(capi_stream_get_position),
stream_get_latency: Some(capi_stream_get_latency),
stream_set_volume: Some(capi_stream_set_volume),
stream_set_panning: Some(capi_stream_set_panning),
stream_get_current_device: Some(capi_stream_get_current_device),
stream_device_destroy: Some(capi_stream_device_destroy),
stream_register_device_changed_callback: None,
register_device_collection_changed: Some(capi_register_device_collection_changed),
};
#[no_mangle]
pub unsafe extern "C" fn pulse_rust_init(c: *mut *mut cubeb::Context, context_name: *const c_char) -> i32 {
capi_init(c, context_name)
}

View File

@ -0,0 +1,16 @@
//! Cubeb backend interface to Pulse Audio
// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
#[macro_use]
extern crate cubeb_ffi as cubeb;
extern crate pulse_ffi;
extern crate semver;
mod capi;
mod backend;
pub use capi::pulse_rust_init;

View File

@ -0,0 +1,33 @@
# Usage: sh update.sh <upstream_src_directory>
set -e
cp -p $1/AUTHORS .
cp -p $1/LICENSE .
cp -p $1/README.md .
cp -p $1/Cargo.toml .
test -d src || mkdir -p src
cp -pr $1/src/* src/
test -d cubeb-ffi/src || mkdir -p cubeb-ffi/src
cp -pr $1/cubeb-ffi/Cargo.toml cubeb-ffi/
cp -pr $1/cubeb-ffi/src/* cubeb-ffi/src/
test -d pulse-ffi/src || mkdir -p pulse-ffi/src
cp -pr $1/pulse-ffi/Cargo.toml pulse-ffi/
cp -pr $1/pulse-ffi/src/* pulse-ffi/src/
if [ -d $1/.git ]; then
rev=$(cd $1 && git rev-parse --verify HEAD)
date=$(cd $1 && git show -s --format=%ci HEAD)
dirty=$(cd $1 && git diff-index --name-only HEAD)
fi
if [ -n "$rev" ]; then
version=$rev
if [ -n "$dirty" ]; then
version=$version-dirty
echo "WARNING: updating from a dirty git repository."
fi
sed -i.bak -e "/The git commit ID used was/ s/[0-9a-f]\{40\}\(-dirty\)\{0,1\} .\{1,100\}/$version ($date)/" README_MOZILLA
rm README_MOZILLA.bak
else
echo "Remember to update README_MOZILLA with the version details."
fi

View File

@ -0,0 +1 @@
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"f9b1ca6ae27d1c18215265024629a8960c31379f206d9ed20f64e0b2dcf79805","Cargo.toml":"67597114802114d2a7fdb457c1cf5f7e0c951b21e287c6a47b9a86b9028cf64d","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"d38feaa4f9468cd1e0ece22e0ad2eadfe6195a9a0a3843b7c722d5c7d81804fb","src/common.rs":"dc42336abd34e19ca9f732f33657e106f98dcc8c10d4c2564bc4f160cb31926e","src/lib.rs":"3ac8ef5a280344a25cb18ac386034c0fee8d64060fa14af5e25ed49f0cb2fd9e","src/range.rs":"3596f048d466d43887aff1e8c8c834476672a4627631ed35379c35466b5f02ec","src/recognize.rs":"9f16eda9fcd7d8af7eee4c3b89c611bd648040273fde6b35778f8a50b004c8b1","src/version.rs":"dbd91a4e4fd92a0aa9eb4f858ecbc1ecd680aa60572cc2ad2085e5c5c30e5b77"},"package":"388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"}

View File

View File

@ -0,0 +1,2 @@
target
Cargo.lock

View File

@ -0,0 +1,11 @@
[package]
name = "semver-parser"
version = "0.7.0"
authors = ["Steve Klabnik <steve@steveklabnik.com>"]
license = "MIT/Apache-2.0"
repository = "https://github.com/steveklabnik/semver-parser"
homepage = "https://github.com/steveklabnik/semver-parser"
documentation = "https://docs.rs/semver-parser"
description = """
Parsing of the semver spec.
"""

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -0,0 +1,25 @@
Copyright (c) 2016 Steve Klabnik
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,66 @@
use version::Identifier;
use recognize::{Recognize, Alt, OneOrMore, Inclusive, OneByte};
use std::str::from_utf8;
// by the time we get here, we know that it's all valid characters, so this doesn't need to return
// a result or anything
fn parse_meta(s: &str) -> Vec<Identifier> {
// Originally, I wanted to implement this method via calling parse, but parse is tolerant of
// leading zeroes, and we want anything with leading zeroes to be considered alphanumeric, not
// numeric. So the strategy is to check with a recognizer first, and then call parse once
// we've determined that it's a number without a leading zero.
s.split(".")
.map(|part| {
// another wrinkle: we made sure that any number starts with a
// non-zero. But there's a problem: an actual zero is a number, yet
// gets left out by this heuristic. So let's also check for the
// single, lone zero.
if is_alpha_numeric(part) {
Identifier::AlphaNumeric(part.to_string())
} else {
// we can unwrap here because we know it is only digits due to the regex
Identifier::Numeric(part.parse().unwrap())
}
}).collect()
}
// parse optional metadata (preceded by the prefix character)
pub fn parse_optional_meta(s: &[u8], prefix_char: u8)-> Result<(Vec<Identifier>, usize), String> {
if let Some(len) = prefix_char.p(s) {
let start = len;
if let Some(len) = letters_numbers_dash_dot(&s[start..]) {
let end = start + len;
Ok((parse_meta(from_utf8(&s[start..end]).unwrap()), end))
} else {
Err("Error parsing prerelease".to_string())
}
} else {
Ok((Vec::new(), 0))
}
}
pub fn is_alpha_numeric(s: &str) -> bool {
if let Some((_val, len)) = numeric_identifier(s.as_bytes()) {
// Return true for number with leading zero
// Note: doing it this way also handily makes overflow fail over.
len != s.len()
} else {
true
}
}
// Note: could plumb overflow error up to return value as Result
pub fn numeric_identifier(s: &[u8]) -> Option<(u64, usize)> {
if let Some(len) = Alt(b'0', OneOrMore(Inclusive(b'0'..b'9'))).p(s) {
from_utf8(&s[0..len]).unwrap().parse().ok().map(|val| (val, len))
} else {
None
}
}
pub fn letters_numbers_dash_dot(s: &[u8]) -> Option<usize> {
OneOrMore(OneByte(|c| c == b'-' || c == b'.' ||
(b'0' <= c && c <= b'9') ||
(b'a' <= c && c <= b'z') ||
(b'A' <= c && c <= b'Z'))).p(s)
}

View File

@ -0,0 +1,8 @@
pub mod version;
pub mod range;
// for private stuff the two share
mod common;
// for recognizer combinators
mod recognize;

View File

@ -0,0 +1,696 @@
use common::{self, numeric_identifier, letters_numbers_dash_dot};
use version::Identifier;
use std::str::{FromStr, from_utf8};
use recognize::*;
#[derive(Debug)]
pub struct VersionReq {
pub predicates: Vec<Predicate>,
}
#[derive(PartialEq,Debug)]
pub enum WildcardVersion {
Major,
Minor,
Patch,
}
#[derive(PartialEq,Debug)]
pub enum Op {
Ex, // Exact
Gt, // Greater than
GtEq, // Greater than or equal to
Lt, // Less than
LtEq, // Less than or equal to
Tilde, // e.g. ~1.0.0
Compatible, // compatible by definition of semver, indicated by ^
Wildcard(WildcardVersion), // x.y.*, x.*, *
}
impl FromStr for Op {
type Err = String;
fn from_str(s: &str) -> Result<Op, String> {
match s {
"=" => Ok(Op::Ex),
">" => Ok(Op::Gt),
">=" => Ok(Op::GtEq),
"<" => Ok(Op::Lt),
"<=" => Ok(Op::LtEq),
"~" => Ok(Op::Tilde),
"^" => Ok(Op::Compatible),
_ => Err(String::from("Could not parse Op")),
}
}
}
#[derive(PartialEq,Debug)]
pub struct Predicate {
pub op: Op,
pub major: u64,
pub minor: Option<u64>,
pub patch: Option<u64>,
pub pre: Vec<Identifier>,
}
fn numeric_or_wild(s: &[u8]) -> Option<(Option<u64>, usize)> {
if let Some((val, len)) = numeric_identifier(s) {
Some((Some(val), len))
} else if let Some(len) = OneOf(b"*xX").p(s) {
Some((None, len))
} else {
None
}
}
fn dot_numeric_or_wild(s: &[u8]) -> Option<(Option<u64>, usize)> {
b'.'.p(s).and_then(|len|
numeric_or_wild(&s[len..]).map(|(val, len2)| (val, len + len2))
)
}
fn operation(s: &[u8]) -> Option<(Op, usize)> {
if let Some(len) = "=".p(s) {
Some((Op::Ex, len))
} else if let Some(len) = ">=".p(s) {
Some((Op::GtEq, len))
} else if let Some(len) = ">".p(s) {
Some((Op::Gt, len))
} else if let Some(len) = "<=".p(s) {
Some((Op::LtEq, len))
} else if let Some(len) = "<".p(s) {
Some((Op::Lt, len))
} else if let Some(len) = "~".p(s) {
Some((Op::Tilde, len))
} else if let Some(len) = "^".p(s) {
Some((Op::Compatible, len))
} else {
None
}
}
fn whitespace(s: &[u8]) -> Option<usize> {
ZeroOrMore(OneOf(b"\t\r\n ")).p(s)
}
pub fn parse_predicate(range: &str) -> Result<Predicate, String> {
let s = range.trim().as_bytes();
let mut i = 0;
let mut operation = if let Some((op, len)) = operation(&s[i..]) {
i += len;
op
} else {
// operations default to Compatible
Op::Compatible
};
if let Some(len) = whitespace.p(&s[i..]) {
i += len;
}
let major = if let Some((major, len)) = numeric_identifier(&s[i..]) {
i += len;
major
} else {
return Err("Error parsing major version number: ".to_string());
};
let minor = if let Some((minor, len)) = dot_numeric_or_wild(&s[i..]) {
i += len;
if minor.is_none() {
operation = Op::Wildcard(WildcardVersion::Minor);
}
minor
} else {
None
};
let patch = if let Some((patch, len)) = dot_numeric_or_wild(&s[i..]) {
i += len;
if patch.is_none() {
operation = Op::Wildcard(WildcardVersion::Patch);
}
patch
} else {
None
};
let (pre, pre_len) = common::parse_optional_meta(&s[i..], b'-')?;
i += pre_len;
if let Some(len) = (b'+', letters_numbers_dash_dot).p(&s[i..]) {
i += len;
}
if i != s.len() {
return Err("Extra junk after valid predicate: ".to_string() +
from_utf8(&s[i..]).unwrap());
}
Ok(Predicate {
op: operation,
major: major,
minor: minor,
patch: patch,
pre: pre,
})
}
pub fn parse(ranges: &str) -> Result<VersionReq, String> {
// null is an error
if ranges == "\0" {
return Err(String::from("Null is not a valid VersionReq"));
}
// an empty range is a major version wildcard
// so is a lone * or x of either capitalization
if (ranges == "")
|| (ranges == "*")
|| (ranges == "x")
|| (ranges == "X") {
return Ok(VersionReq {
predicates: vec![Predicate {
op: Op::Wildcard(WildcardVersion::Major),
major: 0,
minor: None,
patch: None,
pre: Vec::new(),
}],
});
}
let ranges = ranges.trim();
let predicates: Result<Vec<_>, String> = ranges
.split(",")
.map(|range| {
parse_predicate(range)
})
.collect();
let predicates = try!(predicates);
if predicates.len() == 0 {
return Err(String::from("VersionReq did not parse properly"));
}
Ok(VersionReq {
predicates: predicates,
})
}
#[cfg(test)]
mod tests {
use super::*;
use range;
use version::Identifier;
#[test]
fn test_parsing_default() {
let r = range::parse("1.0.0").unwrap();
assert_eq!(Predicate {
op: Op::Compatible,
major: 1,
minor: Some(0),
patch: Some(0),
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
fn test_parsing_exact_01() {
let r = range::parse("=1.0.0").unwrap();
assert_eq!(Predicate {
op: Op::Ex,
major: 1,
minor: Some(0),
patch: Some(0),
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
fn test_parsing_exact_02() {
let r = range::parse("=0.9.0").unwrap();
assert_eq!(Predicate {
op: Op::Ex,
major: 0,
minor: Some(9),
patch: Some(0),
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
fn test_parsing_exact_03() {
let r = range::parse("=0.1.0-beta2.a").unwrap();
assert_eq!(Predicate {
op: Op::Ex,
major: 0,
minor: Some(1),
patch: Some(0),
pre: vec![Identifier::AlphaNumeric(String::from("beta2")),
Identifier::AlphaNumeric(String::from("a"))],
},
r.predicates[0]
);
}
#[test]
pub fn test_parsing_greater_than() {
let r = range::parse("> 1.0.0").unwrap();
assert_eq!(Predicate {
op: Op::Gt,
major: 1,
minor: Some(0),
patch: Some(0),
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
pub fn test_parsing_greater_than_01() {
let r = range::parse(">= 1.0.0").unwrap();
assert_eq!(Predicate {
op: Op::GtEq,
major: 1,
minor: Some(0),
patch: Some(0),
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
pub fn test_parsing_greater_than_02() {
let r = range::parse(">= 2.1.0-alpha2").unwrap();
assert_eq!(Predicate {
op: Op::GtEq,
major: 2,
minor: Some(1),
patch: Some(0),
pre: vec![Identifier::AlphaNumeric(String::from("alpha2"))],
},
r.predicates[0]
);
}
#[test]
pub fn test_parsing_less_than() {
let r = range::parse("< 1.0.0").unwrap();
assert_eq!(Predicate {
op: Op::Lt,
major: 1,
minor: Some(0),
patch: Some(0),
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
pub fn test_parsing_less_than_eq() {
let r = range::parse("<= 2.1.0-alpha2").unwrap();
assert_eq!(Predicate {
op: Op::LtEq,
major: 2,
minor: Some(1),
patch: Some(0),
pre: vec![Identifier::AlphaNumeric(String::from("alpha2"))],
},
r.predicates[0]
);
}
#[test]
pub fn test_parsing_tilde() {
let r = range::parse("~1").unwrap();
assert_eq!(Predicate {
op: Op::Tilde,
major: 1,
minor: None,
patch: None,
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
pub fn test_parsing_compatible() {
let r = range::parse("^0").unwrap();
assert_eq!(Predicate {
op: Op::Compatible,
major: 0,
minor: None,
patch: None,
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
fn test_parsing_blank() {
let r = range::parse("").unwrap();
assert_eq!(Predicate {
op: Op::Wildcard(WildcardVersion::Major),
major: 0,
minor: None,
patch: None,
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
fn test_parsing_wildcard() {
let r = range::parse("*").unwrap();
assert_eq!(Predicate {
op: Op::Wildcard(WildcardVersion::Major),
major: 0,
minor: None,
patch: None,
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
fn test_parsing_x() {
let r = range::parse("x").unwrap();
assert_eq!(Predicate {
op: Op::Wildcard(WildcardVersion::Major),
major: 0,
minor: None,
patch: None,
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
fn test_parsing_capital_x() {
let r = range::parse("X").unwrap();
assert_eq!(Predicate {
op: Op::Wildcard(WildcardVersion::Major),
major: 0,
minor: None,
patch: None,
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
fn test_parsing_minor_wildcard_star() {
let r = range::parse("1.*").unwrap();
assert_eq!(Predicate {
op: Op::Wildcard(WildcardVersion::Minor),
major: 1,
minor: None,
patch: None,
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
fn test_parsing_minor_wildcard_x() {
let r = range::parse("1.x").unwrap();
assert_eq!(Predicate {
op: Op::Wildcard(WildcardVersion::Minor),
major: 1,
minor: None,
patch: None,
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
fn test_parsing_minor_wildcard_capital_x() {
let r = range::parse("1.X").unwrap();
assert_eq!(Predicate {
op: Op::Wildcard(WildcardVersion::Minor),
major: 1,
minor: None,
patch: None,
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
fn test_parsing_patch_wildcard_star() {
let r = range::parse("1.2.*").unwrap();
assert_eq!(Predicate {
op: Op::Wildcard(WildcardVersion::Patch),
major: 1,
minor: Some(2),
patch: None,
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
fn test_parsing_patch_wildcard_x() {
let r = range::parse("1.2.x").unwrap();
assert_eq!(Predicate {
op: Op::Wildcard(WildcardVersion::Patch),
major: 1,
minor: Some(2),
patch: None,
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
fn test_parsing_patch_wildcard_capital_x() {
let r = range::parse("1.2.X").unwrap();
assert_eq!(Predicate {
op: Op::Wildcard(WildcardVersion::Patch),
major: 1,
minor: Some(2),
patch: None,
pre: Vec::new(),
},
r.predicates[0]
);
}
#[test]
pub fn test_multiple_01() {
let r = range::parse("> 0.0.9, <= 2.5.3").unwrap();
assert_eq!(Predicate {
op: Op::Gt,
major: 0,
minor: Some(0),
patch: Some(9),
pre: Vec::new(),
},
r.predicates[0]
);
assert_eq!(Predicate {
op: Op::LtEq,
major: 2,
minor: Some(5),
patch: Some(3),
pre: Vec::new(),
},
r.predicates[1]
);
}
#[test]
pub fn test_multiple_02() {
let r = range::parse("0.3.0, 0.4.0").unwrap();
assert_eq!(Predicate {
op: Op::Compatible,
major: 0,
minor: Some(3),
patch: Some(0),
pre: Vec::new(),
},
r.predicates[0]
);
assert_eq!(Predicate {
op: Op::Compatible,
major: 0,
minor: Some(4),
patch: Some(0),
pre: Vec::new(),
},
r.predicates[1]
);
}
#[test]
pub fn test_multiple_03() {
let r = range::parse("<= 0.2.0, >= 0.5.0").unwrap();
assert_eq!(Predicate {
op: Op::LtEq,
major: 0,
minor: Some(2),
patch: Some(0),
pre: Vec::new(),
},
r.predicates[0]
);
assert_eq!(Predicate {
op: Op::GtEq,
major: 0,
minor: Some(5),
patch: Some(0),
pre: Vec::new(),
},
r.predicates[1]
);
}
#[test]
pub fn test_multiple_04() {
let r = range::parse("0.1.0, 0.1.4, 0.1.6").unwrap();
assert_eq!(Predicate {
op: Op::Compatible,
major: 0,
minor: Some(1),
patch: Some(0),
pre: Vec::new(),
},
r.predicates[0]
);
assert_eq!(Predicate {
op: Op::Compatible,
major: 0,
minor: Some(1),
patch: Some(4),
pre: Vec::new(),
},
r.predicates[1]
);
assert_eq!(Predicate {
op: Op::Compatible,
major: 0,
minor: Some(1),
patch: Some(6),
pre: Vec::new(),
},
r.predicates[2]
);
}
#[test]
pub fn test_multiple_05() {
let r = range::parse(">=0.5.1-alpha3, <0.6").unwrap();
assert_eq!(Predicate {
op: Op::GtEq,
major: 0,
minor: Some(5),
patch: Some(1),
pre: vec![Identifier::AlphaNumeric(String::from("alpha3"))],
},
r.predicates[0]
);
assert_eq!(Predicate {
op: Op::Lt,
major: 0,
minor: Some(6),
patch: None,
pre: Vec::new(),
},
r.predicates[1]
);
}
#[test]
fn test_parse_build_metadata_with_predicate() {
assert_eq!(range::parse("^1.2.3+meta").unwrap().predicates[0].op,
Op::Compatible);
assert_eq!(range::parse("~1.2.3+meta").unwrap().predicates[0].op,
Op::Tilde);
assert_eq!(range::parse("=1.2.3+meta").unwrap().predicates[0].op,
Op::Ex);
assert_eq!(range::parse("<=1.2.3+meta").unwrap().predicates[0].op,
Op::LtEq);
assert_eq!(range::parse(">=1.2.3+meta").unwrap().predicates[0].op,
Op::GtEq);
assert_eq!(range::parse("<1.2.3+meta").unwrap().predicates[0].op,
Op::Lt);
assert_eq!(range::parse(">1.2.3+meta").unwrap().predicates[0].op,
Op::Gt);
}
#[test]
pub fn test_parse_errors() {
assert!(range::parse("\0").is_err());
assert!(range::parse(">= >= 0.0.2").is_err());
assert!(range::parse(">== 0.0.2").is_err());
assert!(range::parse("a.0.0").is_err());
assert!(range::parse("1.0.0-").is_err());
assert!(range::parse(">=").is_err());
assert!(range::parse("> 0.1.0,").is_err());
assert!(range::parse("> 0.3.0, ,").is_err());
}
#[test]
pub fn test_large_major_version() {
assert!(range::parse("18446744073709551617.0.0").is_err());
}
#[test]
pub fn test_large_minor_version() {
assert!(range::parse("0.18446744073709551617.0").is_err());
}
#[test]
pub fn test_large_patch_version() {
assert!(range::parse("0.0.18446744073709551617").is_err());
}
}

View File

@ -0,0 +1,154 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under either of MIT or Apache License, Version 2.0,
// at your option.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
//
// 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.
//! Simple recognizer combinators.
// This version is similar to a similar one in the "lang" module of
// xi-editor, but is stripped down to only the needed combinators.
use std::ops;
pub trait Recognize {
fn p(&self, s: &[u8]) -> Option<usize>;
}
impl<F: Fn(&[u8]) -> Option<usize>> Recognize for F {
#[inline(always)]
fn p(&self, s: &[u8]) -> Option<usize> {
self(s)
}
}
pub struct OneByte<F>(pub F);
impl<F: Fn(u8) -> bool> Recognize for OneByte<F> {
#[inline(always)]
fn p(&self, s: &[u8]) -> Option<usize> {
if s.is_empty() || !self.0(s[0]) {
None
} else {
Some(1)
}
}
}
impl Recognize for u8 {
#[inline(always)]
fn p(&self, s: &[u8]) -> Option<usize> {
OneByte(|b| b == *self).p(s)
}
}
/// Use Inclusive(a..b) to indicate an inclusive range. When a...b syntax becomes
/// stable, we can get rid of this and switch to that.
pub struct Inclusive<T>(pub T);
impl Recognize for Inclusive<ops::Range<u8>> {
#[inline(always)]
fn p(&self, s: &[u8]) -> Option<usize> {
OneByte(|x| x >= self.0.start && x <= self.0.end).p(s)
}
}
impl<'a> Recognize for &'a [u8] {
#[inline(always)]
fn p(&self, s: &[u8]) -> Option<usize> {
let len = self.len();
if s.len() >= len && &s[..len] == *self {
Some(len)
} else {
None
}
}
}
impl<'a> Recognize for &'a str {
#[inline(always)]
fn p(&self, s: &[u8]) -> Option<usize> {
self.as_bytes().p(s)
}
}
impl<P1: Recognize, P2: Recognize> Recognize for (P1, P2) {
#[inline(always)]
fn p(&self, s: &[u8]) -> Option<usize> {
self.0.p(s).and_then(|len1|
self.1.p(&s[len1..]).map(|len2|
len1 + len2))
}
}
/// Choice from two heterogeneous alternatives.
pub struct Alt<P1, P2>(pub P1, pub P2);
impl<P1: Recognize, P2: Recognize> Recognize for Alt<P1, P2> {
#[inline(always)]
fn p(&self, s: &[u8]) -> Option<usize> {
self.0.p(s).or_else(|| self.1.p(s))
}
}
/// Choice from a homogenous slice of parsers.
pub struct OneOf<'a, P: 'a>(pub &'a [P]);
impl<'a, P: Recognize> Recognize for OneOf<'a, P> {
#[inline]
fn p(&self, s: &[u8]) -> Option<usize> {
for ref p in self.0 {
if let Some(len) = p.p(s) {
return Some(len);
}
}
None
}
}
pub struct OneOrMore<P>(pub P);
impl<P: Recognize> Recognize for OneOrMore<P> {
#[inline]
fn p(&self, s: &[u8]) -> Option<usize> {
let mut i = 0;
let mut count = 0;
while let Some(len) = self.0.p(&s[i..]) {
i += len;
count += 1;
}
if count >= 1 {
Some(i)
} else {
None
}
}
}
pub struct ZeroOrMore<P>(pub P);
impl<P: Recognize> Recognize for ZeroOrMore<P> {
#[inline]
fn p(&self, s: &[u8]) -> Option<usize> {
let mut i = 0;
while let Some(len) = self.0.p(&s[i..]) {
i += len;
}
Some(i)
}
}

View File

@ -0,0 +1,365 @@
use std::fmt;
use std::str::from_utf8;
use recognize::*;
use common::{self, numeric_identifier};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Version {
pub major: u64,
pub minor: u64,
pub patch: u64,
pub pre: Vec<Identifier>,
pub build: Vec<Identifier>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Identifier {
/// An identifier that's solely numbers.
Numeric(u64),
/// An identifier with letters and numbers.
AlphaNumeric(String),
}
pub fn parse(version: &str) -> Result<Version, String> {
let s = version.trim().as_bytes();
let mut i = 0;
let major = if let Some((major, len)) = numeric_identifier(&s[i..]) {
i += len;
major
} else {
return Err("Error parsing major identifier".to_string());
};
if let Some(len) = b'.'.p(&s[i..]) {
i += len;
} else {
return Err("Expected dot".to_string());
}
let minor = if let Some((minor, len)) = numeric_identifier(&s[i..]) {
i += len;
minor
} else {
return Err("Error parsing minor identifier".to_string());
};
if let Some(len) = b'.'.p(&s[i..]) {
i += len;
} else {
return Err("Expected dot".to_string());
}
let patch = if let Some((patch, len)) = numeric_identifier(&s[i..]) {
i += len;
patch
} else {
return Err("Error parsing patch identifier".to_string());
};
let (pre, pre_len) = common::parse_optional_meta(&s[i..], b'-')?;
i += pre_len;
let (build, build_len) = common::parse_optional_meta(&s[i..], b'+')?;
i += build_len;
if i != s.len() {
return Err("Extra junk after valid version: ".to_string() +
from_utf8(&s[i..]).unwrap());
}
Ok(Version {
major: major,
minor: minor,
patch: patch,
pre: pre,
build: build,
})
}
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "{}.{}.{}", self.major, self.minor, self.patch));
if !self.pre.is_empty() {
let strs: Vec<_> =
self.pre.iter().map(ToString::to_string).collect();
try!(write!(f, "-{}", strs.join(".")));
}
if !self.build.is_empty() {
let strs: Vec<_> =
self.build.iter().map(ToString::to_string).collect();
try!(write!(f, "+{}", strs.join(".")));
}
Ok(())
}
}
impl fmt::Display for Identifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Identifier::Numeric(ref id) => id.fmt(f),
Identifier::AlphaNumeric(ref id) => id.fmt(f),
}
}
}
#[cfg(test)]
mod tests {
use version;
use super::*;
#[test]
fn parse_empty() {
let version = "";
let parsed = version::parse(version);
assert!(parsed.is_err(), "empty string incorrectly considered a valid parse");
}
#[test]
fn parse_blank() {
let version = " ";
let parsed = version::parse(version);
assert!(parsed.is_err(), "blank string incorrectly considered a valid parse");
}
#[test]
fn parse_no_minor_patch() {
let version = "1";
let parsed = version::parse(version);
assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version));
}
#[test]
fn parse_no_patch() {
let version = "1.2";
let parsed = version::parse(version);
assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version));
}
#[test]
fn parse_empty_pre() {
let version = "1.2.3-";
let parsed = version::parse(version);
assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version));
}
#[test]
fn parse_letters() {
let version = "a.b.c";
let parsed = version::parse(version);
assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version));
}
#[test]
fn parse_with_letters() {
let version = "1.2.3 a.b.c";
let parsed = version::parse(version);
assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version));
}
#[test]
fn parse_basic_version() {
let version = "1.2.3";
let parsed = version::parse(version).unwrap();
assert_eq!(1, parsed.major);
assert_eq!(2, parsed.minor);
assert_eq!(3, parsed.patch);
}
#[test]
fn parse_trims_input() {
let version = " 1.2.3 ";
let parsed = version::parse(version).unwrap();
assert_eq!(1, parsed.major);
assert_eq!(2, parsed.minor);
assert_eq!(3, parsed.patch);
}
#[test]
fn parse_no_major_leading_zeroes() {
let version = "01.0.0";
let parsed = version::parse(version);
assert!(parsed.is_err(), "01 incorrectly considered a valid major version");
}
#[test]
fn parse_no_minor_leading_zeroes() {
let version = "0.01.0";
let parsed = version::parse(version);
assert!(parsed.is_err(), "01 incorrectly considered a valid minor version");
}
#[test]
fn parse_no_patch_leading_zeroes() {
let version = "0.0.01";
let parsed = version::parse(version);
assert!(parsed.is_err(), "01 incorrectly considered a valid patch version");
}
#[test]
fn parse_no_major_overflow() {
let version = "98765432109876543210.0.0";
let parsed = version::parse(version);
assert!(parsed.is_err(), "98765432109876543210 incorrectly considered a valid major version");
}
#[test]
fn parse_no_minor_overflow() {
let version = "0.98765432109876543210.0";
let parsed = version::parse(version);
assert!(parsed.is_err(), "98765432109876543210 incorrectly considered a valid minor version");
}
#[test]
fn parse_no_patch_overflow() {
let version = "0.0.98765432109876543210";
let parsed = version::parse(version);
assert!(parsed.is_err(), "98765432109876543210 incorrectly considered a valid patch version");
}
#[test]
fn parse_basic_prerelease() {
let version = "1.2.3-pre";
let parsed = version::parse(version).unwrap();
let expected_pre = vec![Identifier::AlphaNumeric(String::from("pre"))];
assert_eq!(expected_pre, parsed.pre);
}
#[test]
fn parse_prerelease_alphanumeric() {
let version = "1.2.3-alpha1";
let parsed = version::parse(version).unwrap();
let expected_pre = vec![Identifier::AlphaNumeric(String::from("alpha1"))];
assert_eq!(expected_pre, parsed.pre);
}
#[test]
fn parse_prerelease_zero() {
let version = "1.2.3-pre.0";
let parsed = version::parse(version).unwrap();
let expected_pre = vec![Identifier::AlphaNumeric(String::from("pre")),
Identifier::Numeric(0)];
assert_eq!(expected_pre, parsed.pre);
}
#[test]
fn parse_basic_build() {
let version = "1.2.3+build";
let parsed = version::parse(version).unwrap();
let expected_build = vec![Identifier::AlphaNumeric(String::from("build"))];
assert_eq!(expected_build, parsed.build);
}
#[test]
fn parse_build_alphanumeric() {
let version = "1.2.3+build5";
let parsed = version::parse(version).unwrap();
let expected_build = vec![Identifier::AlphaNumeric(String::from("build5"))];
assert_eq!(expected_build, parsed.build);
}
#[test]
fn parse_pre_and_build() {
let version = "1.2.3-alpha1+build5";
let parsed = version::parse(version).unwrap();
let expected_pre = vec![Identifier::AlphaNumeric(String::from("alpha1"))];
assert_eq!(expected_pre, parsed.pre);
let expected_build = vec![Identifier::AlphaNumeric(String::from("build5"))];
assert_eq!(expected_build, parsed.build);
}
#[test]
fn parse_complex_metadata_01() {
let version = "1.2.3-1.alpha1.9+build5.7.3aedf ";
let parsed = version::parse(version).unwrap();
let expected_pre = vec![Identifier::Numeric(1),
Identifier::AlphaNumeric(String::from("alpha1")),
Identifier::Numeric(9)];
assert_eq!(expected_pre, parsed.pre);
let expected_build = vec![Identifier::AlphaNumeric(String::from("build5")),
Identifier::Numeric(7),
Identifier::AlphaNumeric(String::from("3aedf"))];
assert_eq!(expected_build, parsed.build);
}
#[test]
fn parse_complex_metadata_02() {
let version = "0.4.0-beta.1+0851523";
let parsed = version::parse(version).unwrap();
let expected_pre = vec![Identifier::AlphaNumeric(String::from("beta")),
Identifier::Numeric(1)];
assert_eq!(expected_pre, parsed.pre);
let expected_build = vec![Identifier::AlphaNumeric(String::from("0851523"))];
assert_eq!(expected_build, parsed.build);
}
#[test]
fn parse_metadata_overflow() {
let version = "0.4.0-beta.1+98765432109876543210";
let parsed = version::parse(version).unwrap();
let expected_pre = vec![Identifier::AlphaNumeric(String::from("beta")),
Identifier::Numeric(1)];
assert_eq!(expected_pre, parsed.pre);
let expected_build = vec![Identifier::AlphaNumeric(String::from("98765432109876543210"))];
assert_eq!(expected_build, parsed.build);
}
#[test]
fn parse_regression_01() {
let version = "0.0.0-WIP";
let parsed = version::parse(version).unwrap();
assert_eq!(0, parsed.major);
assert_eq!(0, parsed.minor);
assert_eq!(0, parsed.patch);
let expected_pre = vec![Identifier::AlphaNumeric(String::from("WIP"))];
assert_eq!(expected_pre, parsed.pre);
}
}

View File

@ -0,0 +1 @@
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"9659c8f11da8480d67f59d6a5c5efcca98a55540af290691e1e4ac291d260c3f",".travis.yml":"c9f96f84fd07f4d2b4b76d20bc17a4bebe0ec6f527a0d8707bc40a94152b8c84","Cargo.toml":"d02f51526644dbe6c82add37202f246a6b95e185a5519ef0c3ca4d55a245f22c","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"bb80bfbd16bf7dc34de45891c21333c42590d5c5c10e8ba0ed59549fce5af1dd","src/lib.rs":"b33fcb9a79d46a581a4628f7c2bb00f7ba3451b1acfeeae0b38ff22532ed5b68","src/version.rs":"fc36d1e58c9a73f46fdec63283fd50ea78c89a0f816b8abaaa89438fb0c47c70","src/version_req.rs":"093f2ac08f2bc8fcf6a93ae0fc333eccc1ea04932e0c0b26835a952bb220eca1","tests/deprecation.rs":"b5ec79e19d61968d05b96b876c449e54d43cbd1762c6e63c23c3470f9db56292","tests/regression.rs":"180b699ad029b81e6135d42f0a8e6d782177bc29a41132f875ee6f8607a46b56"},"package":"7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"}

0
third_party/rust/semver/.cargo-ok vendored Normal file
View File

3
third_party/rust/semver/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
target/
*.sw?
Cargo.lock

18
third_party/rust/semver/.travis.yml vendored Normal file
View File

@ -0,0 +1,18 @@
language: rust
rust:
- nightly
- beta
- stable
sudo: false
script:
- cargo build --verbose
- cargo test --verbose --features ci
addons:
apt:
sources:
- kalakris-cmake
packages:
- cmake
notifications:
email:
on_success: never

25
third_party/rust/semver/Cargo.toml vendored Normal file
View File

@ -0,0 +1,25 @@
[package]
name = "semver"
version = "0.6.0"
authors = ["Steve Klabnik <steve@steveklabnik.com>", "The Rust Project Developers"]
license = "MIT/Apache-2.0"
repository = "https://github.com/steveklabnik/semver"
homepage = "https://docs.rs/crate/semver/"
documentation = "https://docs.rs/crate/semver/"
description = """
Semantic version parsing and comparison.
"""
[dependencies]
semver-parser = "0.7.0"
[features]
default = []
# are we testing on CI?
ci = []
[dev-dependencies]
crates-index = "0.5.0"
tempdir = "0.3.4"

201
third_party/rust/semver/LICENSE-APACHE vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

25
third_party/rust/semver/LICENSE-MIT vendored Normal file
View File

@ -0,0 +1,25 @@
Copyright (c) 2014 The Rust Project Developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

103
third_party/rust/semver/README.md vendored Normal file
View File

@ -0,0 +1,103 @@
semver
======
Semantic version parsing and comparison.
[![Build Status](https://api.travis-ci.org/steveklabnik/semver.svg?branch=master)](https://travis-ci.org/steveklabnik/semver)
[Documentation](https://steveklabnik.github.io/semver)
Semantic versioning (see http://semver.org/) is a set of rules for
assigning version numbers.
## SemVer and the Rust ecosystem
Rust itself follows the SemVer specification, as does its standard libraries. The two are
not tied together.
[Cargo](https://crates.io), Rust's package manager, uses SemVer to determine which versions of
packages you need installed.
## Installation
To use `semver`, add this to your `[dependencies]` section:
```toml
semver = "0.6.0"
```
And this to your crate root:
```rust
extern crate semver;
```
## Versions
At its simplest, the `semver` crate allows you to construct `Version` objects using the `parse`
method:
```rust
use semver::Version;
assert!(Version::parse("1.2.3") == Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: vec!(),
build: vec!(),
}));
```
If you have multiple `Version`s, you can use the usual comparison operators to compare them:
```rust
use semver::Version;
assert!(Version::parse("1.2.3-alpha") != Version::parse("1.2.3-beta"));
assert!(Version::parse("1.2.3-alpha2") > Version::parse("1.2.0"));
```
## Requirements
The `semver` crate also provides the ability to compare requirements, which are more complex
comparisons.
For example, creating a requirement that only matches versions greater than or
equal to 1.0.0:
```rust
use semver::Version;
use semver::VersionReq;
let r = VersionReq::parse(">= 1.0.0").unwrap();
let v = Version::parse("1.0.0").unwrap();
assert!(r.to_string() == ">= 1.0.0".to_string());
assert!(r.matches(&v))
```
It also allows parsing of `~x.y.z` and `^x.y.z` requirements as defined at
https://www.npmjs.org/doc/misc/semver.html
**Tilde requirements** specify a minimal version with some updates:
```notrust
~1.2.3 := >=1.2.3 <1.3.0
~1.2 := >=1.2.0 <1.3.0
~1 := >=1.0.0 <2.0.0
```
**Caret requirements** allow SemVer compatible updates to a specified version,
`0.x` and `0.x+1` are not considered compatible, but `1.x` and `1.x+1` are.
`0.0.x` is not considered compatible with any other version.
Missing minor and patch versions are desugared to `0` but allow flexibility for that value.
```notrust
^1.2.3 := >=1.2.3 <2.0.0
^0.2.3 := >=0.2.3 <0.3.0
^0.0.3 := >=0.0.3 <0.0.4
^0.0 := >=0.0.0 <0.1.0
^0 := >=0.0.0 <1.0.0
```

178
third_party/rust/semver/src/lib.rs vendored Normal file
View File

@ -0,0 +1,178 @@
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.
//! Semantic version parsing and comparison.
//!
//! Semantic versioning (see http://semver.org/) is a set of rules for
//! assigning version numbers.
//!
//! ## SemVer overview
//!
//! Given a version number MAJOR.MINOR.PATCH, increment the:
//!
//! 1. MAJOR version when you make incompatible API changes,
//! 2. MINOR version when you add functionality in a backwards-compatible
//! manner, and
//! 3. PATCH version when you make backwards-compatible bug fixes.
//!
//! Additional labels for pre-release and build metadata are available as
//! extensions to the MAJOR.MINOR.PATCH format.
//!
//! Any references to 'the spec' in this documentation refer to [version 2.0 of
//! the SemVer spec](http://semver.org/spec/v2.0.0.html).
//!
//! ## SemVer and the Rust ecosystem
//!
//! Rust itself follows the SemVer specification, as does its standard
//! libraries. The two are not tied together.
//!
//! [Cargo](http://crates.io), Rust's package manager, uses SemVer to determine
//! which versions of packages you need installed.
//!
//! ## Versions
//!
//! At its simplest, the `semver` crate allows you to construct `Version`
//! objects using the `parse` method:
//!
//! ```{rust}
//! use semver::Version;
//!
//! assert!(Version::parse("1.2.3") == Ok(Version {
//! major: 1,
//! minor: 2,
//! patch: 3,
//! pre: vec!(),
//! build: vec!(),
//! }));
//! ```
//!
//! If you have multiple `Version`s, you can use the usual comparison operators
//! to compare them:
//!
//! ```{rust}
//! use semver::Version;
//!
//! assert!(Version::parse("1.2.3-alpha") != Version::parse("1.2.3-beta"));
//! assert!(Version::parse("1.2.3-alpha2") > Version::parse("1.2.0"));
//! ```
//!
//! If you explicitly need to modify a Version, SemVer also allows you to
//! increment the major, minor, and patch numbers in accordance with the spec.
//!
//! Please note that in order to do this, you must use a mutable Version:
//!
//! ```{rust}
//! use semver::Version;
//!
//! let mut bugfix_release = Version::parse("1.0.0").unwrap();
//! bugfix_release.increment_patch();
//!
//! assert_eq!(Ok(bugfix_release), Version::parse("1.0.1"));
//! ```
//!
//! When incrementing the minor version number, the patch number resets to zero
//! (in accordance with section 7 of the spec)
//!
//! ```{rust}
//! use semver::Version;
//!
//! let mut feature_release = Version::parse("1.4.6").unwrap();
//! feature_release.increment_minor();
//!
//! assert_eq!(Ok(feature_release), Version::parse("1.5.0"));
//! ```
//!
//! Similarly, when incrementing the major version number, the patch and minor
//! numbers reset to zero (in accordance with section 8 of the spec)
//!
//! ```{rust}
//! use semver::Version;
//!
//! let mut chrome_release = Version::parse("41.5.5377").unwrap();
//! chrome_release.increment_major();
//!
//! assert_eq!(Ok(chrome_release), Version::parse("42.0.0"));
//! ```
//!
//! ## Requirements
//!
//! The `semver` crate also provides the ability to compare requirements, which
//! are more complex comparisons.
//!
//! For example, creating a requirement that only matches versions greater than
//! or equal to 1.0.0:
//!
//! ```{rust}
//! # #![allow(unstable)]
//! use semver::Version;
//! use semver::VersionReq;
//!
//! let r = VersionReq::parse(">= 1.0.0").unwrap();
//! let v = Version::parse("1.0.0").unwrap();
//!
//! assert!(r.to_string() == ">= 1.0.0".to_string());
//! assert!(r.matches(&v))
//! ```
//!
//! It also allows parsing of `~x.y.z` and `^x.y.z` requirements as defined at
//! https://www.npmjs.org/doc/misc/semver.html
//!
//! **Tilde requirements** specify a minimal version with some updates:
//!
//! ```notrust
//! ~1.2.3 := >=1.2.3 <1.3.0
//! ~1.2 := >=1.2.0 <1.3.0
//! ~1 := >=1.0.0 <2.0.0
//! ```
//!
//! **Caret requirements** allow SemVer compatible updates to a specified
//! verion, `0.x` and `0.x+1` are not considered compatible, but `1.x` and
//! `1.x+1` are.
//!
//! `0.0.x` is not considered compatible with any other version.
//! Missing minor and patch versions are desugared to `0` but allow flexibility
//! for that value.
//!
//! ```notrust
//! ^1.2.3 := >=1.2.3 <2.0.0
//! ^0.2.3 := >=0.2.3 <0.3.0
//! ^0.0.3 := >=0.0.3 <0.0.4
//! ^0.0 := >=0.0.0 <0.1.0
//! ^0 := >=0.0.0 <1.0.0
//! ```
//!
//! **Wildcard requirements** allows parsing of version requirements of the
//! formats `*`, `x.*` and `x.y.*`.
//!
//! ```notrust
//! * := >=0.0.0
//! 1.* := >=1.0.0 <2.0.0
//! 1.2.* := >=1.2.0 <1.3.0
//! ```
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://www.rust-lang.org/favicon.ico")]
#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]
extern crate semver_parser;
// We take the common approach of keeping our own module system private, and
// just re-exporting the interface that we want.
pub use version::{Version, Identifier, SemVerError};
pub use version::Identifier::{Numeric, AlphaNumeric};
pub use version_req::{VersionReq, ReqParseError};
// SemVer-compliant versions.
mod version;
// advanced version comparisons
mod version_req;

650
third_party/rust/semver/src/version.rs vendored Normal file
View File

@ -0,0 +1,650 @@
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.
//! The `version` module gives you tools to create and compare SemVer-compliant
//! versions.
use std::cmp::{self, Ordering};
use std::fmt;
use std::hash;
use std::error::Error;
use std::result;
use std::str;
use semver_parser;
/// An identifier in the pre-release or build metadata.
///
/// See sections 9 and 10 of the spec for more about pre-release identifers and
/// build metadata.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Identifier {
/// An identifier that's solely numbers.
Numeric(u64),
/// An identifier with letters and numbers.
AlphaNumeric(String),
}
impl From<semver_parser::version::Identifier> for Identifier {
fn from(other: semver_parser::version::Identifier) -> Identifier {
match other {
semver_parser::version::Identifier::Numeric(n) => Identifier::Numeric(n),
semver_parser::version::Identifier::AlphaNumeric(s) => Identifier::AlphaNumeric(s),
}
}
}
impl fmt::Display for Identifier {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Identifier::Numeric(ref n) => fmt::Display::fmt(n, f),
Identifier::AlphaNumeric(ref s) => fmt::Display::fmt(s, f),
}
}
}
/// Represents a version number conforming to the semantic versioning scheme.
#[derive(Clone, Eq, Debug)]
pub struct Version {
/// The major version, to be incremented on incompatible changes.
pub major: u64,
/// The minor version, to be incremented when functionality is added in a
/// backwards-compatible manner.
pub minor: u64,
/// The patch version, to be incremented when backwards-compatible bug
/// fixes are made.
pub patch: u64,
/// The pre-release version identifier, if one exists.
pub pre: Vec<Identifier>,
/// The build metadata, ignored when determining version precedence.
pub build: Vec<Identifier>,
}
impl From<semver_parser::version::Version> for Version {
fn from(other: semver_parser::version::Version) -> Version {
Version {
major: other.major,
minor: other.minor,
patch: other.patch,
pre: other.pre.into_iter().map(From::from).collect(),
build: other.build.into_iter().map(From::from).collect(),
}
}
}
/// An error type for this crate
///
/// Currently, just a generic error. Will make this nicer later.
#[derive(Clone,PartialEq,Debug,PartialOrd)]
pub enum SemVerError {
/// An error ocurred while parsing.
ParseError(String),
}
impl fmt::Display for SemVerError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&SemVerError::ParseError(ref m) => write!(f, "{}", m),
}
}
}
impl Error for SemVerError {
fn description(&self) -> &str {
match self {
&SemVerError::ParseError(ref m) => m,
}
}
}
/// A Result type for errors
pub type Result<T> = result::Result<T, SemVerError>;
impl Version {
/// Parse a string into a semver object.
pub fn parse(version: &str) -> Result<Version> {
let res = semver_parser::version::parse(version);
match res {
// Convert plain String error into proper ParseError
Err(e) => Err(SemVerError::ParseError(e)),
Ok(v) => Ok(From::from(v)),
}
}
/// Clears the build metadata
fn clear_metadata(&mut self) {
self.build = Vec::new();
self.pre = Vec::new();
}
/// Increments the patch number for this Version (Must be mutable)
pub fn increment_patch(&mut self) {
self.patch += 1;
self.clear_metadata();
}
/// Increments the minor version number for this Version (Must be mutable)
///
/// As instructed by section 7 of the spec, the patch number is reset to 0.
pub fn increment_minor(&mut self) {
self.minor += 1;
self.patch = 0;
self.clear_metadata();
}
/// Increments the major version number for this Version (Must be mutable)
///
/// As instructed by section 8 of the spec, the minor and patch numbers are
/// reset to 0
pub fn increment_major(&mut self) {
self.major += 1;
self.minor = 0;
self.patch = 0;
self.clear_metadata();
}
/// Checks to see if the current Version is in pre-release status
pub fn is_prerelease(&self) -> bool {
!self.pre.is_empty()
}
}
impl str::FromStr for Version {
type Err = SemVerError;
fn from_str(s: &str) -> Result<Version> {
Version::parse(s)
}
}
impl fmt::Display for Version {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "{}.{}.{}", self.major, self.minor, self.patch));
if !self.pre.is_empty() {
try!(write!(f, "-"));
for (i, x) in self.pre.iter().enumerate() {
if i != 0 {
try!(write!(f, "."))
}
try!(write!(f, "{}", x));
}
}
if !self.build.is_empty() {
try!(write!(f, "+"));
for (i, x) in self.build.iter().enumerate() {
if i != 0 {
try!(write!(f, "."))
}
try!(write!(f, "{}", x));
}
}
Ok(())
}
}
impl cmp::PartialEq for Version {
#[inline]
fn eq(&self, other: &Version) -> bool {
// We should ignore build metadata here, otherwise versions v1 and v2
// can exist such that !(v1 < v2) && !(v1 > v2) && v1 != v2, which
// violate strict total ordering rules.
self.major == other.major && self.minor == other.minor && self.patch == other.patch &&
self.pre == other.pre
}
}
impl cmp::PartialOrd for Version {
fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl cmp::Ord for Version {
fn cmp(&self, other: &Version) -> Ordering {
match self.major.cmp(&other.major) {
Ordering::Equal => {}
r => return r,
}
match self.minor.cmp(&other.minor) {
Ordering::Equal => {}
r => return r,
}
match self.patch.cmp(&other.patch) {
Ordering::Equal => {}
r => return r,
}
// NB: semver spec says 0.0.0-pre < 0.0.0
// but the version of ord defined for vec
// says that [] < [pre] so we alter it here
match (self.pre.len(), other.pre.len()) {
(0, 0) => Ordering::Equal,
(0, _) => Ordering::Greater,
(_, 0) => Ordering::Less,
(_, _) => self.pre.cmp(&other.pre),
}
}
}
impl hash::Hash for Version {
fn hash<H: hash::Hasher>(&self, into: &mut H) {
self.major.hash(into);
self.minor.hash(into);
self.patch.hash(into);
self.pre.hash(into);
}
}
#[cfg(test)]
mod tests {
use std::result;
use super::Version;
use super::Identifier;
use super::SemVerError;
#[test]
fn test_parse() {
fn parse_error(e: &str) -> result::Result<Version, SemVerError> {
return Err(SemVerError::ParseError(e.to_string()));
}
assert_eq!(Version::parse(""),
parse_error("Error parsing major identifier"));
assert_eq!(Version::parse(" "),
parse_error("Error parsing major identifier"));
assert_eq!(Version::parse("1"),
parse_error("Expected dot"));
assert_eq!(Version::parse("1.2"),
parse_error("Expected dot"));
assert_eq!(Version::parse("1.2.3-"),
parse_error("Error parsing prerelease"));
assert_eq!(Version::parse("a.b.c"),
parse_error("Error parsing major identifier"));
assert_eq!(Version::parse("1.2.3 abc"),
parse_error("Extra junk after valid version: abc"));
assert_eq!(Version::parse("1.2.3"),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: Vec::new(),
build: Vec::new(),
}));
assert_eq!(Version::parse(" 1.2.3 "),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: Vec::new(),
build: Vec::new(),
}));
assert_eq!(Version::parse("1.2.3-alpha1"),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))],
build: Vec::new(),
}));
assert_eq!(Version::parse(" 1.2.3-alpha1 "),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))],
build: Vec::new(),
}));
assert_eq!(Version::parse("1.2.3+build5"),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: Vec::new(),
build: vec![Identifier::AlphaNumeric(String::from("build5"))],
}));
assert_eq!(Version::parse(" 1.2.3+build5 "),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: Vec::new(),
build: vec![Identifier::AlphaNumeric(String::from("build5"))],
}));
assert_eq!(Version::parse("1.2.3-alpha1+build5"),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))],
build: vec![Identifier::AlphaNumeric(String::from("build5"))],
}));
assert_eq!(Version::parse(" 1.2.3-alpha1+build5 "),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))],
build: vec![Identifier::AlphaNumeric(String::from("build5"))],
}));
assert_eq!(Version::parse("1.2.3-1.alpha1.9+build5.7.3aedf "),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: vec![Identifier::Numeric(1),
Identifier::AlphaNumeric(String::from("alpha1")),
Identifier::Numeric(9),
],
build: vec![Identifier::AlphaNumeric(String::from("build5")),
Identifier::Numeric(7),
Identifier::AlphaNumeric(String::from("3aedf")),
],
}));
assert_eq!(Version::parse("0.4.0-beta.1+0851523"),
Ok(Version {
major: 0,
minor: 4,
patch: 0,
pre: vec![Identifier::AlphaNumeric(String::from("beta")),
Identifier::Numeric(1),
],
build: vec![Identifier::AlphaNumeric(String::from("0851523"))],
}));
}
#[test]
fn test_increment_patch() {
let mut buggy_release = Version::parse("0.1.0").unwrap();
buggy_release.increment_patch();
assert_eq!(buggy_release, Version::parse("0.1.1").unwrap());
}
#[test]
fn test_increment_minor() {
let mut feature_release = Version::parse("1.4.6").unwrap();
feature_release.increment_minor();
assert_eq!(feature_release, Version::parse("1.5.0").unwrap());
}
#[test]
fn test_increment_major() {
let mut chrome_release = Version::parse("46.1.246773").unwrap();
chrome_release.increment_major();
assert_eq!(chrome_release, Version::parse("47.0.0").unwrap());
}
#[test]
fn test_increment_keep_prerelease() {
let mut release = Version::parse("1.0.0-alpha").unwrap();
release.increment_patch();
assert_eq!(release, Version::parse("1.0.1").unwrap());
release.increment_minor();
assert_eq!(release, Version::parse("1.1.0").unwrap());
release.increment_major();
assert_eq!(release, Version::parse("2.0.0").unwrap());
}
#[test]
fn test_increment_clear_metadata() {
let mut release = Version::parse("1.0.0+4442").unwrap();
release.increment_patch();
assert_eq!(release, Version::parse("1.0.1").unwrap());
release = Version::parse("1.0.1+hello").unwrap();
release.increment_minor();
assert_eq!(release, Version::parse("1.1.0").unwrap());
release = Version::parse("1.1.3747+hello").unwrap();
release.increment_major();
assert_eq!(release, Version::parse("2.0.0").unwrap());
}
#[test]
fn test_eq() {
assert_eq!(Version::parse("1.2.3"), Version::parse("1.2.3"));
assert_eq!(Version::parse("1.2.3-alpha1"),
Version::parse("1.2.3-alpha1"));
assert_eq!(Version::parse("1.2.3+build.42"),
Version::parse("1.2.3+build.42"));
assert_eq!(Version::parse("1.2.3-alpha1+42"),
Version::parse("1.2.3-alpha1+42"));
assert_eq!(Version::parse("1.2.3+23"), Version::parse("1.2.3+42"));
}
#[test]
fn test_ne() {
assert!(Version::parse("0.0.0") != Version::parse("0.0.1"));
assert!(Version::parse("0.0.0") != Version::parse("0.1.0"));
assert!(Version::parse("0.0.0") != Version::parse("1.0.0"));
assert!(Version::parse("1.2.3-alpha") != Version::parse("1.2.3-beta"));
}
#[test]
fn test_show() {
assert_eq!(format!("{}", Version::parse("1.2.3").unwrap()),
"1.2.3".to_string());
assert_eq!(format!("{}", Version::parse("1.2.3-alpha1").unwrap()),
"1.2.3-alpha1".to_string());
assert_eq!(format!("{}", Version::parse("1.2.3+build.42").unwrap()),
"1.2.3+build.42".to_string());
assert_eq!(format!("{}", Version::parse("1.2.3-alpha1+42").unwrap()),
"1.2.3-alpha1+42".to_string());
}
#[test]
fn test_to_string() {
assert_eq!(Version::parse("1.2.3").unwrap().to_string(),
"1.2.3".to_string());
assert_eq!(Version::parse("1.2.3-alpha1").unwrap().to_string(),
"1.2.3-alpha1".to_string());
assert_eq!(Version::parse("1.2.3+build.42").unwrap().to_string(),
"1.2.3+build.42".to_string());
assert_eq!(Version::parse("1.2.3-alpha1+42").unwrap().to_string(),
"1.2.3-alpha1+42".to_string());
}
#[test]
fn test_lt() {
assert!(Version::parse("0.0.0") < Version::parse("1.2.3-alpha2"));
assert!(Version::parse("1.0.0") < Version::parse("1.2.3-alpha2"));
assert!(Version::parse("1.2.0") < Version::parse("1.2.3-alpha2"));
assert!(Version::parse("1.2.3-alpha1") < Version::parse("1.2.3"));
assert!(Version::parse("1.2.3-alpha1") < Version::parse("1.2.3-alpha2"));
assert!(!(Version::parse("1.2.3-alpha2") < Version::parse("1.2.3-alpha2")));
assert!(!(Version::parse("1.2.3+23") < Version::parse("1.2.3+42")));
}
#[test]
fn test_le() {
assert!(Version::parse("0.0.0") <= Version::parse("1.2.3-alpha2"));
assert!(Version::parse("1.0.0") <= Version::parse("1.2.3-alpha2"));
assert!(Version::parse("1.2.0") <= Version::parse("1.2.3-alpha2"));
assert!(Version::parse("1.2.3-alpha1") <= Version::parse("1.2.3-alpha2"));
assert!(Version::parse("1.2.3-alpha2") <= Version::parse("1.2.3-alpha2"));
assert!(Version::parse("1.2.3+23") <= Version::parse("1.2.3+42"));
}
#[test]
fn test_gt() {
assert!(Version::parse("1.2.3-alpha2") > Version::parse("0.0.0"));
assert!(Version::parse("1.2.3-alpha2") > Version::parse("1.0.0"));
assert!(Version::parse("1.2.3-alpha2") > Version::parse("1.2.0"));
assert!(Version::parse("1.2.3-alpha2") > Version::parse("1.2.3-alpha1"));
assert!(Version::parse("1.2.3") > Version::parse("1.2.3-alpha2"));
assert!(!(Version::parse("1.2.3-alpha2") > Version::parse("1.2.3-alpha2")));
assert!(!(Version::parse("1.2.3+23") > Version::parse("1.2.3+42")));
}
#[test]
fn test_ge() {
assert!(Version::parse("1.2.3-alpha2") >= Version::parse("0.0.0"));
assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.0.0"));
assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.2.0"));
assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.2.3-alpha1"));
assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.2.3-alpha2"));
assert!(Version::parse("1.2.3+23") >= Version::parse("1.2.3+42"));
}
#[test]
fn test_prerelease_check() {
assert!(Version::parse("1.0.0").unwrap().is_prerelease() == false);
assert!(Version::parse("0.0.1").unwrap().is_prerelease() == false);
assert!(Version::parse("4.1.4-alpha").unwrap().is_prerelease());
assert!(Version::parse("1.0.0-beta294296").unwrap().is_prerelease());
}
#[test]
fn test_spec_order() {
let vs = ["1.0.0-alpha",
"1.0.0-alpha.1",
"1.0.0-alpha.beta",
"1.0.0-beta",
"1.0.0-beta.2",
"1.0.0-beta.11",
"1.0.0-rc.1",
"1.0.0"];
let mut i = 1;
while i < vs.len() {
let a = Version::parse(vs[i - 1]);
let b = Version::parse(vs[i]);
assert!(a < b, "nope {:?} < {:?}", a, b);
i += 1;
}
}
#[test]
fn test_from_str() {
assert_eq!("1.2.3".parse(),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: Vec::new(),
build: Vec::new(),
}));
assert_eq!(" 1.2.3 ".parse(),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: Vec::new(),
build: Vec::new(),
}));
assert_eq!("1.2.3-alpha1".parse(),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))],
build: Vec::new(),
}));
assert_eq!(" 1.2.3-alpha1 ".parse(),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))],
build: Vec::new(),
}));
assert_eq!("1.2.3+build5".parse(),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: Vec::new(),
build: vec![Identifier::AlphaNumeric(String::from("build5"))],
}));
assert_eq!(" 1.2.3+build5 ".parse(),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: Vec::new(),
build: vec![Identifier::AlphaNumeric(String::from("build5"))],
}));
assert_eq!("1.2.3-alpha1+build5".parse(),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))],
build: vec![Identifier::AlphaNumeric(String::from("build5"))],
}));
assert_eq!(" 1.2.3-alpha1+build5 ".parse(),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))],
build: vec![Identifier::AlphaNumeric(String::from("build5"))],
}));
assert_eq!("1.2.3-1.alpha1.9+build5.7.3aedf ".parse(),
Ok(Version {
major: 1,
minor: 2,
patch: 3,
pre: vec![Identifier::Numeric(1),
Identifier::AlphaNumeric(String::from("alpha1")),
Identifier::Numeric(9),
],
build: vec![Identifier::AlphaNumeric(String::from("build5")),
Identifier::Numeric(7),
Identifier::AlphaNumeric(String::from("3aedf")),
],
}));
assert_eq!("0.4.0-beta.1+0851523".parse(),
Ok(Version {
major: 0,
minor: 4,
patch: 0,
pre: vec![Identifier::AlphaNumeric(String::from("beta")),
Identifier::Numeric(1),
],
build: vec![Identifier::AlphaNumeric(String::from("0851523"))],
}));
}
#[test]
fn test_from_str_errors() {
fn parse_error(e: &str) -> result::Result<Version, SemVerError> {
return Err(SemVerError::ParseError(e.to_string()));
}
assert_eq!("".parse(), parse_error("Error parsing major identifier"));
assert_eq!(" ".parse(), parse_error("Error parsing major identifier"));
assert_eq!("1".parse(), parse_error("Expected dot"));
assert_eq!("1.2".parse(),
parse_error("Expected dot"));
assert_eq!("1.2.3-".parse(),
parse_error("Error parsing prerelease"));
assert_eq!("a.b.c".parse(),
parse_error("Error parsing major identifier"));
assert_eq!("1.2.3 abc".parse(),
parse_error("Extra junk after valid version: abc"));
}
}

View File

@ -0,0 +1,824 @@
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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::error::Error;
use std::fmt;
use std::str;
use Version;
use version::Identifier;
use semver_parser;
use self::Op::{Ex, Gt, GtEq, Lt, LtEq, Tilde, Compatible, Wildcard};
use self::WildcardVersion::{Major, Minor, Patch};
use self::ReqParseError::*;
/// A `VersionReq` is a struct containing a list of predicates that can apply to ranges of version
/// numbers. Matching operations can then be done with the `VersionReq` against a particular
/// version to see if it satisfies some or all of the constraints.
#[derive(PartialEq,Clone,Debug)]
pub struct VersionReq {
predicates: Vec<Predicate>,
}
impl From<semver_parser::range::VersionReq> for VersionReq {
fn from(other: semver_parser::range::VersionReq) -> VersionReq {
VersionReq { predicates: other.predicates.into_iter().map(From::from).collect() }
}
}
#[derive(Clone, PartialEq, Debug)]
enum WildcardVersion {
Major,
Minor,
Patch,
}
#[derive(PartialEq,Clone,Debug)]
enum Op {
Ex, // Exact
Gt, // Greater than
GtEq, // Greater than or equal to
Lt, // Less than
LtEq, // Less than or equal to
Tilde, // e.g. ~1.0.0
Compatible, // compatible by definition of semver, indicated by ^
Wildcard(WildcardVersion), // x.y.*, x.*, *
}
impl From<semver_parser::range::Op> for Op {
fn from(other: semver_parser::range::Op) -> Op {
use semver_parser::range;
match other {
range::Op::Ex => Op::Ex,
range::Op::Gt => Op::Gt,
range::Op::GtEq => Op::GtEq,
range::Op::Lt => Op::Lt,
range::Op::LtEq => Op::LtEq,
range::Op::Tilde => Op::Tilde,
range::Op::Compatible => Op::Compatible,
range::Op::Wildcard(version) => {
match version {
range::WildcardVersion::Major => Op::Wildcard(WildcardVersion::Major),
range::WildcardVersion::Minor => Op::Wildcard(WildcardVersion::Minor),
range::WildcardVersion::Patch => Op::Wildcard(WildcardVersion::Patch),
}
}
}
}
}
#[derive(PartialEq,Clone,Debug)]
struct Predicate {
op: Op,
major: u64,
minor: Option<u64>,
patch: Option<u64>,
pre: Vec<Identifier>,
}
impl From<semver_parser::range::Predicate> for Predicate {
fn from(other: semver_parser::range::Predicate) -> Predicate {
Predicate {
op: From::from(other.op),
major: other.major,
minor: other.minor,
patch: other.patch,
pre: other.pre.into_iter().map(From::from).collect(),
}
}
}
/// A `ReqParseError` is returned from methods which parse a string into a `VersionReq`. Each
/// enumeration is one of the possible errors that can occur.
#[derive(Clone, Debug, PartialEq)]
pub enum ReqParseError {
/// The given version requirement is invalid.
InvalidVersionRequirement,
/// You have already provided an operation, such as `=`, `~`, or `^`. Only use one.
OpAlreadySet,
/// The sigil you have written is not correct.
InvalidSigil,
/// All components of a version must be numeric.
VersionComponentsMustBeNumeric,
/// There was an error parsing an identifier.
InvalidIdentifier,
/// At least a major version is required.
MajorVersionRequired,
/// An unimplemented version requirement.
UnimplementedVersionRequirement,
/// This form of requirement is deprecated.
DeprecatedVersionRequirement(VersionReq),
}
impl fmt::Display for ReqParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.description().fmt(f)
}
}
impl Error for ReqParseError {
fn description(&self) -> &str {
match self {
&InvalidVersionRequirement => "the given version requirement is invalid",
&OpAlreadySet => {
"you have already provided an operation, such as =, ~, or ^; only use one"
},
&InvalidSigil => "the sigil you have written is not correct",
&VersionComponentsMustBeNumeric => "version components must be numeric",
&InvalidIdentifier => "invalid identifier",
&MajorVersionRequired => "at least a major version number is required",
&UnimplementedVersionRequirement => {
"the given version requirement is not implemented, yet"
},
&DeprecatedVersionRequirement(_) => "This requirement is deprecated",
}
}
}
impl From<String> for ReqParseError {
fn from(other: String) -> ReqParseError {
match &*other {
"Null is not a valid VersionReq" => ReqParseError::InvalidVersionRequirement,
"VersionReq did not parse properly." => ReqParseError::OpAlreadySet,
_ => ReqParseError::InvalidVersionRequirement,
}
}
}
impl VersionReq {
/// `any()` is a factory method which creates a `VersionReq` with no constraints. In other
/// words, any version will match against it.
///
/// # Examples
///
/// ```
/// use semver::VersionReq;
///
/// let anything = VersionReq::any();
/// ```
pub fn any() -> VersionReq {
VersionReq { predicates: vec![] }
}
/// `parse()` is the main constructor of a `VersionReq`. It turns a string like `"^1.2.3"`
/// and turns it into a `VersionReq` that matches that particular constraint.
///
/// A `Result` is returned which contains a `ReqParseError` if there was a problem parsing the
/// `VersionReq`.
///
/// # Examples
///
/// ```
/// use semver::VersionReq;
///
/// let version = VersionReq::parse("=1.2.3");
/// let version = VersionReq::parse(">1.2.3");
/// let version = VersionReq::parse("<1.2.3");
/// let version = VersionReq::parse("~1.2.3");
/// let version = VersionReq::parse("^1.2.3");
/// let version = VersionReq::parse("<=1.2.3");
/// let version = VersionReq::parse(">=1.2.3");
/// ```
///
/// This example demonstrates error handling, and will panic.
///
/// ```should-panic
/// use semver::VersionReq;
///
/// let version = match VersionReq::parse("not a version") {
/// Ok(version) => version,
/// Err(e) => panic!("There was a problem parsing: {}", e),
/// }
/// ```
pub fn parse(input: &str) -> Result<VersionReq, ReqParseError> {
let res = semver_parser::range::parse(input);
if let Ok(v) = res {
return Ok(From::from(v));
}
return match VersionReq::parse_deprecated(input) {
Some(v) => {
Err(ReqParseError::DeprecatedVersionRequirement(v))
}
None => Err(From::from(res.err().unwrap())),
}
}
fn parse_deprecated(version: &str) -> Option<VersionReq> {
return match version {
".*" => Some(VersionReq::any()),
"0.1.0." => Some(VersionReq::parse("0.1.0").unwrap()),
"0.3.1.3" => Some(VersionReq::parse("0.3.13").unwrap()),
"0.2*" => Some(VersionReq::parse("0.2.*").unwrap()),
"*.0" => Some(VersionReq::any()),
_ => None,
}
}
/// `exact()` is a factory method which creates a `VersionReq` with one exact constraint.
///
/// # Examples
///
/// ```
/// use semver::VersionReq;
/// use semver::Version;
///
/// let version = Version { major: 1, minor: 1, patch: 1, pre: vec![], build: vec![] };
/// let exact = VersionReq::exact(&version);
/// ```
pub fn exact(version: &Version) -> VersionReq {
VersionReq { predicates: vec![Predicate::exact(version)] }
}
/// `matches()` matches a given `Version` against this `VersionReq`.
///
/// # Examples
///
/// ```
/// use semver::VersionReq;
/// use semver::Version;
///
/// let version = Version { major: 1, minor: 1, patch: 1, pre: vec![], build: vec![] };
/// let exact = VersionReq::exact(&version);
///
/// assert!(exact.matches(&version));
/// ```
pub fn matches(&self, version: &Version) -> bool {
// no predicates means anything matches
if self.predicates.is_empty() {
return true;
}
self.predicates.iter().all(|p| p.matches(version)) &&
self.predicates.iter().any(|p| p.pre_tag_is_compatible(version))
}
}
impl str::FromStr for VersionReq {
type Err = ReqParseError;
fn from_str(s: &str) -> Result<VersionReq, ReqParseError> {
VersionReq::parse(s)
}
}
impl Predicate {
fn exact(version: &Version) -> Predicate {
Predicate {
op: Ex,
major: version.major,
minor: Some(version.minor),
patch: Some(version.patch),
pre: version.pre.clone(),
}
}
/// `matches()` takes a `Version` and determines if it matches this particular `Predicate`.
pub fn matches(&self, ver: &Version) -> bool {
match self.op {
Ex => self.is_exact(ver),
Gt => self.is_greater(ver),
GtEq => self.is_exact(ver) || self.is_greater(ver),
Lt => !self.is_exact(ver) && !self.is_greater(ver),
LtEq => !self.is_greater(ver),
Tilde => self.matches_tilde(ver),
Compatible => self.is_compatible(ver),
Wildcard(_) => self.matches_wildcard(ver),
}
}
fn is_exact(&self, ver: &Version) -> bool {
if self.major != ver.major {
return false;
}
match self.minor {
Some(minor) => {
if minor != ver.minor {
return false;
}
}
None => return true,
}
match self.patch {
Some(patch) => {
if patch != ver.patch {
return false;
}
}
None => return true,
}
if self.pre != ver.pre {
return false;
}
true
}
// https://docs.npmjs.com/misc/semver#prerelease-tags
fn pre_tag_is_compatible(&self, ver: &Version) -> bool {
// If a version has a prerelease tag (for example, 1.2.3-alpha.3) then it will
// only be
// allowed to satisfy comparator sets if at least one comparator with the same
// [major,
// minor, patch] tuple also has a prerelease tag.
!ver.is_prerelease() ||
(self.major == ver.major && self.minor == Some(ver.minor) &&
self.patch == Some(ver.patch) && !self.pre.is_empty())
}
fn is_greater(&self, ver: &Version) -> bool {
if self.major != ver.major {
return ver.major > self.major;
}
match self.minor {
Some(minor) => {
if minor != ver.minor {
return ver.minor > minor;
}
}
None => return false,
}
match self.patch {
Some(patch) => {
if patch != ver.patch {
return ver.patch > patch;
}
}
None => return false,
}
if !self.pre.is_empty() {
return ver.pre.is_empty() || ver.pre > self.pre;
}
false
}
// see https://www.npmjs.org/doc/misc/semver.html for behavior
fn matches_tilde(&self, ver: &Version) -> bool {
let minor = match self.minor {
Some(n) => n,
None => return self.major == ver.major,
};
match self.patch {
Some(patch) => {
self.major == ver.major && minor == ver.minor &&
(ver.patch > patch || (ver.patch == patch && self.pre_is_compatible(ver)))
}
None => self.major == ver.major && minor == ver.minor,
}
}
// see https://www.npmjs.org/doc/misc/semver.html for behavior
fn is_compatible(&self, ver: &Version) -> bool {
if self.major != ver.major {
return false;
}
let minor = match self.minor {
Some(n) => n,
None => return self.major == ver.major,
};
match self.patch {
Some(patch) => {
if self.major == 0 {
if minor == 0 {
ver.minor == minor && ver.patch == patch && self.pre_is_compatible(ver)
} else {
ver.minor == minor &&
(ver.patch > patch || (ver.patch == patch && self.pre_is_compatible(ver)))
}
} else {
ver.minor > minor ||
(ver.minor == minor &&
(ver.patch > patch || (ver.patch == patch && self.pre_is_compatible(ver))))
}
}
None => {
if self.major == 0 {
ver.minor == minor
} else {
ver.minor >= minor
}
}
}
}
fn pre_is_compatible(&self, ver: &Version) -> bool {
ver.pre.is_empty() || ver.pre >= self.pre
}
// see https://www.npmjs.org/doc/misc/semver.html for behavior
fn matches_wildcard(&self, ver: &Version) -> bool {
match self.op {
Wildcard(Major) => true,
Wildcard(Minor) => self.major == ver.major,
Wildcard(Patch) => {
match self.minor {
Some(minor) => self.major == ver.major && minor == ver.minor,
None => {
// minor and patch version astericks mean match on major
self.major == ver.major
}
}
}
_ => false, // unreachable
}
}
}
impl fmt::Display for VersionReq {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
if self.predicates.is_empty() {
try!(write!(fmt, "*"));
} else {
for (i, ref pred) in self.predicates.iter().enumerate() {
if i == 0 {
try!(write!(fmt, "{}", pred));
} else {
try!(write!(fmt, ", {}", pred));
}
}
}
Ok(())
}
}
impl fmt::Display for Predicate {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self.op {
Wildcard(Major) => try!(write!(fmt, "*")),
Wildcard(Minor) => try!(write!(fmt, "{}.*", self.major)),
Wildcard(Patch) => {
if let Some(minor) = self.minor {
try!(write!(fmt, "{}.{}.*", self.major, minor))
} else {
try!(write!(fmt, "{}.*.*", self.major))
}
}
_ => {
try!(write!(fmt, "{}{}", self.op, self.major));
match self.minor {
Some(v) => try!(write!(fmt, ".{}", v)),
None => (),
}
match self.patch {
Some(v) => try!(write!(fmt, ".{}", v)),
None => (),
}
if !self.pre.is_empty() {
try!(write!(fmt, "-"));
for (i, x) in self.pre.iter().enumerate() {
if i != 0 {
try!(write!(fmt, "."))
}
try!(write!(fmt, "{}", x));
}
}
}
}
Ok(())
}
}
impl fmt::Display for Op {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Ex => try!(write!(fmt, "= ")),
Gt => try!(write!(fmt, "> ")),
GtEq => try!(write!(fmt, ">= ")),
Lt => try!(write!(fmt, "< ")),
LtEq => try!(write!(fmt, "<= ")),
Tilde => try!(write!(fmt, "~")),
Compatible => try!(write!(fmt, "^")),
// gets handled specially in Predicate::fmt
Wildcard(_) => try!(write!(fmt, "")),
}
Ok(())
}
}
#[cfg(test)]
mod test {
use super::{VersionReq, Op};
use super::super::version::Version;
fn req(s: &str) -> VersionReq {
VersionReq::parse(s).unwrap()
}
fn version(s: &str) -> Version {
match Version::parse(s) {
Ok(v) => v,
Err(e) => panic!("`{}` is not a valid version. Reason: {:?}", s, e),
}
}
fn assert_match(req: &VersionReq, vers: &[&str]) {
for ver in vers.iter() {
assert!(req.matches(&version(*ver)), "did not match {}", ver);
}
}
fn assert_not_match(req: &VersionReq, vers: &[&str]) {
for ver in vers.iter() {
assert!(!req.matches(&version(*ver)), "matched {}", ver);
}
}
#[test]
fn test_parsing_default() {
let r = req("1.0.0");
assert_eq!(r.to_string(), "^1.0.0".to_string());
assert_match(&r, &["1.0.0", "1.0.1"]);
assert_not_match(&r, &["0.9.9", "0.10.0", "0.1.0"]);
}
#[test]
fn test_parsing_exact() {
let r = req("=1.0.0");
assert!(r.to_string() == "= 1.0.0".to_string());
assert_eq!(r.to_string(), "= 1.0.0".to_string());
assert_match(&r, &["1.0.0"]);
assert_not_match(&r, &["1.0.1", "0.9.9", "0.10.0", "0.1.0", "1.0.0-pre"]);
let r = req("=0.9.0");
assert_eq!(r.to_string(), "= 0.9.0".to_string());
assert_match(&r, &["0.9.0"]);
assert_not_match(&r, &["0.9.1", "1.9.0", "0.0.9"]);
let r = req("=0.1.0-beta2.a");
assert_eq!(r.to_string(), "= 0.1.0-beta2.a".to_string());
assert_match(&r, &["0.1.0-beta2.a"]);
assert_not_match(&r, &["0.9.1", "0.1.0", "0.1.1-beta2.a", "0.1.0-beta2"]);
}
#[test]
fn test_parse_metadata_see_issue_88_see_issue_88() {
for op in &[Op::Compatible, Op::Ex, Op::Gt, Op::GtEq, Op::Lt, Op::LtEq, Op::Tilde] {
req(&format!("{} 1.2.3+meta", op));
}
}
#[test]
pub fn test_parsing_greater_than() {
let r = req(">= 1.0.0");
assert_eq!(r.to_string(), ">= 1.0.0".to_string());
assert_match(&r, &["1.0.0", "2.0.0"]);
assert_not_match(&r, &["0.1.0", "0.0.1", "1.0.0-pre", "2.0.0-pre"]);
let r = req(">= 2.1.0-alpha2");
assert_match(&r, &["2.1.0-alpha2", "2.1.0-alpha3", "2.1.0", "3.0.0"]);
assert_not_match(&r,
&["2.0.0", "2.1.0-alpha1", "2.0.0-alpha2", "3.0.0-alpha2"]);
}
#[test]
pub fn test_parsing_less_than() {
let r = req("< 1.0.0");
assert_eq!(r.to_string(), "< 1.0.0".to_string());
assert_match(&r, &["0.1.0", "0.0.1"]);
assert_not_match(&r, &["1.0.0", "1.0.0-beta", "1.0.1", "0.9.9-alpha"]);
let r = req("<= 2.1.0-alpha2");
assert_match(&r, &["2.1.0-alpha2", "2.1.0-alpha1", "2.0.0", "1.0.0"]);
assert_not_match(&r,
&["2.1.0", "2.2.0-alpha1", "2.0.0-alpha2", "1.0.0-alpha2"]);
}
#[test]
pub fn test_multiple() {
let r = req("> 0.0.9, <= 2.5.3");
assert_eq!(r.to_string(), "> 0.0.9, <= 2.5.3".to_string());
assert_match(&r, &["0.0.10", "1.0.0", "2.5.3"]);
assert_not_match(&r, &["0.0.8", "2.5.4"]);
let r = req("0.3.0, 0.4.0");
assert_eq!(r.to_string(), "^0.3.0, ^0.4.0".to_string());
assert_not_match(&r, &["0.0.8", "0.3.0", "0.4.0"]);
let r = req("<= 0.2.0, >= 0.5.0");
assert_eq!(r.to_string(), "<= 0.2.0, >= 0.5.0".to_string());
assert_not_match(&r, &["0.0.8", "0.3.0", "0.5.1"]);
let r = req("0.1.0, 0.1.4, 0.1.6");
assert_eq!(r.to_string(), "^0.1.0, ^0.1.4, ^0.1.6".to_string());
assert_match(&r, &["0.1.6", "0.1.9"]);
assert_not_match(&r, &["0.1.0", "0.1.4", "0.2.0"]);
assert!(VersionReq::parse("> 0.1.0,").is_err());
assert!(VersionReq::parse("> 0.3.0, ,").is_err());
let r = req(">=0.5.1-alpha3, <0.6");
assert_eq!(r.to_string(), ">= 0.5.1-alpha3, < 0.6".to_string());
assert_match(&r,
&["0.5.1-alpha3", "0.5.1-alpha4", "0.5.1-beta", "0.5.1", "0.5.5"]);
assert_not_match(&r,
&["0.5.1-alpha1", "0.5.2-alpha3", "0.5.5-pre", "0.5.0-pre"]);
assert_not_match(&r, &["0.6.0", "0.6.0-pre"]);
}
#[test]
pub fn test_parsing_tilde() {
let r = req("~1");
assert_match(&r, &["1.0.0", "1.0.1", "1.1.1"]);
assert_not_match(&r, &["0.9.1", "2.9.0", "0.0.9"]);
let r = req("~1.2");
assert_match(&r, &["1.2.0", "1.2.1"]);
assert_not_match(&r, &["1.1.1", "1.3.0", "0.0.9"]);
let r = req("~1.2.2");
assert_match(&r, &["1.2.2", "1.2.4"]);
assert_not_match(&r, &["1.2.1", "1.9.0", "1.0.9", "2.0.1", "0.1.3"]);
let r = req("~1.2.3-beta.2");
assert_match(&r, &["1.2.3", "1.2.4", "1.2.3-beta.2", "1.2.3-beta.4"]);
assert_not_match(&r, &["1.3.3", "1.1.4", "1.2.3-beta.1", "1.2.4-beta.2"]);
}
#[test]
pub fn test_parsing_compatible() {
let r = req("^1");
assert_match(&r, &["1.1.2", "1.1.0", "1.2.1", "1.0.1"]);
assert_not_match(&r, &["0.9.1", "2.9.0", "0.1.4"]);
assert_not_match(&r, &["1.0.0-beta1", "0.1.0-alpha", "1.0.1-pre"]);
let r = req("^1.1");
assert_match(&r, &["1.1.2", "1.1.0", "1.2.1"]);
assert_not_match(&r, &["0.9.1", "2.9.0", "1.0.1", "0.1.4"]);
let r = req("^1.1.2");
assert_match(&r, &["1.1.2", "1.1.4", "1.2.1"]);
assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1"]);
assert_not_match(&r, &["1.1.2-alpha1", "1.1.3-alpha1", "2.9.0-alpha1"]);
let r = req("^0.1.2");
assert_match(&r, &["0.1.2", "0.1.4"]);
assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1"]);
assert_not_match(&r, &["0.1.2-beta", "0.1.3-alpha", "0.2.0-pre"]);
let r = req("^0.5.1-alpha3");
assert_match(&r,
&["0.5.1-alpha3", "0.5.1-alpha4", "0.5.1-beta", "0.5.1", "0.5.5"]);
assert_not_match(&r,
&["0.5.1-alpha1", "0.5.2-alpha3", "0.5.5-pre", "0.5.0-pre", "0.6.0"]);
let r = req("^0.0.2");
assert_match(&r, &["0.0.2"]);
assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1", "0.1.4"]);
let r = req("^0.0");
assert_match(&r, &["0.0.2", "0.0.0"]);
assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.1.4"]);
let r = req("^0");
assert_match(&r, &["0.9.1", "0.0.2", "0.0.0"]);
assert_not_match(&r, &["2.9.0", "1.1.1"]);
let r = req("^1.4.2-beta.5");
assert_match(&r,
&["1.4.2", "1.4.3", "1.4.2-beta.5", "1.4.2-beta.6", "1.4.2-c"]);
assert_not_match(&r,
&["0.9.9", "2.0.0", "1.4.2-alpha", "1.4.2-beta.4", "1.4.3-beta.5"]);
}
#[test]
pub fn test_parsing_wildcard() {
let r = req("");
assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);
assert_not_match(&r, &[]);
let r = req("*");
assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);
assert_not_match(&r, &[]);
let r = req("x");
assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);
assert_not_match(&r, &[]);
let r = req("X");
assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);
assert_not_match(&r, &[]);
let r = req("1.*");
assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]);
assert_not_match(&r, &["0.0.9"]);
let r = req("1.x");
assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]);
assert_not_match(&r, &["0.0.9"]);
let r = req("1.X");
assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]);
assert_not_match(&r, &["0.0.9"]);
let r = req("1.2.*");
assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]);
assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]);
let r = req("1.2.x");
assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]);
assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]);
let r = req("1.2.X");
assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]);
assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]);
}
#[test]
pub fn test_any() {
let r = VersionReq::any();
assert_match(&r, &["0.0.1", "0.1.0", "1.0.0"]);
}
#[test]
pub fn test_pre() {
let r = req("=2.1.1-really.0");
assert_match(&r, &["2.1.1-really.0"]);
}
// #[test]
// pub fn test_parse_errors() {
// assert_eq!(Err(InvalidVersionRequirement), VersionReq::parse("\0"));
// assert_eq!(Err(OpAlreadySet), VersionReq::parse(">= >= 0.0.2"));
// assert_eq!(Err(InvalidSigil), VersionReq::parse(">== 0.0.2"));
// assert_eq!(Err(VersionComponentsMustBeNumeric),
// VersionReq::parse("a.0.0"));
// assert_eq!(Err(InvalidIdentifier), VersionReq::parse("1.0.0-"));
// assert_eq!(Err(MajorVersionRequired), VersionReq::parse(">="));
// }
#[test]
pub fn test_from_str() {
assert_eq!("1.0.0".parse::<VersionReq>().unwrap().to_string(),
"^1.0.0".to_string());
assert_eq!("=1.0.0".parse::<VersionReq>().unwrap().to_string(),
"= 1.0.0".to_string());
assert_eq!("~1".parse::<VersionReq>().unwrap().to_string(),
"~1".to_string());
assert_eq!("~1.2".parse::<VersionReq>().unwrap().to_string(),
"~1.2".to_string());
assert_eq!("^1".parse::<VersionReq>().unwrap().to_string(),
"^1".to_string());
assert_eq!("^1.1".parse::<VersionReq>().unwrap().to_string(),
"^1.1".to_string());
assert_eq!("*".parse::<VersionReq>().unwrap().to_string(),
"*".to_string());
assert_eq!("1.*".parse::<VersionReq>().unwrap().to_string(),
"1.*".to_string());
assert_eq!("< 1.0.0".parse::<VersionReq>().unwrap().to_string(),
"< 1.0.0".to_string());
}
// #[test]
// pub fn test_from_str_errors() {
// assert_eq!(Err(InvalidVersionRequirement), "\0".parse::<VersionReq>());
// assert_eq!(Err(OpAlreadySet), ">= >= 0.0.2".parse::<VersionReq>());
// assert_eq!(Err(InvalidSigil), ">== 0.0.2".parse::<VersionReq>());
// assert_eq!(Err(VersionComponentsMustBeNumeric),
// "a.0.0".parse::<VersionReq>());
// assert_eq!(Err(InvalidIdentifier), "1.0.0-".parse::<VersionReq>());
// assert_eq!(Err(MajorVersionRequired), ">=".parse::<VersionReq>());
// }
#[test]
fn test_cargo3202() {
let v = "0.*.*".parse::<VersionReq>().unwrap();
assert_eq!("0.*.*", format!("{}", v.predicates[0]));
let v = "0.0.*".parse::<VersionReq>().unwrap();
assert_eq!("0.0.*", format!("{}", v.predicates[0]));
let r = req("0.*.*");
assert_match(&r, &["0.5.0"]);
}
}

View File

@ -0,0 +1,22 @@
extern crate semver;
#[test]
fn test_regressions() {
use semver::VersionReq;
use semver::ReqParseError;
let versions = vec![
(".*", VersionReq::any()),
("0.1.0.", VersionReq::parse("0.1.0").unwrap()),
("0.3.1.3", VersionReq::parse("0.3.13").unwrap()),
("0.2*", VersionReq::parse("0.2.*").unwrap()),
("*.0", VersionReq::any()),
];
for (version, requirement) in versions.into_iter() {
let parsed = VersionReq::parse(version);
let error = parsed.err().unwrap();
assert_eq!(ReqParseError::DeprecatedVersionRequirement(requirement), error);
}
}

View File

@ -0,0 +1,25 @@
extern crate semver;
extern crate crates_index;
extern crate tempdir;
// This test checks to see if every existing crate parses successfully. Important to not break the
// Rust universe!
#[cfg(feature = "ci")]
#[test]
fn test_regressions() {
use tempdir::TempDir;
use crates_index::Index;
use semver::Version;
let dir = TempDir::new("semver").unwrap();
let index = Index::new(dir.into_path());
index.clone().unwrap();
for krate in index.crates() {
for version in krate.versions() {
let v = version.version();
assert!(Version::parse(v).is_ok(), "failed: {} ({})", version.name(), v);
}
}
}

View File

@ -203,6 +203,22 @@ dependencies = [
"syn 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cubeb-ffi"
version = "0.0.1"
dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cubeb-pulse"
version = "0.0.1"
dependencies = [
"cubeb-ffi 0.0.1",
"pulse-ffi 0.1.0",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "deque"
version = "0.3.1"
@ -300,6 +316,7 @@ dependencies = [
name = "gkrust-shared"
version = "0.1.0"
dependencies = [
"cubeb-pulse 0.0.1",
"geckoservo 0.0.1",
"mp4parse_capi 0.8.0",
"nserror 0.1.0",
@ -568,6 +585,13 @@ name = "procedural-masquerade"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pulse-ffi"
version = "0.1.0"
dependencies = [
"libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quasi"
version = "0.29.0"
@ -670,6 +694,19 @@ dependencies = [
"smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "0.9.9"
@ -1107,6 +1144,8 @@ dependencies = [
"checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457"
"checksum rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "237546c689f20bb44980270c73c3b9edd0891c1be49cc1274406134a66d3957b"
"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)" = "05a67b8a53f885f4b6e3ed183806035819f9862474e747fe4488a6d63bcbfcb7"
"checksum serde_codegen_internals 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d52006899f910528a10631e5b727973fe668f3228109d1707ccf5bad5490b6e"
"checksum serde_derive 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f15ea24bd037b2d64646b4d934fa99c649be66e3f7b29fb595a5543b212b1452"

View File

@ -9,6 +9,7 @@ description = "Testing code for libgkrust"
bindgen = ["gkrust-shared/bindgen"]
servo = ["gkrust-shared/servo"]
quantum_render = ["gkrust-shared/quantum_render"]
cubeb_pulse_rust = ["gkrust-shared/cubeb_pulse_rust"]
[dependencies]
mp4parse-gtest = { path = "../../../../dom/media/gtest" }

View File

@ -14,4 +14,7 @@ if CONFIG['MOZ_STYLO']:
if CONFIG['MOZ_BUILD_WEBRENDER']:
features += ['quantum_render']
if CONFIG['MOZ_PULSEAUDIO']:
features += ['cubeb_pulse_rust']
RustLibrary('gkrust-gtest', features, '../..')

View File

@ -201,6 +201,22 @@ dependencies = [
"syn 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cubeb-ffi"
version = "0.0.1"
dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cubeb-pulse"
version = "0.0.1"
dependencies = [
"cubeb-ffi 0.0.1",
"pulse-ffi 0.1.0",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "deque"
version = "0.3.1"
@ -298,6 +314,7 @@ dependencies = [
name = "gkrust-shared"
version = "0.1.0"
dependencies = [
"cubeb-pulse 0.0.1",
"geckoservo 0.0.1",
"mp4parse_capi 0.8.0",
"nserror 0.1.0",
@ -555,6 +572,13 @@ name = "procedural-masquerade"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pulse-ffi"
version = "0.1.0"
dependencies = [
"libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quasi"
version = "0.29.0"
@ -657,6 +681,19 @@ dependencies = [
"smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "0.9.9"
@ -1094,6 +1131,8 @@ dependencies = [
"checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457"
"checksum rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "237546c689f20bb44980270c73c3b9edd0891c1be49cc1274406134a66d3957b"
"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)" = "05a67b8a53f885f4b6e3ed183806035819f9862474e747fe4488a6d63bcbfcb7"
"checksum serde_codegen_internals 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d52006899f910528a10631e5b727973fe668f3228109d1707ccf5bad5490b6e"
"checksum serde_derive 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f15ea24bd037b2d64646b4d934fa99c649be66e3f7b29fb595a5543b212b1452"

View File

@ -9,6 +9,7 @@ description = "Rust code for libxul"
bindgen = ["gkrust-shared/bindgen"]
servo = ["gkrust-shared/servo"]
quantum_render = ["gkrust-shared/quantum_render"]
cubeb_pulse_rust = ["gkrust-shared/cubeb_pulse_rust"]
[dependencies]
gkrust-shared = { path = "shared" }

View File

@ -14,4 +14,7 @@ if CONFIG['MOZ_STYLO']:
if CONFIG['MOZ_BUILD_WEBRENDER']:
features += ['quantum_render']
if CONFIG['MOZ_PULSEAUDIO']:
features += ['cubeb_pulse_rust']
RustLibrary('gkrust', features, '..')

View File

@ -12,12 +12,14 @@ nsstring = { path = "../../../../xpcom/rust/nsstring" }
nserror = { path = "../../../../xpcom/rust/nserror" }
rust_url_capi = { path = "../../../../netwerk/base/rust-url-capi" }
webrender_bindings = { path = "../../../../gfx/webrender_bindings", optional = true }
cubeb-pulse = { path = "../../../../media/libcubeb/cubeb-pulse-rs", optional = true, features=["pulse-dlopen"] }
[features]
default = []
bindgen = ["geckoservo/bindgen"]
servo = ["geckoservo"]
quantum_render = ["webrender_bindings"]
cubeb_pulse_rust = ["cubeb-pulse"]
[lib]
path = "lib.rs"

View File

@ -11,6 +11,8 @@ extern crate nserror;
extern crate rust_url_capi;
#[cfg(feature = "quantum_render")]
extern crate webrender_bindings;
#[cfg(feature = "cubeb_pulse_rust")]
extern crate cubeb_pulse;
use std::boxed::Box;
use std::ffi::CStr;