<commit_before>use libc::{c_void, c_char, c_int, c_uint, c_double};
use std::ffi::CString;
use std::ptr;

pub type POETApplyFn = unsafe extern fn(states: *mut c_void,
                                        num_states: c_uint,
                                        id: c_uint,
                                        last_id: c_uint);

pub type POETCurrentStateFn = unsafe extern fn(states: *const c_void,
                                               num_states: c_uint,
                                               curr_state_id: *mut c_uint) -> i32;

#[link(name = "poet")]
extern {
    /*
     * Core functions - poet.h
     */

    fn poet_init(perf_goal: c_double,
                 num_system_states: c_uint,
                 control_states: *mut POETControlState,
                 apply_states: *mut POETCpuState,
                 apply_func: POETApplyFn,
                 curr_state_func: POETCurrentStateFn,
                 period: c_uint,
                 buffer_depth: c_uint,
                 log_filename: *const c_char) -> *mut c_void;

    fn poet_apply_control(state: *mut c_void,
                          id: u64,
                          perf: c_double,
                          pwr: c_double);

    fn poet_destroy(state: *mut c_void);

    /*
     * Configuration/utility functions - poet_config.h
     */

    fn apply_cpu_config(states: *mut c_void,
                        num_states: c_uint,
                        id: c_uint,
                        last_id: c_uint);

    pub fn get_control_states(path: *const c_char,
                              states: *mut *mut POETControlState,
                              num_states: *mut c_uint) -> c_int;

    pub fn get_cpu_states(path: *const c_char,
                          states: *mut *mut POETCpuState,
                          num_states: *mut c_uint) -> c_int;

    fn get_current_cpu_state(states: *const c_void,
                             num_states: c_uint,
                             curr_state_id: *mut c_uint) -> c_int;

}

#[repr(C)]
pub struct POETControlState {
    pub id: c_uint,
    pub speedup: c_double,
    pub cost: c_double,
}

impl POETControlState {
    pub fn new() -> Result<(*mut POETControlState, u32), String> {
        let mut control_states = ptr::null_mut();
        let mut num_ctl_states : u32 = 0;
        let res = unsafe {
            get_control_states(ptr::null(),
                               &mut control_states,
                               &mut num_ctl_states)
        };
        if res != 0 {
            return Err("Failed to load control states".to_string());
        }
        Ok((control_states, num_ctl_states))
    }
}

#[repr(C)]
pub struct POETCpuState {
    id: c_uint,
    freq: c_uint,
    cores: c_uint,
}

impl POETCpuState {
    pub fn new() -> Result<(*mut POETCpuState, u32), String> {
        let mut cpu_states = ptr::null_mut();
        let mut num_cpu_states : u32 = 0;
        let res = unsafe {
            get_cpu_states(ptr::null(),
                           &mut cpu_states,
                           &mut num_cpu_states)
        };
        if res != 0 {
            return Err("Failed to load cpu states".to_string());
        }
        Ok((cpu_states, num_cpu_states))
    }
}

pub struct POET {
    pub poet: *mut c_void,
}

impl POET {
    pub fn new(perf_goal: f64,
               control_states: *mut POETControlState,
               cpu_states: *mut POETCpuState,
               num_states: u32,
               apply_func: Option<POETApplyFn>,
               curr_state_func: Option<POETCurrentStateFn>,
               period: u32,
               buffer_depth: u32,
               log_filename: &str) -> Result<POET, String> {
        let apply_func = match apply_func {
            Some(p) => p,
            None => apply_cpu_config,
        };
        let curr_state_func = match curr_state_func {
            Some(p) => p,
            None => get_current_cpu_state,
        };
        let poet = unsafe {
            poet_init(perf_goal,
                      num_states, control_states, cpu_states,
                      apply_func, curr_state_func,
                      period, buffer_depth,
                      CString::new(log_filename).unwrap().as_ptr())
        };
        if poet.is_null() {
            return Err("Failed to instantiate POET object".to_string());
        }
        Ok(POET { poet: poet, })
    }

    pub fn apply_control(&mut self, tag: u64, window_rate: f64, window_power: f64) {
        unsafe {
            poet_apply_control(self.poet, tag, window_rate, window_power);
        }
    }
}

impl Drop for POET {
    fn drop(&mut self) {
        unsafe {
            poet_destroy(self.poet);
        }
        println!("Cleaned up POET");
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use libc::{self, c_void};

    #[test]
    fn test_basic() {
        let (control_states, num_ctl_states): (*mut POETControlState, u32) = POETControlState::new().ok().expect("Failed to load control states");
        let (cpu_states, num_cpu_states): (*mut POETCpuState, u32) = POETCpuState::new().ok().expect("Failed to load cpu states");
        if num_ctl_states != num_cpu_states {
            panic!("Number of control and cpu states don't match");
        }
        let mut poet = POET::new(100.0,
                                 control_states, cpu_states, num_ctl_states,
                                 None, None,
                                 20u32, 1u32, "poet.log").ok().expect("Failed to initialize POET");
        poet.apply_control(0, 1.0, 1.0);
        unsafe {
            libc::free(control_states as *mut c_void);
            libc::free(cpu_states as *mut c_void);
        }
    }

}
<commit_msg>log file name is now an Option<commit_after>use libc::{c_void, c_char, c_int, c_uint, c_double};
use std::ffi::CString;
use std::ptr;

pub type POETApplyFn = unsafe extern fn(states: *mut c_void,
                                        num_states: c_uint,
                                        id: c_uint,
                                        last_id: c_uint);

pub type POETCurrentStateFn = unsafe extern fn(states: *const c_void,
                                               num_states: c_uint,
                                               curr_state_id: *mut c_uint) -> i32;

#[link(name = "poet")]
extern {
    /*
     * Core functions - poet.h
     */

    fn poet_init(perf_goal: c_double,
                 num_system_states: c_uint,
                 control_states: *mut POETControlState,
                 apply_states: *mut POETCpuState,
                 apply_func: POETApplyFn,
                 curr_state_func: POETCurrentStateFn,
                 period: c_uint,
                 buffer_depth: c_uint,
                 log_filename: *const c_char) -> *mut c_void;

    fn poet_apply_control(state: *mut c_void,
                          id: u64,
                          perf: c_double,
                          pwr: c_double);

    fn poet_destroy(state: *mut c_void);

    /*
     * Configuration/utility functions - poet_config.h
     */

    fn apply_cpu_config(states: *mut c_void,
                        num_states: c_uint,
                        id: c_uint,
                        last_id: c_uint);

    pub fn get_control_states(path: *const c_char,
                              states: *mut *mut POETControlState,
                              num_states: *mut c_uint) -> c_int;

    pub fn get_cpu_states(path: *const c_char,
                          states: *mut *mut POETCpuState,
                          num_states: *mut c_uint) -> c_int;

    fn get_current_cpu_state(states: *const c_void,
                             num_states: c_uint,
                             curr_state_id: *mut c_uint) -> c_int;

}

#[repr(C)]
pub struct POETControlState {
    pub id: c_uint,
    pub speedup: c_double,
    pub cost: c_double,
}

impl POETControlState {
    pub fn new() -> Result<(*mut POETControlState, u32), String> {
        let mut control_states = ptr::null_mut();
        let mut num_ctl_states : u32 = 0;
        let res = unsafe {
            get_control_states(ptr::null(),
                               &mut control_states,
                               &mut num_ctl_states)
        };
        if res != 0 {
            return Err("Failed to load control states".to_string());
        }
        Ok((control_states, num_ctl_states))
    }
}

#[repr(C)]
pub struct POETCpuState {
    id: c_uint,
    freq: c_uint,
    cores: c_uint,
}

impl POETCpuState {
    pub fn new() -> Result<(*mut POETCpuState, u32), String> {
        let mut cpu_states = ptr::null_mut();
        let mut num_cpu_states : u32 = 0;
        let res = unsafe {
            get_cpu_states(ptr::null(),
                           &mut cpu_states,
                           &mut num_cpu_states)
        };
        if res != 0 {
            return Err("Failed to load cpu states".to_string());
        }
        Ok((cpu_states, num_cpu_states))
    }
}

pub struct POET {
    pub poet: *mut c_void,
}

impl POET {
    pub fn new(perf_goal: f64,
               control_states: *mut POETControlState,
               cpu_states: *mut POETCpuState,
               num_states: u32,
               apply_func: Option<POETApplyFn>,
               curr_state_func: Option<POETCurrentStateFn>,
               period: u32,
               buffer_depth: u32,
               log_filename: Option<&str>) -> Result<POET, String> {
        let apply_func = match apply_func {
            Some(p) => p,
            None => apply_cpu_config,
        };
        let curr_state_func = match curr_state_func {
            Some(p) => p,
            None => get_current_cpu_state,
        };
        let log_filename = match log_filename {
            Some(l) => CString::new(l).unwrap().as_ptr(),
            None => ptr::null(),
        };
        let poet = unsafe {
            poet_init(perf_goal,
                      num_states, control_states, cpu_states,
                      apply_func, curr_state_func,
                      period, buffer_depth, log_filename)
        };
        if poet.is_null() {
            return Err("Failed to instantiate POET object".to_string());
        }
        Ok(POET { poet: poet, })
    }

    pub fn apply_control(&mut self, tag: u64, window_rate: f64, window_power: f64) {
        unsafe {
            poet_apply_control(self.poet, tag, window_rate, window_power);
        }
    }
}

impl Drop for POET {
    fn drop(&mut self) {
        unsafe {
            poet_destroy(self.poet);
        }
        println!("Cleaned up POET");
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use libc::{self, c_void};

    #[test]
    fn test_basic() {
        let (control_states, num_ctl_states): (*mut POETControlState, u32) = POETControlState::new().ok().expect("Failed to load control states");
        let (cpu_states, num_cpu_states): (*mut POETCpuState, u32) = POETCpuState::new().ok().expect("Failed to load cpu states");
        if num_ctl_states != num_cpu_states {
            panic!("Number of control and cpu states don't match");
        }
        let mut poet = POET::new(100.0,
                                 control_states, cpu_states, num_ctl_states,
                                 None, None,
                                 20u32, 1u32, None).unwrap();
        poet.apply_control(0, 1.0, 1.0);
        unsafe {
            libc::free(control_states as *mut c_void);
            libc::free(cpu_states as *mut c_void);
        }
    }

}
<|endoftext|>


<commit_before>use std::str::Chars;
use std::iter::Peekable;

use mdo::option::{bind, ret};
use expr::Expr;

fn next_nonwhitespaces(mut input: &mut Peekable<Chars>, first: char) -> Option<char> {
    match first.is_whitespace(){
        false => return Some(first),
        true =>()
    }
    while input.peek().map(|c| c.is_whitespace()).unwrap_or(false) {
        input.next();
    }
    input.next()
}

fn read_uint(mut input: &mut Peekable<Chars>, first: char, radix: u32) -> Option<Expr> {
    let mut acc = String::new();
    let mut c = first;
    acc.push(first);
    while input.peek().unwrap_or(&' ').is_digit(radix) {
        match input.next() {
            Some(x) => c = x,
            None => break
        }
        acc.push(c);
    }
    Some(Expr::Int(isize::from_str_radix(&acc[..], radix).unwrap()))
}

fn read_int(mut input: &mut Peekable<Chars>, first: char, radix: u32) -> Option<Expr> {
    match first {
        '0'...'9' => read_uint(input, first, radix),
        _ => mdo!{
            c =<< input.next();
            ret match first {
                '+' => read_uint(input, c, radix),
                '-' => read_uint(input, c, radix).map(|e| match e {Expr::Int(i) => Expr::Int(-i),_ =>e}),
                _   => None
            }
        }
    }
}

fn read_hyphen(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = match input.peek() {
        None => return None,
        Some(c) => c.clone()
    };
    match c.is_digit(10) {
            true => read_int(input, first, 10),
            false => None
    }
}

fn read_list(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = next_nonwhitespaces(input, first);
    let car =  match c {
        Some(c) => match c {
            ')' => return Some(Expr::Nil),
            _ => read_aux(input, c)
        },
        None => return None
    };
    let c = input.next();
    let cdr = c.and_then(|c| read_list(input, c));
    match (car, cdr) {
        (Some(car_), Some(cdr_)) => Some(Expr::Cons(Box::new(car_), Box::new(cdr_))),
        _ => None
    }

}

fn read_aux(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let first =  match next_nonwhitespaces(input, first) {
        Some(c) => c,
        None => return None
    };
    match first {
        '0'...'9' => read_uint(input, first, 10),
        '-' => read_int(input, first, 10),
        '+' => read_int(input, first, 10),
        '(' => match input.next() {
            Some(c) => read_list(input, c),
            None => None
        },
        _   => unreachable!()
    }
}

fn read(s: &str) -> Option<Expr> {
    let mut input = s.chars().peekable();
    read_aux(&mut input, ' ')
}

#[test]
fn test_read_empty(){
    assert!(read("") == None)
}

#[test]
fn test_read_int() {
    assert!(read("0") == Some(Expr::Int(0)));
    assert!(read("10") == Some(Expr::Int(10)));
    assert!(read("-10") == Some(Expr::Int(-10)));
}

#[test]
fn test_read_list(){
    assert!(read("()") == Some(Expr::Nil));
    assert!(read("(1)") == Some(Expr::Cons(Box::new(Expr::Int(1)), Box::new(Expr::Nil))));
    assert!(read("(1 2)") == Some(Expr::Cons(Box::new(Expr::Int(1)), Box::new(Expr::Cons(Box::new(Expr::Int(2)), Box::new(Expr::Nil))))));
}
<commit_msg>add symbol and string reader<commit_after>use std::str::Chars;
use std::iter::Peekable;

use mdo::option::{bind, ret};
use expr::Expr;

fn next_nonwhitespaces(mut input: &mut Peekable<Chars>, first: char) -> Option<char> {
    match first.is_whitespace(){
        false => return Some(first),
        true =>()
    }
    while input.peek().map(|c| c.is_whitespace()).unwrap_or(false) {
        input.next();
    }
    input.next()
}

fn is_delimiter(c: char) -> bool {
    c.is_whitespace() || "()\"'".contains(c)
}

fn read_uint(mut input: &mut Peekable<Chars>, first: char, radix: u32) -> Option<Expr> {
    let mut acc = String::new();
    let mut c = first;
    acc.push(first);
    while input.peek().unwrap_or(&' ').is_digit(radix) {
        match input.next() {
            Some(x) => c = x,
            None => break
        }
        acc.push(c);
    }
    Some(Expr::Int(isize::from_str_radix(&acc[..], radix).unwrap()))
}

fn read_int(mut input: &mut Peekable<Chars>, first: char, radix: u32) -> Option<Expr> {
    match first {
        '0'...'9' => read_uint(input, first, radix),
        _ => mdo!{
            c =<< input.next();
            ret match first {
                '+' => read_uint(input, c, radix),
                '-' => read_uint(input, c, radix).map(|e| match e {Expr::Int(i) => Expr::Int(-i),_ =>e}),
                _   => None
            }
        }
    }
}

fn read_symbol(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let mut sym = first.to_string();
    while input.peek().map(|c| !is_delimiter(*c)).unwrap_or(false) {
        sym.push(input.next().unwrap());
    }
    Some(Expr::Sym(sym))
}

fn read_plus(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = match input.peek() {
        None => return None,
        Some(c) => c.clone()
    };
    match c.is_digit(10) {
        true => read_int(input, first, 10),
        false => read_symbol(input, first)
    }
}

fn read_hyphen(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = match input.peek() {
        None => return None,
        Some(c) => c.clone()
    };
    match c.is_digit(10) {
        true => read_int(input, first, 10),
        false => read_symbol(input, first)
    }
}

fn read_string(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let mut sym = String::new();
    // :TODO: treat escapes
    while input.peek().map(|c| *c != '"').unwrap_or(false) {
        sym.push(input.next().unwrap());
    }
    Some(Expr::Str(sym))
}

fn read_list(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let c = next_nonwhitespaces(input, ' ');
    let car =  match c {
        Some(c) => match c {
            ')' => return Some(Expr::Nil),
            _ => read_aux(input, c)
        },
        None => return None
    };
    match input.peek() {
        None => return None,
        Some(c) => ()
    }
    let cdr = read_list(input, '(');
    match (car, cdr) {
        (Some(car_), Some(cdr_)) => Some(Expr::Cons(Box::new(car_), Box::new(cdr_))),
        _ => None
    }

}

fn read_aux(mut input: &mut Peekable<Chars>, first: char) -> Option<Expr> {
    let first =  match next_nonwhitespaces(input, first) {
        Some(c) => c,
        None => return None
    };
    match first {
        '0'...'9' => read_uint(input, first, 10),
        '-' => read_hyphen(input, first),
        '+' => read_plus(input, first),
        '(' => read_list(input, first),
        '"' => read_string(input, first),
        _   => read_symbol(input, first)
    }
}

fn read(s: &str) -> Expr {
    let mut input = s.chars().peekable();
    match read_aux(&mut input, ' ') {
        Some(ex) => ex,
        None => Expr::EOF
    }
}

#[test]
fn test_read_empty(){
    assert!(read("") == Expr::EOF)
}

#[test]
fn test_read_int() {
    assert!(read("0") == (Expr::Int(0)));
    assert!(read("10") == (Expr::Int(10)));
    assert!(read("-10") == (Expr::Int(-10)));
    assert!(read("+10") == (Expr::Int(10)));
}

#[test]
fn test_read_list(){
    assert!(read("()") == (Expr::Nil));
    assert!(read("(1)") == (Expr::Cons(Box::new(Expr::Int(1)), Box::new(Expr::Nil))));
    assert!(read("(1 2)") == (Expr::Cons(Box::new(Expr::Int(1)), Box::new(Expr::Cons(Box::new(Expr::Int(2)), Box::new(Expr::Nil))))));
}

#[test]
fn test_read_symbol(){
    assert!(read("symbol") == (Expr::Sym("symbol".to_string())));
    assert!(read("+symbol") == (Expr::Sym("+symbol".to_string())));
    assert!(read("-symbol") == (Expr::Sym("-symbol".to_string())));
    assert!(read("sym-bol") == (Expr::Sym("sym-bol".to_string())));
    assert!(read("symbol2") == (Expr::Sym("symbol2".to_string())));
}

#[test]
fn test_read_string(){
    println!("{:?}", read("\"string\""));
    assert!(read("\"string\"") == (Expr::Str("string".to_string())));
    assert!(read("\"str()ing\"") == (Expr::Str("str()ing".to_string())));
    assert!(read("\"str123ing\"") == (Expr::Str("str123ing".to_string())));
    assert!(read("\"()string\"") == (Expr::Str("()string".to_string())));
    assert!(read("\"123string\"") == (Expr::Str("123string".to_string())));
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate criterion;
extern crate syntect;

use criterion::{Bencher, Criterion};
use syntect::parsing::SyntaxSet;
use syntect::highlighting::ThemeSet;

fn bench_load_internal_dump(b: &mut Bencher) {
    b.iter(|| {
        SyntaxSet::load_defaults_newlines()
    });
}

fn bench_load_internal_themes(b: &mut Bencher) {
    b.iter(|| {
        ThemeSet::load_defaults()
    });
}

fn bench_load_theme(b: &mut Bencher) {
    b.iter(|| {
        ThemeSet::get_theme("testdata/spacegray/base16-ocean.dark.tmTheme")
    });
}

fn bench_load_syntaxes(b: &mut Bencher) {
    b.iter(|| {
        let mut ps = SyntaxSet::new();
        ps.load_syntaxes("testdata/Packages", false).unwrap()
    });
}

fn bench_link_syntaxes(b: &mut Bencher) {
    let mut ps = SyntaxSet::new();
    ps.load_syntaxes("testdata/Packages", false).unwrap();
    b.iter(|| {
        ps.link_syntaxes();
    });
}

fn loading_benchmark(c: &mut Criterion) {
    c.bench_function("load_internal_dump", bench_load_internal_dump);
    c.bench_function("load_internal_themes", bench_load_internal_themes);
    c.bench_function("load_theme", bench_load_theme);
    c.bench_function("load_syntaxes", bench_load_syntaxes);
    c.bench_function("link_syntaxes", bench_link_syntaxes);
}

criterion_group! {
    name = benches;
    config = Criterion::default().sample_size(100);
    targets = loading_benchmark
}
criterion_main!(benches);
<commit_msg>Decrease number of samples for loading benches<commit_after>#[macro_use]
extern crate criterion;
extern crate syntect;

use criterion::{Bencher, Criterion};
use syntect::parsing::SyntaxSet;
use syntect::highlighting::ThemeSet;

fn bench_load_internal_dump(b: &mut Bencher) {
    b.iter(|| {
        SyntaxSet::load_defaults_newlines()
    });
}

fn bench_load_internal_themes(b: &mut Bencher) {
    b.iter(|| {
        ThemeSet::load_defaults()
    });
}

fn bench_load_theme(b: &mut Bencher) {
    b.iter(|| {
        ThemeSet::get_theme("testdata/spacegray/base16-ocean.dark.tmTheme")
    });
}

fn bench_load_syntaxes(b: &mut Bencher) {
    b.iter(|| {
        let mut ps = SyntaxSet::new();
        ps.load_syntaxes("testdata/Packages", false).unwrap()
    });
}

fn bench_link_syntaxes(b: &mut Bencher) {
    let mut ps = SyntaxSet::new();
    ps.load_syntaxes("testdata/Packages", false).unwrap();
    b.iter(|| {
        ps.link_syntaxes();
    });
}

fn loading_benchmark(c: &mut Criterion) {
    c.bench_function("load_internal_dump", bench_load_internal_dump);
    c.bench_function("load_internal_themes", bench_load_internal_themes);
    c.bench_function("load_theme", bench_load_theme);
    c.bench_function("load_syntaxes", bench_load_syntaxes);
    c.bench_function("link_syntaxes", bench_link_syntaxes);
}

criterion_group! {
    name = benches;
    config = Criterion::default().sample_size(50);
    targets = loading_benchmark
}
criterion_main!(benches);
<|endoftext|>


<commit_before>use crate::logger::{self, Level};
use std::ffi::{CString, OsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
use std::process;

fn remove_lock(lock_filepath: &Path) {
    let os: &OsStr = lock_filepath.as_ref();
    // Safety: Safe to convert when memory is enough
    let path = CString::new(os.as_bytes()).unwrap();
    unsafe { libc::unlink(path.as_ptr()) };
    log!(
        Level::Debug,
        "FsLock: removed lockfile {}",
        lock_filepath.display()
    );
}

pub struct FsLock {
    lock_filepath: PathBuf,
    fd: i32,
    locked: bool,
}

impl Default for FsLock {
    fn default() -> Self {
        FsLock {
            lock_filepath: PathBuf::new(),
            fd: -1,
            locked: false,
        }
    }
}

impl Drop for FsLock {
    fn drop(&mut self) {
        if self.locked {
            remove_lock(&self.lock_filepath);
            let _ = unsafe { libc::close(self.fd) };
        }
    }
}

impl FsLock {
    pub fn try_lock(&mut self, new_lock_filepath: &Path, pid: &mut libc::pid_t) -> bool {
        // switched to string as it is easier to work with c ffi later on
        // Safety: this might fail if the path is not valid utf-8
        if self.locked && self.lock_filepath == new_lock_filepath {
            return true;
        }

        // pid == 0 indicates that something went majorly wrong during locking
        *pid = 0;

        // TODO: move to safe rust later
        // let openoptions = OpenOptions::new().read(true).write(true).create(true);
        // let mut file = match openoptions.open(path) {
        //     Ok(file) => file,
        //     Err(_) => return false,
        // };

        log!(
            Level::Debug,
            "FsLock: trying to lock `{}'",
            new_lock_filepath.display()
        );

        // first we open (and possibly create) the lock file
        self.fd = unsafe {
            let os: &OsStr = new_lock_filepath.as_ref();
            // Safety: Safe to convert when memory is enough
            let buf = CString::new(os.as_bytes()).unwrap();
            libc::open(buf.as_ptr(), libc::O_RDWR | libc::O_CREAT, 0o600)
        };
        if self.fd < 0 {
            return false;
        }

        // then we lock it (returns immediately if locking is not possible)
        if unsafe { libc::lockf(self.fd, libc::F_TLOCK, 0) } == 0 {
            log!(
                Level::Debug,
                "FsLock: locked `{}', writing PID...",
                new_lock_filepath.display()
            );
            let pidtext = process::id().to_string();
            let mut written = 0;
            if unsafe { libc::ftruncate(self.fd, 0) } == 0 {
                written = unsafe {
                    libc::write(
                        self.fd,
                        pidtext.as_bytes().as_ptr() as *const libc::c_void,
                        pidtext.len(),
                    )
                };
            }
            let success = written != -1 && written as usize == pidtext.len();
            log!(
                Level::Debug,
                "FsLock: PID written successfully: {}",
                success as usize
            );
            if success {
                if self.locked {
                    remove_lock(&self.lock_filepath);
                }
                self.locked = true;
                self.lock_filepath = new_lock_filepath.into();
            } else {
                let _ = unsafe { libc::close(self.fd) };
            }
            success
        } else {
            log!(Level::Error, "FsLock: something went wrong during locking");

            // locking was not successful -> read PID of locking process from the file
            if self.fd >= 0 {
                let mut buf = [0; 32];
                let len = unsafe {
                    libc::read(self.fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len())
                } as usize;
                if len > 0 {
                    let buf = unsafe { std::str::from_utf8_unchecked(buf.get_unchecked(..len)) };
                    *pid = buf.parse().unwrap_or(0);
                }
                let _ = unsafe { libc::close(self.fd) };
            }
            log!(
                Level::Debug,
                "FsLock: locking failed, already locked by {}",
                pid
            );
            false
        }
    }
}
<commit_msg>fixup! Port FsLock to Rust<commit_after>use crate::logger::{self, Level};
use std::ffi::{CString, OsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
use std::process;

fn remove_lock(lock_filepath: &Path) {
    let os: &OsStr = lock_filepath.as_ref();
    // Safety: Safe to convert when memory is enough
    let path = CString::new(os.as_bytes()).unwrap();
    unsafe { libc::unlink(path.as_ptr()) };
    log!(
        Level::Debug,
        "FsLock: removed lockfile {}",
        lock_filepath.display()
    );
}

pub struct FsLock {
    lock_filepath: PathBuf,
    fd: i32,
    locked: bool,
}

impl Default for FsLock {
    fn default() -> Self {
        FsLock {
            lock_filepath: PathBuf::new(),
            fd: -1,
            locked: false,
        }
    }
}

impl Drop for FsLock {
    fn drop(&mut self) {
        if self.locked {
            remove_lock(&self.lock_filepath);
            let _ = unsafe { libc::close(self.fd) };
        }
    }
}

impl FsLock {
    pub fn try_lock(&mut self, new_lock_filepath: &Path, pid: &mut libc::pid_t) -> bool {
        if self.locked && self.lock_filepath == new_lock_filepath {
            return true;
        }

        // pid == 0 indicates that something went majorly wrong during locking
        *pid = 0;

        // TODO: move to safe rust later
        // let openoptions = OpenOptions::new().read(true).write(true).create(true);
        // let mut file = match openoptions.open(path) {
        //     Ok(file) => file,
        //     Err(_) => return false,
        // };

        log!(
            Level::Debug,
            "FsLock: trying to lock `{}'",
            new_lock_filepath.display()
        );

        // first we open (and possibly create) the lock file
        self.fd = unsafe {
            let os: &OsStr = new_lock_filepath.as_ref();
            // Safety: Safe to convert when memory is enough
            let buf = CString::new(os.as_bytes()).unwrap();
            libc::open(buf.as_ptr(), libc::O_RDWR | libc::O_CREAT, 0o600)
        };
        if self.fd < 0 {
            return false;
        }

        // then we lock it (returns immediately if locking is not possible)
        if unsafe { libc::lockf(self.fd, libc::F_TLOCK, 0) } == 0 {
            log!(
                Level::Debug,
                "FsLock: locked `{}', writing PID...",
                new_lock_filepath.display()
            );
            let pidtext = process::id().to_string();
            let mut written = 0;
            if unsafe { libc::ftruncate(self.fd, 0) } == 0 {
                written = unsafe {
                    libc::write(
                        self.fd,
                        pidtext.as_bytes().as_ptr() as *const libc::c_void,
                        pidtext.len(),
                    )
                };
            }
            let success = written != -1 && written as usize == pidtext.len();
            log!(
                Level::Debug,
                "FsLock: PID written successfully: {}",
                success as usize
            );
            if success {
                if self.locked {
                    remove_lock(&self.lock_filepath);
                }
                self.locked = true;
                self.lock_filepath = new_lock_filepath.into();
            } else {
                let _ = unsafe { libc::close(self.fd) };
            }
            success
        } else {
            log!(Level::Error, "FsLock: something went wrong during locking");

            // locking was not successful -> read PID of locking process from the file
            if self.fd >= 0 {
                let mut buf = [0; 32];
                let len = unsafe {
                    libc::read(self.fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len())
                } as usize;
                if len > 0 {
                    let buf = unsafe { std::str::from_utf8_unchecked(buf.get_unchecked(..len)) };
                    *pid = buf.parse().unwrap_or(0);
                }
                let _ = unsafe { libc::close(self.fd) };
            }
            log!(
                Level::Debug,
                "FsLock: locking failed, already locked by {}",
                pid
            );
            false
        }
    }
}
<|endoftext|>


<commit_before>use crate::logger::{self, Level};
use std::fs::{self, File, OpenOptions};
use std::io::{Error, Read, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf};
use std::process;

fn remove_lock(lock_path: &Path) {
    fs::remove_file(lock_path).ok();
    log!(
        Level::Debug,
        "FsLock: removed lockfile {}",
        lock_path.display()
    );
}

#[derive(Default)]
pub struct FsLock {
    lock_path: PathBuf,
    lock_file: Option<File>,
}

impl Drop for FsLock {
    fn drop(&mut self) {
        if self.lock_file.is_some() {
            remove_lock(&self.lock_path);
        }
    }
}

impl FsLock {
    pub fn try_lock(&mut self, new_lock_path: &Path, pid: &mut libc::pid_t) -> bool {
        if self.lock_file.is_some() && self.lock_path == new_lock_path {
            return true;
        }

        // pid == 0 indicates that something went majorly wrong during locking
        *pid = 0;

        log!(
            Level::Debug,
            "FsLock: trying to lock `{}'",
            new_lock_path.display()
        );

        // first we open (and possibly create) the lock file
        let mut options = OpenOptions::new();
        options.create(true).read(true).write(true).mode(0o600);
        let mut file = match options.open(&new_lock_path) {
            Ok(file) => file,
            Err(_) => return false,
        };

        // then we lock it (returns immediately if locking is not possible)
        if unsafe { libc::lockf(file.as_raw_fd(), libc::F_TLOCK, 0) } == 0 {
            log!(
                Level::Debug,
                "FsLock: locked `{}', writing PID...",
                new_lock_path.display()
            );
            let pid = process::id().to_string();
            let success = match unsafe { libc::ftruncate(file.as_raw_fd(), 0) } {
                0 => file.write_all(&pid.as_bytes()).is_ok(),
                _ => false,
            };
            log!(
                Level::Debug,
                "FsLock: PID written successfully: {}",
                success as usize
            );
            if success {
                if self.lock_file.take().is_some() {
                    remove_lock(&self.lock_path);
                }
                self.lock_file = Some(file);
                self.lock_path = new_lock_path.to_owned();
            }
            success
        } else {
            log!(
                Level::Error,
                "FsLock: something went wrong during locking: {}",
                Error::last_os_error()
            );

            // locking was not successful -> read PID of locking process from the file
            let mut buf = String::new();
            if file.read_to_string(&mut buf).is_ok() {
                *pid = buf.parse().unwrap_or(0);
            }
            log!(
                Level::Debug,
                "FsLock: locking failed, already locked by {}",
                pid
            );
            false
        }
    }
}
<commit_msg>Use safe set_len to truncate file<commit_after>use crate::logger::{self, Level};
use std::fs::{self, File, OpenOptions};
use std::io::{Error, Read, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf};
use std::process;

fn remove_lock(lock_path: &Path) {
    fs::remove_file(lock_path).ok();
    log!(
        Level::Debug,
        "FsLock: removed lockfile {}",
        lock_path.display()
    );
}

#[derive(Default)]
pub struct FsLock {
    lock_path: PathBuf,
    lock_file: Option<File>,
}

impl Drop for FsLock {
    fn drop(&mut self) {
        if self.lock_file.is_some() {
            remove_lock(&self.lock_path);
        }
    }
}

impl FsLock {
    pub fn try_lock(&mut self, new_lock_path: &Path, pid: &mut libc::pid_t) -> bool {
        if self.lock_file.is_some() && self.lock_path == new_lock_path {
            return true;
        }

        // pid == 0 indicates that something went majorly wrong during locking
        *pid = 0;

        log!(
            Level::Debug,
            "FsLock: trying to lock `{}'",
            new_lock_path.display()
        );

        // first we open (and possibly create) the lock file
        let mut options = OpenOptions::new();
        options.create(true).read(true).write(true).mode(0o600);
        let mut file = match options.open(&new_lock_path) {
            Ok(file) => file,
            Err(_) => return false,
        };

        // then we lock it (returns immediately if locking is not possible)
        if unsafe { libc::lockf(file.as_raw_fd(), libc::F_TLOCK, 0) } == 0 {
            log!(
                Level::Debug,
                "FsLock: locked `{}', writing PID...",
                new_lock_path.display()
            );
            let pid = process::id().to_string();
            let success = file
                .set_len(0)
                .and_then(|_| file.write_all(&pid.as_bytes()))
                .is_ok();
            log!(
                Level::Debug,
                "FsLock: PID written successfully: {}",
                success as usize
            );
            if success {
                if self.lock_file.take().is_some() {
                    remove_lock(&self.lock_path);
                }
                self.lock_file = Some(file);
                self.lock_path = new_lock_path.to_owned();
            }
            success
        } else {
            log!(
                Level::Error,
                "FsLock: something went wrong during locking: {}",
                Error::last_os_error()
            );

            // locking was not successful -> read PID of locking process from the file
            let mut buf = String::new();
            if file.read_to_string(&mut buf).is_ok() {
                *pid = buf.parse().unwrap_or(0);
            }
            log!(
                Level::Debug,
                "FsLock: locking failed, already locked by {}",
                pid
            );
            false
        }
    }
}
<|endoftext|>


<commit_before>use crate::actually_private::Private;
use alloc::borrow::Cow;
use alloc::string::String;
use core::fmt::{self, Debug, Display};
use core::marker::{PhantomData, PhantomPinned};
use core::mem::MaybeUninit;
use core::pin::Pin;
use core::slice;
use core::str::{self, Utf8Error};

extern "C" {
    #[link_name = "cxxbridge1$cxx_string$init"]
    fn string_init(this: &mut MaybeUninit<CxxString>, ptr: *const u8, len: usize);
    #[link_name = "cxxbridge1$cxx_string$destroy"]
    fn string_destroy(this: &mut MaybeUninit<CxxString>);
    #[link_name = "cxxbridge1$cxx_string$data"]
    fn string_data(this: &CxxString) -> *const u8;
    #[link_name = "cxxbridge1$cxx_string$length"]
    fn string_length(this: &CxxString) -> usize;
    #[link_name = "cxxbridge1$cxx_string$push"]
    fn string_push(this: Pin<&mut CxxString>, ptr: *const u8, len: usize);
}

/// Binding to C++ `std::string`.
///
/// # Invariants
///
/// As an invariant of this API and the static analysis of the cxx::bridge
/// macro, in Rust code we can never obtain a `CxxString` by value. C++'s string
/// requires a move constructor and may hold internal pointers, which is not
/// compatible with Rust's move behavior. Instead in Rust code we will only ever
/// look at a CxxString through a reference or smart pointer, as in `&CxxString`
/// or `UniquePtr<CxxString>`.
#[repr(C)]
pub struct CxxString {
    _private: [u8; 0],
    _pinned: PhantomData<PhantomPinned>,
}

/// Construct a C++ std::string on the Rust stack.
///
/// # Syntax
///
/// In statement position:
///
/// ```
/// # use cxx::let_cxx_string;
/// # let expression = "";
/// let_cxx_string!(var = expression);
/// ```
///
/// The `expression` may have any type that implements `AsRef<[u8]>`. Commonly
/// it will be a string literal, but for example `&[u8]` and `String` would work
/// as well.
///
/// The macro expands to something resembling `let $var: Pin<&mut CxxString> =
/// /*???*/;`. The resulting [`Pin`] can be deref'd to `&CxxString` as needed.
///
/// # Example
///
/// ```
/// use cxx::{let_cxx_string, CxxString};
///
/// fn f(s: &CxxString) {/* ... */}
///
/// fn main() {
///     let_cxx_string!(s = "example");
///     f(&s);
/// }
/// ```
#[macro_export]
macro_rules! let_cxx_string {
    ($var:ident = $value:expr $(,)?) => {
        let mut $var = $crate::private::StackString::new();
        #[allow(unused_mut, unused_unsafe)]
        let mut $var = match $value {
            let_cxx_string => unsafe { $var.init(let_cxx_string) },
        };
    };
}

impl CxxString {
    /// `CxxString` is not constructible via `new`. Instead, use the
    /// [`let_cxx_string!`] macro.
    pub fn new<T: Private>() -> Self {
        unreachable!()
    }

    /// Returns the length of the string in bytes.
    ///
    /// Matches the behavior of C++ [std::string::size][size].
    ///
    /// [size]: https://en.cppreference.com/w/cpp/string/basic_string/size
    pub fn len(&self) -> usize {
        unsafe { string_length(self) }
    }

    /// Returns true if `self` has a length of zero bytes.
    ///
    /// Matches the behavior of C++ [std::string::empty][empty].
    ///
    /// [empty]: https://en.cppreference.com/w/cpp/string/basic_string/empty
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns a byte slice of this string's contents.
    pub fn as_bytes(&self) -> &[u8] {
        let data = self.as_ptr();
        let len = self.len();
        unsafe { slice::from_raw_parts(data, len) }
    }

    /// Produces a pointer to the first character of the string.
    ///
    /// Matches the behavior of C++ [std::string::data][data].
    ///
    /// Note that the return type may look like `const char *` but is not a
    /// `const char *` in the typical C sense, as C++ strings may contain
    /// internal null bytes. As such, the returned pointer only makes sense as a
    /// string in combination with the length returned by [`len()`][len].
    ///
    /// [data]: https://en.cppreference.com/w/cpp/string/basic_string/data
    /// [len]: #method.len
    pub fn as_ptr(&self) -> *const u8 {
        unsafe { string_data(self) }
    }

    /// Validates that the C++ string contains UTF-8 data and produces a view of
    /// it as a Rust &str, otherwise an error.
    pub fn to_str(&self) -> Result<&str, Utf8Error> {
        str::from_utf8(self.as_bytes())
    }

    /// If the contents of the C++ string are valid UTF-8, this function returns
    /// a view as a Cow::Borrowed &str. Otherwise replaces any invalid UTF-8
    /// sequences with the U+FFFD [replacement character] and returns a
    /// Cow::Owned String.
    ///
    /// [replacement character]: https://doc.rust-lang.org/std/char/constant.REPLACEMENT_CHARACTER.html
    pub fn to_string_lossy(&self) -> Cow<str> {
        String::from_utf8_lossy(self.as_bytes())
    }

    /// Appends a given string slice onto the end of this C++ string.
    pub fn push_str(self: Pin<&mut Self>, s: &str) {
        self.push_bytes(s.as_bytes());
    }

    /// Appends arbitrary bytes onto the end of this C++ string.
    pub fn push_bytes(self: Pin<&mut Self>, bytes: &[u8]) {
        unsafe { string_push(self, bytes.as_ptr(), bytes.len()) }
    }
}

impl Display for CxxString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Display::fmt(self.to_string_lossy().as_ref(), f)
    }
}

impl Debug for CxxString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(self.to_string_lossy().as_ref(), f)
    }
}

impl PartialEq for CxxString {
    fn eq(&self, other: &CxxString) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}

impl PartialEq<CxxString> for str {
    fn eq(&self, other: &CxxString) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}

impl PartialEq<str> for CxxString {
    fn eq(&self, other: &str) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}

#[doc(hidden)]
#[repr(C)]
pub struct StackString {
    // Static assertions in cxx.cc validate that this is large enough and
    // aligned enough.
    space: MaybeUninit<[*const (); 8]>,
}

impl StackString {
    pub fn new() -> Self {
        StackString {
            space: MaybeUninit::uninit(),
        }
    }

    pub unsafe fn init(&mut self, value: impl AsRef<[u8]>) -> Pin<&mut CxxString> {
        let value = value.as_ref();
        let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>();
        string_init(this, value.as_ptr(), value.len());
        Pin::new_unchecked(&mut *this.as_mut_ptr())
    }
}

impl Drop for StackString {
    fn drop(&mut self) {
        unsafe {
            let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>();
            string_destroy(this);
        }
    }
}
<commit_msg>Allow Send/Sync of let_cxx_string<commit_after>use crate::actually_private::Private;
use alloc::borrow::Cow;
use alloc::string::String;
use core::fmt::{self, Debug, Display};
use core::marker::{PhantomData, PhantomPinned};
use core::mem::MaybeUninit;
use core::pin::Pin;
use core::slice;
use core::str::{self, Utf8Error};

extern "C" {
    #[link_name = "cxxbridge1$cxx_string$init"]
    fn string_init(this: &mut MaybeUninit<CxxString>, ptr: *const u8, len: usize);
    #[link_name = "cxxbridge1$cxx_string$destroy"]
    fn string_destroy(this: &mut MaybeUninit<CxxString>);
    #[link_name = "cxxbridge1$cxx_string$data"]
    fn string_data(this: &CxxString) -> *const u8;
    #[link_name = "cxxbridge1$cxx_string$length"]
    fn string_length(this: &CxxString) -> usize;
    #[link_name = "cxxbridge1$cxx_string$push"]
    fn string_push(this: Pin<&mut CxxString>, ptr: *const u8, len: usize);
}

/// Binding to C++ `std::string`.
///
/// # Invariants
///
/// As an invariant of this API and the static analysis of the cxx::bridge
/// macro, in Rust code we can never obtain a `CxxString` by value. C++'s string
/// requires a move constructor and may hold internal pointers, which is not
/// compatible with Rust's move behavior. Instead in Rust code we will only ever
/// look at a CxxString through a reference or smart pointer, as in `&CxxString`
/// or `UniquePtr<CxxString>`.
#[repr(C)]
pub struct CxxString {
    _private: [u8; 0],
    _pinned: PhantomData<PhantomPinned>,
}

/// Construct a C++ std::string on the Rust stack.
///
/// # Syntax
///
/// In statement position:
///
/// ```
/// # use cxx::let_cxx_string;
/// # let expression = "";
/// let_cxx_string!(var = expression);
/// ```
///
/// The `expression` may have any type that implements `AsRef<[u8]>`. Commonly
/// it will be a string literal, but for example `&[u8]` and `String` would work
/// as well.
///
/// The macro expands to something resembling `let $var: Pin<&mut CxxString> =
/// /*???*/;`. The resulting [`Pin`] can be deref'd to `&CxxString` as needed.
///
/// # Example
///
/// ```
/// use cxx::{let_cxx_string, CxxString};
///
/// fn f(s: &CxxString) {/* ... */}
///
/// fn main() {
///     let_cxx_string!(s = "example");
///     f(&s);
/// }
/// ```
#[macro_export]
macro_rules! let_cxx_string {
    ($var:ident = $value:expr $(,)?) => {
        let mut $var = $crate::private::StackString::new();
        #[allow(unused_mut, unused_unsafe)]
        let mut $var = match $value {
            let_cxx_string => unsafe { $var.init(let_cxx_string) },
        };
    };
}

impl CxxString {
    /// `CxxString` is not constructible via `new`. Instead, use the
    /// [`let_cxx_string!`] macro.
    pub fn new<T: Private>() -> Self {
        unreachable!()
    }

    /// Returns the length of the string in bytes.
    ///
    /// Matches the behavior of C++ [std::string::size][size].
    ///
    /// [size]: https://en.cppreference.com/w/cpp/string/basic_string/size
    pub fn len(&self) -> usize {
        unsafe { string_length(self) }
    }

    /// Returns true if `self` has a length of zero bytes.
    ///
    /// Matches the behavior of C++ [std::string::empty][empty].
    ///
    /// [empty]: https://en.cppreference.com/w/cpp/string/basic_string/empty
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns a byte slice of this string's contents.
    pub fn as_bytes(&self) -> &[u8] {
        let data = self.as_ptr();
        let len = self.len();
        unsafe { slice::from_raw_parts(data, len) }
    }

    /// Produces a pointer to the first character of the string.
    ///
    /// Matches the behavior of C++ [std::string::data][data].
    ///
    /// Note that the return type may look like `const char *` but is not a
    /// `const char *` in the typical C sense, as C++ strings may contain
    /// internal null bytes. As such, the returned pointer only makes sense as a
    /// string in combination with the length returned by [`len()`][len].
    ///
    /// [data]: https://en.cppreference.com/w/cpp/string/basic_string/data
    /// [len]: #method.len
    pub fn as_ptr(&self) -> *const u8 {
        unsafe { string_data(self) }
    }

    /// Validates that the C++ string contains UTF-8 data and produces a view of
    /// it as a Rust &str, otherwise an error.
    pub fn to_str(&self) -> Result<&str, Utf8Error> {
        str::from_utf8(self.as_bytes())
    }

    /// If the contents of the C++ string are valid UTF-8, this function returns
    /// a view as a Cow::Borrowed &str. Otherwise replaces any invalid UTF-8
    /// sequences with the U+FFFD [replacement character] and returns a
    /// Cow::Owned String.
    ///
    /// [replacement character]: https://doc.rust-lang.org/std/char/constant.REPLACEMENT_CHARACTER.html
    pub fn to_string_lossy(&self) -> Cow<str> {
        String::from_utf8_lossy(self.as_bytes())
    }

    /// Appends a given string slice onto the end of this C++ string.
    pub fn push_str(self: Pin<&mut Self>, s: &str) {
        self.push_bytes(s.as_bytes());
    }

    /// Appends arbitrary bytes onto the end of this C++ string.
    pub fn push_bytes(self: Pin<&mut Self>, bytes: &[u8]) {
        unsafe { string_push(self, bytes.as_ptr(), bytes.len()) }
    }
}

impl Display for CxxString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Display::fmt(self.to_string_lossy().as_ref(), f)
    }
}

impl Debug for CxxString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(self.to_string_lossy().as_ref(), f)
    }
}

impl PartialEq for CxxString {
    fn eq(&self, other: &CxxString) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}

impl PartialEq<CxxString> for str {
    fn eq(&self, other: &CxxString) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}

impl PartialEq<str> for CxxString {
    fn eq(&self, other: &str) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}

#[doc(hidden)]
#[repr(C)]
pub struct StackString {
    // Static assertions in cxx.cc validate that this is large enough and
    // aligned enough.
    space: MaybeUninit<[usize; 8]>,
}

impl StackString {
    pub fn new() -> Self {
        StackString {
            space: MaybeUninit::uninit(),
        }
    }

    pub unsafe fn init(&mut self, value: impl AsRef<[u8]>) -> Pin<&mut CxxString> {
        let value = value.as_ref();
        let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>();
        string_init(this, value.as_ptr(), value.len());
        Pin::new_unchecked(&mut *this.as_mut_ptr())
    }
}

impl Drop for StackString {
    fn drop(&mut self) {
        unsafe {
            let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>();
            string_destroy(this);
        }
    }
}
<|endoftext|>


<commit_before>use std::io::{self, Read, Write, Seek, SeekFrom};
use std::iter::IntoIterator;
use num::Bounded;
use std::fmt;

/// The fields in an ID3v1 tag, including the "1.1" track number field.
#[derive(Copy, Clone)]
#[allow(missing_docs)]
pub enum Fields {
    Title,
    Artist,
    Album,
    Year,
    Comment,
    Track,
    Genre,
}

impl Fields {
    fn length(&self) -> usize {
        LENGTHS[*self as usize] as usize
    }
}

static LENGTHS: &'static [i8]=&[30, 30, 30, 4, 30, -1, 1];

static TAG: &'static [u8] = b"TAG";
/// How far from the end of a file to probe for an ID3 tag signature.
pub static TAG_OFFSET: i64 = 128;

static TAGPLUS: &'static [u8] = b"TAG+";
/// How far from the end of a file to probe for an extended ID3 tag signature.
pub static TAGPLUS_OFFSET: i64 = 355;

static XLENGTHS: &'static [i8]=&[60, 60, 60, 30, 6, 6];

/// The fields in an extended ID3v1 tag.
#[derive(Copy, Clone)]
#[allow(missing_docs)]
pub enum XFields {
    XTitle,
    XArtist,
    XAlbum,
    Speed,
    XGenre,
    Start,
    End,
}

impl XFields {
    fn length(&self) -> usize {
        XLENGTHS[*self as usize] as usize
    }
}

/// ID3v1's notion of a four-digit year.
#[derive(Debug, Copy, Clone)]
pub struct Year
{
    value: u16,
}

impl Year {
    fn value(&self) -> u16 {
        self.value
    }
    fn new(year: u16) -> Option<Year> {
        #![allow(deprecated)]
        let max: Year = Bounded::max_value();
        if year > max.value() {
            None
        } else {
            Some(Year {value: year})
        }
    }
}

impl Bounded for Year {
    #![allow(deprecated)]
    fn min_value() -> Year {
        Year {value: 0}
    }
    fn max_value() -> Year {
        Year {value: 9999}
    }
}

/// ID3v1 extended time tags--encoded in the format "mmm:ss", a valid value can be a maximum of 999m99s = 999*60+99 = 60039 seconds.
#[derive(Copy, Clone, Debug)]
pub struct Time
{
    value: u16,
}

impl Time {
    fn seconds(&self) -> u16 {
        self.value
    }
    fn new(seconds: u16) -> Option<Time> {
        #![allow(deprecated)]
        let max: Time = Bounded::max_value();
        if seconds > max.seconds() {
            None
        } else {
            Some(Time {value: seconds})
        }
    }
}

impl Bounded for Time {
    #![allow(deprecated)]
    fn min_value() -> Time {
        Time {value: 0}
    }
    fn max_value() -> Time {
        Time {value: 60039}
    }
}

impl fmt::Display for Time {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:03}:{:02}", self.seconds()/60, self.seconds()%60)
    }
}

/// Parsed ID3v1 tag metadata.
#[derive(Debug)]
pub struct Tag {
    /// The full title (ID3v1 + extension if present).
    pub title: Vec<u8>,
    /// The full artist (ID3v1 + extension if present).
    pub artist: Vec<u8>,
    /// The full album (ID3v1 + extension if present).
    pub album: Vec<u8>,
    /// A 4-digit string, if we are lucky
    pub year: Year,
    /// A free-form comment.
    pub comment: Vec<u8>,
    /// Number of the track, 0 if not set. ID3v1.1 data.
    pub track: u8,
    /// The genre mapping is standardized up to 79, some extensions exist.
    /// http://eyed3.nicfit.net/plugins/genres_plugin.html
    pub genre: u8,
    /// 1 (slow), 2, 3, 4 (fast) or 0 (not set). ID3v1 extended data.
    pub speed: u8,
    /// Free-form genre string. ID3v1 extended data.
    pub genre_str: Vec<u8>,
    /// The real start of the track, mmm:ss. ID3v1 extended data.
    pub start_time: Time,
    /// The real end of the track, mmm:ss. ID3v1 extended data.
    pub end_time: Time,
}

fn write_zero_padded<W: Write>(writer: &mut W, data: &[u8], offset: usize, len: usize) -> Result<(), io::Error> {
    let start = ::std::cmp::min(offset, data.len());
    let actual_len = ::std::cmp::min(offset+len, data.len());
    try!(writer.write(&data[start..actual_len]));
    for _ in 0..(len-(actual_len-start)) {
        try!(writer.write(&[0]));
    }
    Ok(())
}

impl Tag {
    /// Create a new ID3v1 tag with no information.
    pub fn new() -> Tag {
        Tag {
            title: vec![], artist: vec![], album: vec![], year: Year::new(0).unwrap(), comment: vec![], track: 0,
            genre: 0, speed: 0, genre_str: vec![], start_time: Time::new(0).unwrap(), end_time: Time::new(0).unwrap()
        }
    }
    /// Returns whether the tag contains information which would be lost if the extended tag were not written.
    pub fn has_extended_data(&self) -> bool {
        use self::Fields::*;
        self.title.len() > Title.length() ||
        self.artist.len() > Artist.length() ||
        self.album.len() > Album.length() ||
        self.speed > 0 ||
        self.genre_str.len() > 0 ||
        self.start_time.seconds() > 0 ||
        self.end_time.seconds() > 0
    }
    /// Write the simple ID3 tag (128 bytes) into the given writer.
    /// If write_track_number is true, the comment field will be truncated to 28 bytes and the removed two bytes will be used for a NUL and the track number.
    pub fn write<W: Write>(&self, writer: &mut W, write_track_number: bool) -> Result<(), io::Error> {
        use self::Fields::*;
        try!(writer.write(TAG));
        try!(write_zero_padded(writer, &*self.title, 0, Title.length()));
        try!(write_zero_padded(writer, &*self.artist, 0, Artist.length()));
        try!(write_zero_padded(writer, &*self.album, 0, Album.length()));
        try!(write!(writer,"{:04}", self.year.value()));
        if write_track_number {
            try!(writer.write(&self.comment[..Comment.length()-2]));
            try!(writer.write(&[0]));
            try!(writer.write(&[self.track]));
        } else {
            try!(writer.write(&self.comment[..Comment.length()]));
        }
        try!(writer.write(&[self.genre]));
        Ok(())
    }
    /// Write the extended portion of an ID3v1 tag (227 bytes) into the given writer.
    pub fn write_extended<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
        use self::Fields::*;
        use self::XFields::*;
        try!(write_zero_padded(writer, &*self.title, Title.length(), XTitle.length()));
        try!(write_zero_padded(writer, &*self.artist, Artist.length(), XArtist.length()));
        try!(write_zero_padded(writer, &*self.album, Album.length(), XAlbum.length()));
        try!(writer.write(&[self.speed]));
        try!(write_zero_padded(writer, &*self.genre_str, 0, XGenre.length()));
        try!(write!(writer,"{}", self.start_time));
        try!(write!(writer,"{}", self.end_time));
        Ok(())
    }
}

/// ID3v1 tag reading helpers.
trait ID3v1Helpers {
    /// Read `n` bytes starting at an offset from the end.
    fn read_from_end(&mut self, n:usize, offset:i64) -> Result<Vec<u8>, io::Error>;

    /// Read a null-terminated ISO-8859-1 string of size at most `n`, at an offset from the end.
    fn read_str(&mut self, n: usize, offset: i64) -> Result<String, io::Error>;
}

impl<R: Read + Seek> ID3v1Helpers for R {
    #[inline]
    fn read_from_end(&mut self, n: usize, offset:i64) -> Result<Vec<u8>, io::Error> {
        try!(self.seek(SeekFrom::End(-offset)));
        let mut v=Vec::with_capacity(n);
        self.read(&mut v).and(Ok(v))
    }

    #[inline]
    fn read_str(&mut self, n: usize, offset: i64) -> Result<String, io::Error> {
        self.read_from_end(n, offset).map(|vec| extract_nz_88591(vec))
    }
}

/// Checks for presence of the signature indicating an ID3v1 tag at the reader's current offset.
/// Consumes 3 bytes from the reader.
#[inline]
pub fn probe_tag<R: Read>(reader: &mut R) -> Result<bool, io::Error> {
    let mut x=&mut [0; 3/*TAG.len()*/];
    reader.read(x).and(Ok(TAG == x))
}

/// Checks for presence of the signature indicating an ID3v1 extended metadata tag at the reader's current offset.
/// Consumes 4 bytes from the reader.
#[inline]
pub fn probe_xtag<R: Read>(reader: &mut R) -> Result<bool, io::Error> {
    let mut x=&mut [0; 4/*TAGPLUS.len()*/];
    reader.read(x).and(Ok(TAGPLUS == x))
}

fn parse_year(s: &[u8]) -> Year {
    let zero = Year::new(0).unwrap();
    match ::std::str::from_utf8(s) {
        Ok(st) => {
            let mn: Option<u16> = str::parse(st).ok();
            let n = mn.unwrap_or(0);
            Year::new(n).unwrap_or(zero)
        },
        Err(_) => zero
    }
}

fn parse_time(s: &[u8]) -> Time {
    enum State {
        Seconds,
        Minutes,
        LeadingWhitespace,
    }

    let zero = Time::new(0).unwrap();

    let mut mult: u64=1;
    let mut seconds: u64=0;
    let mut state=State::Seconds;

    fn is_digit(s: u8) -> bool {
        s >= b'0' && s <= b'9'
    }
    fn value(s: u8) -> u8 {
        s-b'0'
    }
    for &i in s.iter().rev() {
        match state {
            State::Seconds =>
                if is_digit(i) {
                    seconds+=mult*value(i) as u64;
                    mult*=10;
                } else if i == b':' {
                    state=State::Minutes;
                    mult=60;
                } else {
                    return zero;
                },
            State::Minutes =>
                if is_digit(i) {
                    seconds+=mult*value(i) as u64;
                    mult*=60;
                } else if (i as char).is_whitespace() {
                    state=State::LeadingWhitespace;
                },
            State::LeadingWhitespace =>
                if (i as char).is_whitespace() {
                    continue
                } else {
                    return zero;
                },
        }
    }
    if seconds > 65535 {
        zero
    } else {
        Time::new(seconds as u16).unwrap_or(zero)
    }
}

/// Read an ID3v1 tag from a reader.
pub fn read_tag<R: Read>(reader: &mut R) -> Result<Tag, io::Error> {
    use self::Fields::*;

    let mut tag = Tag::new();
    // Try to read ID3v1 metadata.
    let has_tag = try!(probe_tag(reader));
    if has_tag {
        maybe_read!(reader, tag.title, Title.length());
        maybe_read!(reader, tag.artist, Artist.length());
        maybe_read!(reader, tag.album, Album.length());
        let mut year_str=vec![]; maybe_read!(reader, year_str, Year.length());
        tag.year=parse_year(&*year_str);
        maybe_read!(reader, tag.comment, Comment.length()-2);
        let track_guard_byte=read_u8!(reader);
        if track_guard_byte == 0 {
            tag.track=read_u8!(reader);
        } else {
            tag.comment.push(track_guard_byte);
            maybe_read!(reader, tag.comment, 1);
        }
        tag.genre=read_u8!(reader);
    }

    Ok(tag)
}

/// Read the extended portion of an extended ID3v1 tag from a reader, combining its data with a previously-read ID3v1 tag.
pub fn read_xtag<R: Read>(reader: &mut R, tag: &mut Tag) -> Result<(), io::Error> {
    use self::Fields::*;
    use self::XFields::*;

    // Try to read ID3v1 extended metadata.
    let has_xtag = try!(probe_xtag(reader));
    if has_xtag {
        maybe_read!(reader, tag.title, XTitle.length());
        maybe_read!(reader, tag.artist, XArtist.length());
        maybe_read!(reader, tag.album, XAlbum.length());
        tag.speed = read_u8!(reader);
        maybe_read!(reader, tag.genre_str, Genre.length());
        let mut start_str=vec![]; maybe_read!(reader, start_str, Start.length());
        tag.start_time=parse_time(&*start_str);
        let mut end_str=vec![]; maybe_read!(reader, end_str, End.length());
        tag.end_time=parse_time(&*end_str);
    }
    Ok(())
}

/// Read a string from a null-terminated ISO-8859-1 byte vector.
///
/// Read the whole vector if there is no null byte.
///
/// This function cannot fail, because UTF-8 is compatible with ISO-8859-1
/// at the code point level.
#[inline]
fn extract_nz_88591(s: Vec<u8>) -> String {
    // This works because the ISO 8859-1 code points match the unicode code
    // points. So,`c as char` will map correctly from ISO to unicode.
    s.into_iter().take_while(|&c| c!=0).map(|c| c as char).collect()
}

/// Remove trailing zeros from an &[u8].
pub fn truncate_zeros(mut s: &[u8]) -> &[u8] {
    while s.len() > 0 && s[s.len()-1] == 0 {
        s=&s[..s.len()-1]
    }
    s
}

/*pub fn read_seek<R: Read + Seek>(reader: &mut R) {
}*/

#[test]
fn smoke_test() {
    use std::path::Path;
    let mut f=::std::fs::File::open(&Path::new("test.mp3")).ok().expect("could not open `test.mp3`");
    f.seek(SeekFrom::End(-TAG_OFFSET));
    let mut tag=read_tag(&mut f).unwrap();
    println!("{:?}", tag);
    f.seek(SeekFrom::End(-TAGPLUS_OFFSET));
    read_xtag(&mut f, &mut tag);
    println!("{:?}", tag);
}
<commit_msg>id3v1: remove dead code<commit_after>use std::io::{self, Read, Write, Seek, SeekFrom};
use std::iter::IntoIterator;
use num::Bounded;
use std::fmt;

/// The fields in an ID3v1 tag, including the "1.1" track number field.
#[derive(Copy, Clone)]
#[allow(missing_docs)]
pub enum Fields {
    Title,
    Artist,
    Album,
    Year,
    Comment,
    Track,
    Genre,
}

impl Fields {
    fn length(&self) -> usize {
        LENGTHS[*self as usize] as usize
    }
}

static LENGTHS: &'static [i8]=&[30, 30, 30, 4, 30, -1, 1];

static TAG: &'static [u8] = b"TAG";
/// How far from the end of a file to probe for an ID3 tag signature.
pub static TAG_OFFSET: i64 = 128;

static TAGPLUS: &'static [u8] = b"TAG+";
/// How far from the end of a file to probe for an extended ID3 tag signature.
pub static TAGPLUS_OFFSET: i64 = 355;

static XLENGTHS: &'static [i8]=&[60, 60, 60, 30, 6, 6];

/// The fields in an extended ID3v1 tag.
#[derive(Copy, Clone)]
#[allow(missing_docs)]
pub enum XFields {
    XTitle,
    XArtist,
    XAlbum,
    Speed,
    XGenre,
    Start,
    End,
}

impl XFields {
    fn length(&self) -> usize {
        XLENGTHS[*self as usize] as usize
    }
}

/// ID3v1's notion of a four-digit year.
#[derive(Debug, Copy, Clone)]
pub struct Year
{
    value: u16,
}

impl Year {
    fn value(&self) -> u16 {
        self.value
    }
    fn new(year: u16) -> Option<Year> {
        #![allow(deprecated)]
        let max: Year = Bounded::max_value();
        if year > max.value() {
            None
        } else {
            Some(Year {value: year})
        }
    }
}

impl Bounded for Year {
    #![allow(deprecated)]
    fn min_value() -> Year {
        Year {value: 0}
    }
    fn max_value() -> Year {
        Year {value: 9999}
    }
}

/// ID3v1 extended time tags--encoded in the format "mmm:ss", a valid value can be a maximum of 999m99s = 999*60+99 = 60039 seconds.
#[derive(Copy, Clone, Debug)]
pub struct Time
{
    value: u16,
}

impl Time {
    fn seconds(&self) -> u16 {
        self.value
    }
    fn new(seconds: u16) -> Option<Time> {
        #![allow(deprecated)]
        let max: Time = Bounded::max_value();
        if seconds > max.seconds() {
            None
        } else {
            Some(Time {value: seconds})
        }
    }
}

impl Bounded for Time {
    #![allow(deprecated)]
    fn min_value() -> Time {
        Time {value: 0}
    }
    fn max_value() -> Time {
        Time {value: 60039}
    }
}

impl fmt::Display for Time {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:03}:{:02}", self.seconds()/60, self.seconds()%60)
    }
}

/// Parsed ID3v1 tag metadata.
#[derive(Debug)]
pub struct Tag {
    /// The full title (ID3v1 + extension if present).
    pub title: Vec<u8>,
    /// The full artist (ID3v1 + extension if present).
    pub artist: Vec<u8>,
    /// The full album (ID3v1 + extension if present).
    pub album: Vec<u8>,
    /// A 4-digit string, if we are lucky
    pub year: Year,
    /// A free-form comment.
    pub comment: Vec<u8>,
    /// Number of the track, 0 if not set. ID3v1.1 data.
    pub track: u8,
    /// The genre mapping is standardized up to 79, some extensions exist.
    /// http://eyed3.nicfit.net/plugins/genres_plugin.html
    pub genre: u8,
    /// 1 (slow), 2, 3, 4 (fast) or 0 (not set). ID3v1 extended data.
    pub speed: u8,
    /// Free-form genre string. ID3v1 extended data.
    pub genre_str: Vec<u8>,
    /// The real start of the track, mmm:ss. ID3v1 extended data.
    pub start_time: Time,
    /// The real end of the track, mmm:ss. ID3v1 extended data.
    pub end_time: Time,
}

fn write_zero_padded<W: Write>(writer: &mut W, data: &[u8], offset: usize, len: usize) -> Result<(), io::Error> {
    let start = ::std::cmp::min(offset, data.len());
    let actual_len = ::std::cmp::min(offset+len, data.len());
    try!(writer.write(&data[start..actual_len]));
    for _ in 0..(len-(actual_len-start)) {
        try!(writer.write(&[0]));
    }
    Ok(())
}

impl Tag {
    /// Create a new ID3v1 tag with no information.
    pub fn new() -> Tag {
        Tag {
            title: vec![], artist: vec![], album: vec![], year: Year::new(0).unwrap(), comment: vec![], track: 0,
            genre: 0, speed: 0, genre_str: vec![], start_time: Time::new(0).unwrap(), end_time: Time::new(0).unwrap()
        }
    }
    /// Returns whether the tag contains information which would be lost if the extended tag were not written.
    pub fn has_extended_data(&self) -> bool {
        use self::Fields::*;
        self.title.len() > Title.length() ||
        self.artist.len() > Artist.length() ||
        self.album.len() > Album.length() ||
        self.speed > 0 ||
        self.genre_str.len() > 0 ||
        self.start_time.seconds() > 0 ||
        self.end_time.seconds() > 0
    }
    /// Write the simple ID3 tag (128 bytes) into the given writer.
    /// If write_track_number is true, the comment field will be truncated to 28 bytes and the removed two bytes will be used for a NUL and the track number.
    pub fn write<W: Write>(&self, writer: &mut W, write_track_number: bool) -> Result<(), io::Error> {
        use self::Fields::*;
        try!(writer.write(TAG));
        try!(write_zero_padded(writer, &*self.title, 0, Title.length()));
        try!(write_zero_padded(writer, &*self.artist, 0, Artist.length()));
        try!(write_zero_padded(writer, &*self.album, 0, Album.length()));
        try!(write!(writer,"{:04}", self.year.value()));
        if write_track_number {
            try!(writer.write(&self.comment[..Comment.length()-2]));
            try!(writer.write(&[0]));
            try!(writer.write(&[self.track]));
        } else {
            try!(writer.write(&self.comment[..Comment.length()]));
        }
        try!(writer.write(&[self.genre]));
        Ok(())
    }
    /// Write the extended portion of an ID3v1 tag (227 bytes) into the given writer.
    pub fn write_extended<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
        use self::Fields::*;
        use self::XFields::*;
        try!(write_zero_padded(writer, &*self.title, Title.length(), XTitle.length()));
        try!(write_zero_padded(writer, &*self.artist, Artist.length(), XArtist.length()));
        try!(write_zero_padded(writer, &*self.album, Album.length(), XAlbum.length()));
        try!(writer.write(&[self.speed]));
        try!(write_zero_padded(writer, &*self.genre_str, 0, XGenre.length()));
        try!(write!(writer,"{}", self.start_time));
        try!(write!(writer,"{}", self.end_time));
        Ok(())
    }
}

/// Checks for presence of the signature indicating an ID3v1 tag at the reader's current offset.
/// Consumes 3 bytes from the reader.
#[inline]
pub fn probe_tag<R: Read>(reader: &mut R) -> Result<bool, io::Error> {
    let mut x=&mut [0; 3/*TAG.len()*/];
    reader.read(x).and(Ok(TAG == x))
}

/// Checks for presence of the signature indicating an ID3v1 extended metadata tag at the reader's current offset.
/// Consumes 4 bytes from the reader.
#[inline]
pub fn probe_xtag<R: Read>(reader: &mut R) -> Result<bool, io::Error> {
    let mut x=&mut [0; 4/*TAGPLUS.len()*/];
    reader.read(x).and(Ok(TAGPLUS == x))
}

fn parse_year(s: &[u8]) -> Year {
    let zero = Year::new(0).unwrap();
    match ::std::str::from_utf8(s) {
        Ok(st) => {
            let mn: Option<u16> = str::parse(st).ok();
            let n = mn.unwrap_or(0);
            Year::new(n).unwrap_or(zero)
        },
        Err(_) => zero
    }
}

fn parse_time(s: &[u8]) -> Time {
    enum State {
        Seconds,
        Minutes,
        LeadingWhitespace,
    }

    let zero = Time::new(0).unwrap();

    let mut mult: u64=1;
    let mut seconds: u64=0;
    let mut state=State::Seconds;

    fn is_digit(s: u8) -> bool {
        s >= b'0' && s <= b'9'
    }
    fn value(s: u8) -> u8 {
        s-b'0'
    }
    for &i in s.iter().rev() {
        match state {
            State::Seconds =>
                if is_digit(i) {
                    seconds+=mult*value(i) as u64;
                    mult*=10;
                } else if i == b':' {
                    state=State::Minutes;
                    mult=60;
                } else {
                    return zero;
                },
            State::Minutes =>
                if is_digit(i) {
                    seconds+=mult*value(i) as u64;
                    mult*=60;
                } else if (i as char).is_whitespace() {
                    state=State::LeadingWhitespace;
                },
            State::LeadingWhitespace =>
                if (i as char).is_whitespace() {
                    continue
                } else {
                    return zero;
                },
        }
    }
    if seconds > 65535 {
        zero
    } else {
        Time::new(seconds as u16).unwrap_or(zero)
    }
}

/// Read an ID3v1 tag from a reader.
pub fn read_tag<R: Read>(reader: &mut R) -> Result<Tag, io::Error> {
    use self::Fields::*;

    let mut tag = Tag::new();
    // Try to read ID3v1 metadata.
    let has_tag = try!(probe_tag(reader));
    if has_tag {
        maybe_read!(reader, tag.title, Title.length());
        maybe_read!(reader, tag.artist, Artist.length());
        maybe_read!(reader, tag.album, Album.length());
        let mut year_str=vec![]; maybe_read!(reader, year_str, Year.length());
        tag.year=parse_year(&*year_str);
        maybe_read!(reader, tag.comment, Comment.length()-2);
        let track_guard_byte=read_u8!(reader);
        if track_guard_byte == 0 {
            tag.track=read_u8!(reader);
        } else {
            tag.comment.push(track_guard_byte);
            maybe_read!(reader, tag.comment, 1);
        }
        tag.genre=read_u8!(reader);
    }

    Ok(tag)
}

/// Read the extended portion of an extended ID3v1 tag from a reader, combining its data with a previously-read ID3v1 tag.
pub fn read_xtag<R: Read>(reader: &mut R, tag: &mut Tag) -> Result<(), io::Error> {
    use self::Fields::*;
    use self::XFields::*;

    // Try to read ID3v1 extended metadata.
    let has_xtag = try!(probe_xtag(reader));
    if has_xtag {
        maybe_read!(reader, tag.title, XTitle.length());
        maybe_read!(reader, tag.artist, XArtist.length());
        maybe_read!(reader, tag.album, XAlbum.length());
        tag.speed = read_u8!(reader);
        maybe_read!(reader, tag.genre_str, Genre.length());
        let mut start_str=vec![]; maybe_read!(reader, start_str, Start.length());
        tag.start_time=parse_time(&*start_str);
        let mut end_str=vec![]; maybe_read!(reader, end_str, End.length());
        tag.end_time=parse_time(&*end_str);
    }
    Ok(())
}

/// Remove trailing zeros from an &[u8].
pub fn truncate_zeros(mut s: &[u8]) -> &[u8] {
    while s.len() > 0 && s[s.len()-1] == 0 {
        s=&s[..s.len()-1]
    }
    s
}

/*pub fn read_seek<R: Read + Seek>(reader: &mut R) {
}*/

#[test]
fn smoke_test() {
    use std::path::Path;
    let mut f=::std::fs::File::open(&Path::new("test.mp3")).ok().expect("could not open `test.mp3`");
    f.seek(SeekFrom::End(-TAG_OFFSET));
    let mut tag=read_tag(&mut f).unwrap();
    println!("{:?}", tag);
    f.seek(SeekFrom::End(-TAGPLUS_OFFSET));
    read_xtag(&mut f, &mut tag);
    println!("{:?}", tag);
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Ben Ashford
 *
 * 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.
 */

//! An implementation of the RESP protocol

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

use bytes::{BufMut, BytesMut};

use tokio_io::codec::{Decoder, Encoder};

use super::error::Error;

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum RespValue {
    Array(Vec<RespValue>),
    BulkString(Vec<u8>),
    Error(String),
    Integer(usize),
    SimpleString(String),
}

impl RespValue {
    pub fn into_string(self) -> Result<String, Error> {
        match self {
            RespValue::Array(_) => Err(Error::RESP("Not stringable".into())),
            RespValue::BulkString(ref bytes) => Ok(String::from_utf8_lossy(bytes).into_owned()),
            RespValue::Error(string) => Ok(string),
            RespValue::Integer(i) => Ok(i.to_string()),
            RespValue::SimpleString(string) => Ok(string),
        }
    }
}

pub trait ToResp {
    fn to_resp(&self) -> RespValue;
}

impl<'a> ToResp for &'a str {
    fn to_resp(&self) -> RespValue {
        RespValue::BulkString(self.as_bytes().into())
    }
}

impl<'a> ToResp for &'a [&'a str] {
    fn to_resp(&self) -> RespValue {
        RespValue::Array(self.as_ref().iter().map(|x| x.to_resp()).collect())
    }
}

impl<'a> ToResp for String {
    fn to_resp(&self) -> RespValue {
        RespValue::BulkString(self.as_bytes().into())
    }
}

impl<'a, T> ToResp for Vec<T>
    where T: ToResp
{
    fn to_resp(&self) -> RespValue {
        RespValue::Array(self.into_iter().map(|v| v.to_resp()).collect())
    }
}

impl<T: ToResp> From<T> for RespValue {
    fn from(from: T) -> RespValue {
        from.to_resp()
    }
}

/// Codec to read frames
pub struct RespCodec;

fn write_rn(buf: &mut BytesMut) {
    buf.put_u8(b'\r');
    buf.put_u8(b'\n');
}

fn check_and_reserve(buf: &mut BytesMut, amt: usize) {
    let remaining_bytes = buf.remaining_mut();
    if remaining_bytes < amt {
        buf.reserve(amt);
    }
}

fn write_header(symb: u8, len: usize, buf: &mut BytesMut) {
    let len_as_string = len.to_string();
    let len_as_bytes = len_as_string.as_bytes();
    let header_bytes = 1 + len_as_bytes.len() + 2;
    check_and_reserve(buf, header_bytes);
    buf.put_u8(symb);
    buf.extend(len_as_bytes);
    write_rn(buf);
}

fn write_simple_string(symb: u8, string: &str, buf: &mut BytesMut) {
    let bytes = string.as_bytes();
    let size = 1 + bytes.len() + 2;
    check_and_reserve(buf, size);
    buf.put_u8(symb);
    buf.extend(bytes);
    write_rn(buf);
}

impl Encoder for RespCodec {
    type Item = RespValue;
    type Error = io::Error;

    fn encode(&mut self, msg: RespValue, buf: &mut BytesMut) -> Result<(), Self::Error> {
        match msg {
            RespValue::Array(ary) => {
                write_header(b'*', ary.len(), buf);
                for v in ary {
                    self.encode(v, buf)?;
                }
            }
            RespValue::BulkString(bstr) => {
                let len = bstr.len();
                write_header(b'$', len, buf);
                check_and_reserve(buf, len + 2);
                buf.extend(bstr);
                write_rn(buf);
            }
            RespValue::Error(ref string) => {
                write_simple_string(b'-', string, buf);
            }
            RespValue::Integer(val) => {
                // Simple integer are just the header
                write_header(b':', val, buf);
            }
            RespValue::SimpleString(ref string) => {
                write_simple_string(b'+', string, buf);
            }
        }
        Ok(())
    }
}

#[inline]
fn parse_error(message: String) -> Error {
    Error::RESP(message)
}

/// Many RESP types have their length (which is either bytes or "number of elements", depending on context)
/// encoded as a string, terminated by "\r\n", this looks for them.
///
/// Only return the string if the whole sequence is complete, including the terminator bytes (but those final
/// two bytes will not be returned)
///
/// TODO - rename this function potentially, it's used for simple integers too
fn scan_integer<'a>(buf: &'a mut BytesMut, idx: usize) -> Result<Option<(usize, &'a [u8])>, Error> {
    let length = buf.len();
    let mut at_end = false;
    let mut pos = idx;
    loop {
        if length <= pos {
            return Ok(None);
        }
        match (at_end, buf[pos]) {
            (true, b'\n') => return Ok(Some((pos + 1, &buf[idx..pos - 1]))),
            (false, b'\r') => at_end = true,
            (false, b'0'...b'9') => (),
            (_, val) => return Err(parse_error(format!("Unexpected byte in size_string: {}", val))),
        }
        pos += 1;
    }
}

fn scan_string(buf: &mut BytesMut, idx: usize) -> Option<(usize, String)> {
    let length = buf.len();
    let mut at_end = false;
    let mut pos = idx;
    loop {
        if length <= pos {
            return None;
        }
        match (at_end, buf[pos]) {
            (true, b'\n') => {
                let value = String::from_utf8_lossy(&buf[idx..pos - 1]).into_owned();
                return Some((pos + 1, value));
            }
            (true, _) => at_end = false,
            (false, b'\r') => at_end = true,
            (false, _) => (),
        }
        pos += 1;
    }
}

fn decode_raw_integer(buf: &mut BytesMut, idx: usize) -> Result<Option<(usize, usize)>, Error> {
    match scan_integer(buf, idx) {
        Ok(None) => Ok(None),
        Ok(Some((pos, int_str))) => {
            let int: usize = str::from_utf8(int_str)
                .expect("Not a string")
                .parse()
                .expect("Not an integer");
            Ok(Some((pos, int)))
        }
        Err(e) => Err(e),
    }
}

type DecodeResult = Result<Option<(usize, RespValue)>, Error>;

fn decode_bulk_string(buf: &mut BytesMut, idx: usize) -> DecodeResult {
    match decode_raw_integer(buf, idx) {
        Ok(None) => Ok(None),
        Ok(Some((pos, size))) => {
            let remaining = buf.len() - pos;
            let required_bytes = size + 2;

            if remaining < required_bytes {
                return Ok(None);
            }

            let bulk_string = RespValue::BulkString(buf[pos..(pos + size)].to_vec());
            Ok(Some((pos + required_bytes, bulk_string)))
        }
        Err(e) => Err(e),
    }
}

fn decode_array(buf: &mut BytesMut, idx: usize) -> DecodeResult {
    match decode_raw_integer(buf, idx) {
        Ok(None) => Ok(None),
        Ok(Some((pos, size))) => {
            let mut pos = pos;
            let mut values = Vec::with_capacity(size);
            for _ in 0..size {
                match decode(buf, pos) {
                    Ok(None) => return Ok(None),
                    Ok(Some((new_pos, value))) => {
                        values.push(value);
                        pos = new_pos;
                    }
                    Err(e) => return Err(e),
                }
            }
            Ok(Some((pos, RespValue::Array(values))))
        }
        Err(e) => Err(e),
    }
}

fn decode_integer(buf: &mut BytesMut, idx: usize) -> DecodeResult {
    match decode_raw_integer(buf, idx) {
        Ok(None) => Ok(None),
        Ok(Some((pos, int))) => Ok(Some((pos, RespValue::Integer(int)))),
        Err(e) => Err(e),
    }
}

/// A simple string is any series of bytes that ends with `\r\n`
fn decode_simple_string(buf: &mut BytesMut, idx: usize) -> DecodeResult {
    match scan_string(buf, idx) {
        None => Ok(None),
        Some((pos, string)) => Ok(Some((pos, RespValue::SimpleString(string)))),
    }
}

fn decode_error(buf: &mut BytesMut, idx: usize) -> DecodeResult {
    match scan_string(buf, idx) {
        None => Ok(None),
        Some((pos, string)) => Ok(Some((pos, RespValue::Error(string)))),
    }
}

fn decode(buf: &mut BytesMut, idx: usize) -> DecodeResult {
    let length = buf.len();
    if length <= idx {
        return Ok(None);
    }

    let first_byte = buf[idx];
    match first_byte {
        b'$' => decode_bulk_string(buf, idx + 1),
        b'*' => decode_array(buf, idx + 1),
        b':' => decode_integer(buf, idx + 1),
        b'+' => decode_simple_string(buf, idx + 1),
        b'-' => decode_error(buf, idx + 1),
        _ => Err(parse_error(format!("Unexpected byte: {}", first_byte))),
    }
}

impl Decoder for RespCodec {
    type Item = RespValue;
    type Error = Error;

    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        match decode(buf, 0) {
            Ok(None) => Ok(None),
            Ok(Some((pos, item))) => {
                buf.split_to(pos);
                Ok(Some(item))
            }
            Err(e) => Err(e),
        }
    }
}

#[cfg(test)]
mod tests {
    use bytes::BytesMut;

    use tokio_io::codec::{Decoder, Encoder};

    use super::{RespCodec, RespValue};

    #[test]
    fn test_bulk_string() {
        let resp_object = RespValue::BulkString("THISISATEST".as_bytes().to_vec());
        let mut bytes = BytesMut::new();
        let mut codec = RespCodec;
        codec.encode(resp_object.clone(), &mut bytes).unwrap();
        assert_eq!(b"$11\r\nTHISISATEST\r\n".to_vec(), bytes.to_vec());

        let deserialized = codec.decode(&mut bytes).unwrap().unwrap();
        assert_eq!(deserialized, resp_object);
    }

    #[test]
    fn test_array() {
        let resp_object = RespValue::Array(vec!["TEST1".into(), "TEST2".into()]);
        let mut bytes = BytesMut::new();
        let mut codec = RespCodec;
        codec.encode(resp_object.clone(), &mut bytes).unwrap();
        assert_eq!(b"*2\r\n$5\r\nTEST1\r\n$5\r\nTEST2\r\n".to_vec(),
                   bytes.to_vec());

        let deserialized = codec.decode(&mut bytes).unwrap().unwrap();
        assert_eq!(deserialized, resp_object);
    }
}<commit_msg>Documentation and minor changes<commit_after>/*
 * Copyright 2017 Ben Ashford
 *
 * 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.
 */

//! An implementation of the RESP protocol

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

use bytes::{BufMut, BytesMut};

use tokio_io::codec::{Decoder, Encoder};

use super::error::Error;

/// A single RESP value, this owns the data that is read/to-be written to Redis.
///
/// It is cloneable to allow multiple copies to be delivered in certain circumstances, e.g. multiple
/// subscribers to the same topic. TODO - revisit this assumption to make sure its sound, perhaps a
/// single-subscriber enforcement would make more sense, or sharing via `Arc`
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum RespValue {
    /// Zero, one or more other `RespValue`s.
    Array(Vec<RespValue>),

    /// A bulk string.  In Redis terminology a string is a byte-array, so this is stored as a
    /// vector of `u8`s to allow clients to interpret the bytes as appropriate.
    BulkString(Vec<u8>),

    /// An error from the Redis server
    Error(String),

    Integer(usize),

    SimpleString(String),
}

impl RespValue {
    /// Convert into a `String` if possible.
    ///
    /// Warning: this area is subject to change as its probably too permissive in its current form.
    pub fn into_string(self) -> Result<String, Error> {
        match self {
            RespValue::Array(_) => Err(Error::RESP("Not stringable".into())),
            RespValue::BulkString(ref bytes) => Ok(String::from_utf8_lossy(bytes).into_owned()),
            RespValue::Error(string) => Ok(string),
            RespValue::Integer(i) => Ok(i.to_string()),
            RespValue::SimpleString(string) => Ok(string),
        }
    }
}

/// A trait to be implemented on types that can be automatically converted into `RespValue`s.
///
/// A `From<T>` where `T: ToResp` has been implemented so that everything that implements `ToResp`
/// can be converted with conversion traits.
pub trait ToResp {
    fn to_resp(&self) -> RespValue;
}

impl<'a> ToResp for &'a str {
    fn to_resp(&self) -> RespValue {
        RespValue::BulkString(self.as_bytes().into())
    }
}

impl<'a, T> ToResp for &'a [T]
    where T: ToResp
{
    fn to_resp(&self) -> RespValue {
        RespValue::Array(self.as_ref().iter().map(|x| x.to_resp()).collect())
    }
}

impl<'a> ToResp for String {
    fn to_resp(&self) -> RespValue {
        RespValue::BulkString(self.as_bytes().into())
    }
}

impl<'a, T> ToResp for Vec<T>
    where T: ToResp
{
    fn to_resp(&self) -> RespValue {
        RespValue::Array(self.into_iter().map(|v| v.to_resp()).collect())
    }
}

impl<T: ToResp> From<T> for RespValue {
    fn from(from: T) -> RespValue {
        from.to_resp()
    }
}

/// Codec to read frames
pub struct RespCodec;

fn write_rn(buf: &mut BytesMut) {
    buf.put_u8(b'\r');
    buf.put_u8(b'\n');
}

fn check_and_reserve(buf: &mut BytesMut, amt: usize) {
    let remaining_bytes = buf.remaining_mut();
    if remaining_bytes < amt {
        buf.reserve(amt);
    }
}

fn write_header(symb: u8, len: usize, buf: &mut BytesMut) {
    let len_as_string = len.to_string();
    let len_as_bytes = len_as_string.as_bytes();
    let header_bytes = 1 + len_as_bytes.len() + 2;
    check_and_reserve(buf, header_bytes);
    buf.put_u8(symb);
    buf.extend(len_as_bytes);
    write_rn(buf);
}

fn write_simple_string(symb: u8, string: &str, buf: &mut BytesMut) {
    let bytes = string.as_bytes();
    let size = 1 + bytes.len() + 2;
    check_and_reserve(buf, size);
    buf.put_u8(symb);
    buf.extend(bytes);
    write_rn(buf);
}

impl Encoder for RespCodec {
    type Item = RespValue;
    type Error = io::Error;

    fn encode(&mut self, msg: RespValue, buf: &mut BytesMut) -> Result<(), Self::Error> {
        match msg {
            RespValue::Array(ary) => {
                write_header(b'*', ary.len(), buf);
                for v in ary {
                    self.encode(v, buf)?;
                }
            }
            RespValue::BulkString(bstr) => {
                let len = bstr.len();
                write_header(b'$', len, buf);
                check_and_reserve(buf, len + 2);
                buf.extend(bstr);
                write_rn(buf);
            }
            RespValue::Error(ref string) => {
                write_simple_string(b'-', string, buf);
            }
            RespValue::Integer(val) => {
                // Simple integer are just the header
                write_header(b':', val, buf);
            }
            RespValue::SimpleString(ref string) => {
                write_simple_string(b'+', string, buf);
            }
        }
        Ok(())
    }
}

#[inline]
fn parse_error(message: String) -> Error {
    Error::RESP(message)
}

/// Many RESP types have their length (which is either bytes or "number of elements", depending on context)
/// encoded as a string, terminated by "\r\n", this looks for them.
///
/// Only return the string if the whole sequence is complete, including the terminator bytes (but those final
/// two bytes will not be returned)
///
/// TODO - rename this function potentially, it's used for simple integers too
fn scan_integer<'a>(buf: &'a mut BytesMut, idx: usize) -> Result<Option<(usize, &'a [u8])>, Error> {
    let length = buf.len();
    let mut at_end = false;
    let mut pos = idx;
    loop {
        if length <= pos {
            return Ok(None);
        }
        match (at_end, buf[pos]) {
            (true, b'\n') => return Ok(Some((pos + 1, &buf[idx..pos - 1]))),
            (false, b'\r') => at_end = true,
            (false, b'0'...b'9') => (),
            (_, val) => return Err(parse_error(format!("Unexpected byte in size_string: {}", val))),
        }
        pos += 1;
    }
}

fn scan_string(buf: &mut BytesMut, idx: usize) -> Option<(usize, String)> {
    let length = buf.len();
    let mut at_end = false;
    let mut pos = idx;
    loop {
        if length <= pos {
            return None;
        }
        match (at_end, buf[pos]) {
            (true, b'\n') => {
                let value = String::from_utf8_lossy(&buf[idx..pos - 1]).into_owned();
                return Some((pos + 1, value));
            }
            (true, _) => at_end = false,
            (false, b'\r') => at_end = true,
            (false, _) => (),
        }
        pos += 1;
    }
}

fn decode_raw_integer(buf: &mut BytesMut, idx: usize) -> Result<Option<(usize, usize)>, Error> {
    match scan_integer(buf, idx) {
        Ok(None) => Ok(None),
        Ok(Some((pos, int_str))) => {
            let int: usize = str::from_utf8(int_str)
                .expect("Not a string")
                .parse()
                .expect("Not an integer");
            Ok(Some((pos, int)))
        }
        Err(e) => Err(e),
    }
}

type DecodeResult = Result<Option<(usize, RespValue)>, Error>;

fn decode_bulk_string(buf: &mut BytesMut, idx: usize) -> DecodeResult {
    match decode_raw_integer(buf, idx) {
        Ok(None) => Ok(None),
        Ok(Some((pos, size))) => {
            let remaining = buf.len() - pos;
            let required_bytes = size + 2;

            if remaining < required_bytes {
                return Ok(None);
            }

            let bulk_string = RespValue::BulkString(buf[pos..(pos + size)].to_vec());
            Ok(Some((pos + required_bytes, bulk_string)))
        }
        Err(e) => Err(e),
    }
}

fn decode_array(buf: &mut BytesMut, idx: usize) -> DecodeResult {
    match decode_raw_integer(buf, idx) {
        Ok(None) => Ok(None),
        Ok(Some((pos, size))) => {
            let mut pos = pos;
            let mut values = Vec::with_capacity(size);
            for _ in 0..size {
                match decode(buf, pos) {
                    Ok(None) => return Ok(None),
                    Ok(Some((new_pos, value))) => {
                        values.push(value);
                        pos = new_pos;
                    }
                    Err(e) => return Err(e),
                }
            }
            Ok(Some((pos, RespValue::Array(values))))
        }
        Err(e) => Err(e),
    }
}

fn decode_integer(buf: &mut BytesMut, idx: usize) -> DecodeResult {
    match decode_raw_integer(buf, idx) {
        Ok(None) => Ok(None),
        Ok(Some((pos, int))) => Ok(Some((pos, RespValue::Integer(int)))),
        Err(e) => Err(e),
    }
}

/// A simple string is any series of bytes that ends with `\r\n`
fn decode_simple_string(buf: &mut BytesMut, idx: usize) -> DecodeResult {
    match scan_string(buf, idx) {
        None => Ok(None),
        Some((pos, string)) => Ok(Some((pos, RespValue::SimpleString(string)))),
    }
}

fn decode_error(buf: &mut BytesMut, idx: usize) -> DecodeResult {
    match scan_string(buf, idx) {
        None => Ok(None),
        Some((pos, string)) => Ok(Some((pos, RespValue::Error(string)))),
    }
}

fn decode(buf: &mut BytesMut, idx: usize) -> DecodeResult {
    let length = buf.len();
    if length <= idx {
        return Ok(None);
    }

    let first_byte = buf[idx];
    match first_byte {
        b'$' => decode_bulk_string(buf, idx + 1),
        b'*' => decode_array(buf, idx + 1),
        b':' => decode_integer(buf, idx + 1),
        b'+' => decode_simple_string(buf, idx + 1),
        b'-' => decode_error(buf, idx + 1),
        _ => Err(parse_error(format!("Unexpected byte: {}", first_byte))),
    }
}

impl Decoder for RespCodec {
    type Item = RespValue;
    type Error = Error;

    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        match decode(buf, 0) {
            Ok(None) => Ok(None),
            Ok(Some((pos, item))) => {
                buf.split_to(pos);
                Ok(Some(item))
            }
            Err(e) => Err(e),
        }
    }
}

#[cfg(test)]
mod tests {
    use bytes::BytesMut;

    use tokio_io::codec::{Decoder, Encoder};

    use super::{RespCodec, RespValue};

    #[test]
    fn test_bulk_string() {
        let resp_object = RespValue::BulkString("THISISATEST".as_bytes().to_vec());
        let mut bytes = BytesMut::new();
        let mut codec = RespCodec;
        codec.encode(resp_object.clone(), &mut bytes).unwrap();
        assert_eq!(b"$11\r\nTHISISATEST\r\n".to_vec(), bytes.to_vec());

        let deserialized = codec.decode(&mut bytes).unwrap().unwrap();
        assert_eq!(deserialized, resp_object);
    }

    #[test]
    fn test_array() {
        let resp_object = RespValue::Array(vec!["TEST1".into(), "TEST2".into()]);
        let mut bytes = BytesMut::new();
        let mut codec = RespCodec;
        codec.encode(resp_object.clone(), &mut bytes).unwrap();
        assert_eq!(b"*2\r\n$5\r\nTEST1\r\n$5\r\nTEST2\r\n".to_vec(),
                   bytes.to_vec());

        let deserialized = codec.decode(&mut bytes).unwrap().unwrap();
        assert_eq!(deserialized, resp_object);
    }
}<|endoftext|>


<commit_before>use compositor::View;
use wlroots::{Origin, SeatHandle, SeatHandler};

#[derive(Debug, Default)]
pub struct SeatManager;

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Action {
    /// We are moving a view.
    ///
    /// The start is the surface level coordinates of where the first click was
    Moving { start: Origin }
}

#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct Seat {
    pub seat: SeatHandle,
    pub focused: Option<View>,
    pub action: Option<Action>,
    pub meta: bool
}

impl Seat {
    pub fn new(seat: SeatHandle) -> Seat {
        Seat { seat,
               meta: true,
               ..Seat::default() }
    }
}

impl SeatHandler for SeatManager {}

impl SeatManager {
    pub fn new() -> Self {
        SeatManager::default()
    }
}
<commit_msg>default meta to false<commit_after>use compositor::View;
use wlroots::{Origin, SeatHandle, SeatHandler};

#[derive(Debug, Default)]
pub struct SeatManager;

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Action {
    /// We are moving a view.
    ///
    /// The start is the surface level coordinates of where the first click was
    Moving { start: Origin }
}

#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct Seat {
    pub seat: SeatHandle,
    pub focused: Option<View>,
    pub action: Option<Action>,
    pub meta: bool
}

impl Seat {
    pub fn new(seat: SeatHandle) -> Seat {
        Seat { seat,
               meta: false,
               ..Seat::default() }
    }
}

impl SeatHandler for SeatManager {}

impl SeatManager {
    pub fn new() -> Self {
        SeatManager::default()
    }
}
<|endoftext|>