├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── lib.rs └── reader ├── mod.rs └── workbook.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xls" 3 | version = "0.1.0" 4 | authors = ["Evan Miller "] 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Evan Miller 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-xls 2 | Read Excel files from Rust. Requires **[libxls](http://libxls.sourceforge.net)**. 3 | 4 | Example program: 5 | 6 | ```rust 7 | extern crate xls; 8 | 9 | use std::env; 10 | 11 | fn main() { 12 | let version = xls::reader::version(); 13 | let arg_vec = env::args().collect::>(); 14 | if arg_vec.len() == 2 { 15 | println!("libxls version: {}", version); 16 | let maybe_handle = xls::reader::workbook::new(arg_vec[1].clone()); 17 | match maybe_handle { 18 | Some(workbook) => { 19 | println!("Success!"); 20 | for sheet in workbook.sheets() { 21 | println!("+ Sheet"); 22 | for row in sheet.rows() { 23 | println!("++ Row {}", row.index); 24 | for cell in row.cells() { 25 | match cell.value() { 26 | Some(value) => { 27 | println!("+++ Cell {}: {}", cell.col_number, 28 | value); 29 | }, 30 | None => { }, 31 | } 32 | } 33 | } 34 | } 35 | }, 36 | None => println!("Failure!"), 37 | } 38 | } else { 39 | println!("Usage: {} ", arg_vec[0]); 40 | std::process::exit(1); 41 | }; 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod reader; 2 | -------------------------------------------------------------------------------- /src/reader/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod workbook; 2 | 3 | use std::os::raw::c_char; 4 | use std::ffi::CStr; 5 | use std::str; 6 | 7 | #[link(name = "xlsreader")] 8 | extern { 9 | fn xls_getVersion() -> *const c_char; 10 | } 11 | 12 | pub fn version() -> String { 13 | let c_string = unsafe { CStr::from_ptr(xls_getVersion()) }; 14 | str::from_utf8(c_string.to_bytes()).unwrap().to_string() 15 | } 16 | -------------------------------------------------------------------------------- /src/reader/workbook.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::c_char; 2 | use std::os::raw::c_void; 3 | use std::os::raw::c_int; 4 | use std::mem; 5 | use std::ffi::CString; 6 | use std::ffi::CStr; 7 | use std::str; 8 | use std::fmt::Display; 9 | use std::fmt::Formatter; 10 | use std::fmt::Result; 11 | 12 | pub struct Worksheet { 13 | pointer : *const c_void, 14 | } 15 | pub struct Worksheets<'a> { 16 | workbook : &'a Workbook, 17 | current_index : usize, 18 | } 19 | 20 | pub struct Workbook { 21 | pointer : *const c_void, 22 | } 23 | 24 | pub struct Row<'a> { 25 | pub index: i32, 26 | worksheet: &'a Worksheet, 27 | pointer: *const c_void, 28 | } 29 | pub struct Rows<'a> { 30 | worksheet : &'a Worksheet, 31 | current_row : i32, 32 | } 33 | 34 | #[repr(C)] 35 | struct NativeCell { 36 | id: u16, 37 | row: u16, 38 | col: u16, 39 | xf: u16, 40 | str: *const i8, 41 | d: f64, 42 | l: i32, 43 | width: u16, 44 | colspan: u16, 45 | rowspan: u16, 46 | is_hidden: i8, 47 | } 48 | 49 | enum CellType { 50 | ExcelRecordRK = 0x027E, 51 | ExcelRecordMulRK= 0x00BD, 52 | ExcelRecordNumber = 0x0203, 53 | ExcelRecordBoolErr = 0x0205, 54 | ExcelRecordFormula = 0x0006, 55 | } 56 | 57 | pub enum CellValue { 58 | DoubleValue(f64), 59 | StringValue{ value: String }, 60 | } 61 | 62 | pub struct Cell { 63 | pub row_number: i32, 64 | pub col_number: i32, 65 | pointer: *const NativeCell, 66 | } 67 | pub struct Cells<'a> { 68 | row: &'a Row<'a>, 69 | current_col: i32, 70 | } 71 | 72 | extern { 73 | fn xls_open(filename: *const c_char, encoding: *const c_char) -> *const c_void; 74 | fn xls_close_WB(wb: *const c_void); 75 | fn xls_getWorkSheet(wb: *const c_void, index: c_int) -> *const c_void; 76 | fn xls_parseWorkSheet(ws: *const c_void); 77 | fn xls_row(ws: *const c_void, row: u16) -> *const c_void; 78 | fn xls_cell(ws: *const c_void, row: u16, col: u16) -> *const NativeCell; 79 | } 80 | 81 | fn is_null(handle: *const c_void) -> bool { 82 | let ptr_as_int : usize = unsafe { mem::transmute(handle) }; 83 | return ptr_as_int == 0; 84 | } 85 | 86 | pub fn new(filename: String) -> Option { 87 | let file_string = CString::new(filename.clone()).unwrap(); 88 | let enc_string = CString::new("UTF-8").unwrap(); 89 | let handle = unsafe { xls_open(file_string.as_ptr(), enc_string.as_ptr()) }; 90 | if !is_null(handle as *const c_void) { 91 | Some(Workbook{ pointer: handle }) 92 | } else { 93 | None 94 | } 95 | } 96 | 97 | impl Drop for Workbook { 98 | fn drop(&mut self) { 99 | unsafe { xls_close_WB(self.pointer) }; 100 | self.pointer = unsafe { mem::transmute(0 as usize) }; 101 | } 102 | } 103 | 104 | impl Workbook { 105 | pub fn sheets(&self) -> Worksheets { 106 | Worksheets { workbook: self, current_index: 0 } 107 | } 108 | } 109 | 110 | impl<'a> Iterator for Worksheets<'a> { 111 | type Item = Worksheet; 112 | 113 | fn next(&mut self) -> Option { 114 | let ws = unsafe { xls_getWorkSheet(self.workbook.pointer, self.current_index as c_int) }; 115 | if is_null(ws as *const c_void) { 116 | None 117 | } else { 118 | self.current_index += 1; 119 | unsafe { xls_parseWorkSheet(ws) }; 120 | Some(Worksheet{ pointer: ws }) 121 | } 122 | } 123 | } 124 | 125 | impl Worksheet { 126 | pub fn rows(&self) -> Rows { 127 | Rows { worksheet: self, current_row: 0 } 128 | } 129 | } 130 | 131 | 132 | impl<'a> Iterator for Rows<'a> { 133 | type Item = Row<'a>; 134 | 135 | fn next(&mut self) -> Option> { 136 | let row = unsafe { xls_row(self.worksheet.pointer, self.current_row as u16) }; 137 | if is_null(row) { 138 | None 139 | } else { 140 | let row_struct = Row{ worksheet: self.worksheet, pointer: row, index: self.current_row }; 141 | self.current_row += 1; 142 | Some(row_struct) 143 | } 144 | } 145 | } 146 | 147 | impl<'a> Row<'a> { 148 | pub fn cells(&self) -> Cells { 149 | Cells { row: self, current_col: 0 } 150 | } 151 | } 152 | 153 | impl<'a> Iterator for Cells<'a> { 154 | type Item = Cell; 155 | 156 | fn next(&mut self) -> Option { 157 | let cell = unsafe { xls_cell(self.row.worksheet.pointer, 158 | self.row.index as u16, 159 | self.current_col as u16) }; 160 | if is_null(cell as *const c_void) { 161 | None 162 | } else { 163 | let cell_struct = Cell{ 164 | pointer : cell, 165 | row_number: self.row.index, 166 | col_number: self.current_col }; 167 | self.current_col += 1; 168 | Some(cell_struct) 169 | } 170 | } 171 | } 172 | 173 | impl Cell { 174 | pub fn value(&self) -> Option { 175 | let id = unsafe { (*self.pointer).id }; 176 | let str_val = unsafe { (*self.pointer).str }; 177 | let d_val = unsafe { (*self.pointer).d }; 178 | match id { 179 | id if (id == CellType::ExcelRecordNumber as u16 || 180 | id == CellType::ExcelRecordRK as u16 || 181 | id == CellType::ExcelRecordMulRK as u16 || 182 | id == CellType::ExcelRecordBoolErr as u16 || 183 | id == CellType::ExcelRecordFormula as u16) => Some(CellValue::DoubleValue(d_val)), 184 | _ => { 185 | if is_null(str_val as *const c_void) { 186 | None 187 | } else { 188 | let c_string = unsafe { CStr::from_ptr(str_val) }; 189 | let string_value = str::from_utf8(c_string.to_bytes()).unwrap().to_string(); 190 | Some(CellValue::StringValue{ value: string_value }) 191 | } 192 | }, 193 | } 194 | } 195 | } 196 | 197 | impl Display for CellValue { 198 | fn fmt(&self, formatter: &mut Formatter) -> Result { 199 | match self { 200 | &CellValue::DoubleValue(val) => val.fmt(formatter), 201 | &CellValue::StringValue{ value: ref val } => val.fmt(formatter), 202 | } 203 | } 204 | } 205 | --------------------------------------------------------------------------------