├── .github └── workflows │ └── input-linux.yml ├── .gitignore ├── COPYING ├── Cargo.toml ├── README.md ├── ci.nix ├── shell.nix ├── src ├── de.rs ├── error.rs ├── lib.rs ├── parse.rs ├── ser.rs └── write.rs └── tests ├── enum_seq.rs └── smoke.rs /.github/workflows/input-linux.yml: -------------------------------------------------------------------------------- 1 | env: 2 | CI_ALLOW_ROOT: '1' 3 | CI_CONFIG: ./ci.nix 4 | CI_PLATFORM: gh-actions 5 | jobs: 6 | ci: 7 | name: input-linux 8 | runs-on: ubuntu-latest 9 | steps: 10 | - id: checkout 11 | name: git clone 12 | uses: actions/checkout@v1 13 | with: 14 | submodules: true 15 | - id: nix-install 16 | name: nix install 17 | uses: arcnmx/ci/actions/nix/install@master 18 | - id: ci-setup 19 | name: nix setup 20 | uses: arcnmx/ci/actions/nix/run@master 21 | with: 22 | attrs: ci.run.setup 23 | quiet: false 24 | - id: ci-dirty 25 | name: nix test dirty 26 | uses: arcnmx/ci/actions/nix/run@master 27 | with: 28 | attrs: ci.run.test 29 | command: ci-build-dirty 30 | quiet: false 31 | stdout: ${{ runner.temp }}/ci.build.dirty 32 | - id: ci-test 33 | name: nix test build 34 | uses: arcnmx/ci/actions/nix/run@master 35 | with: 36 | attrs: ci.run.test 37 | command: ci-build-realise 38 | ignore-exit-code: true 39 | quiet: false 40 | stdin: ${{ runner.temp }}/ci.build.dirty 41 | - env: 42 | CI_EXIT_CODE: ${{ steps.ci-test.outputs.exit-code }} 43 | id: ci-summary 44 | name: nix test results 45 | uses: arcnmx/ci/actions/nix/run@master 46 | with: 47 | attrs: ci.run.test 48 | command: ci-build-summarise 49 | quiet: false 50 | stdin: ${{ runner.temp }}/ci.build.dirty 51 | stdout: ${{ runner.temp }}/ci.build.cache 52 | - env: 53 | CACHIX_SIGNING_KEY: ${{ secrets.CACHIX_SIGNING_KEY }} 54 | id: ci-cache 55 | if: always() 56 | name: nix test cache 57 | uses: arcnmx/ci/actions/nix/run@master 58 | with: 59 | attrs: ci.run.test 60 | command: ci-build-cache 61 | quiet: false 62 | stdin: ${{ runner.temp }}/ci.build.cache 63 | ci-check: 64 | name: input-linux check 65 | runs-on: ubuntu-latest 66 | steps: 67 | - id: checkout 68 | name: git clone 69 | uses: actions/checkout@v1 70 | with: 71 | submodules: true 72 | - id: nix-install 73 | name: nix install 74 | uses: arcnmx/ci/actions/nix/install@master 75 | - id: ci-action-build 76 | name: nix build ci.gh-actions.configFile 77 | uses: arcnmx/ci/actions/nix/build@master 78 | with: 79 | attrs: ci.gh-actions.configFile 80 | out-link: .ci/workflow.yml 81 | - id: ci-action-compare 82 | name: gh-actions compare 83 | uses: arcnmx/ci/actions/nix/run@master 84 | with: 85 | args: -u .github/workflows/input-linux.yml .ci/workflow.yml 86 | attrs: nixpkgs.diffutils 87 | command: diff 88 | name: input-linux 89 | 'on': 90 | - push 91 | - pull_request 92 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Cargo.lock 2 | /target/ 3 | /.cargo/ 4 | /.gitattributes 5 | *.swp 6 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 arcnmx 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serde_ini" 3 | version = "0.2.0" 4 | authors = ["arcnmx"] 5 | 6 | description = "Windows INI file {de,}serialization" 7 | keywords = ["ini", "cfg", "serde"] 8 | 9 | documentation = "http://arcnmx.github.io/serde-ini/serde_ini" 10 | repository = "https://github.com/arcnmx/serde-ini" 11 | readme = "README.md" 12 | license = "MIT" 13 | 14 | [dependencies] 15 | serde = "^1.0.0" 16 | result = "^1.0.0" 17 | void = "^1.0.2" 18 | 19 | [dev-dependencies] 20 | serde_derive = "^1.0.0" 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serde-ini 2 | 3 | [![release-badge][]][cargo] [![docs-badge][]][docs] [![license-badge][]][license] 4 | 5 | `serde_ini` provides a serde `Serializer` and `Deserializer` for the [INI format](https://en.wikipedia.org/wiki/INI_file). 6 | The format is rather limited, only allowing top level keys to be maps or structs 7 | and all values and keys must be in the form of a `String`. This implementation 8 | will try to use `ToString` and `FromStr` where appropriate for numeric values. 9 | Sequences, tuples, bytes, bools, and some other data types are not supported. 10 | 11 | ## [Documentation][docs] 12 | 13 | See the [documentation][docs] for up to date API documentation. 14 | 15 | [release-badge]: https://img.shields.io/crates/v/serde_ini.svg?style=flat-square 16 | [cargo]: https://crates.io/crates/serde_ini 17 | [docs-badge]: https://img.shields.io/badge/API-docs-blue.svg?style=flat-square 18 | [docs]: https://docs.rs/serde_ini/ 19 | [license-badge]: https://img.shields.io/badge/license-MIT-ff69b4.svg?style=flat-square 20 | [license]: https://github.com/arcnmx/serde-ini/blob/master/COPYING 21 | -------------------------------------------------------------------------------- /ci.nix: -------------------------------------------------------------------------------- 1 | { config, channels, pkgs, lib, ... }: with pkgs; with lib; let 2 | impureCommand = name: command: ci.command { 3 | inherit name command; 4 | impure = true; 5 | }; 6 | in { 7 | config = { 8 | name = "input-linux"; 9 | ci.gh-actions.enable = true; 10 | cache.cachix.arc.enable = true; 11 | channels = { 12 | rust = "master"; 13 | }; 14 | environment = { 15 | test = { 16 | inherit (config.rustChannel.buildChannel) cargo; 17 | }; 18 | }; 19 | tasks = { 20 | build.inputs = [ 21 | (impureCommand "cargo-build" "cargo build") 22 | (impureCommand "cargo-test" "cargo test") 23 | ]; 24 | }; 25 | }; 26 | 27 | options = { 28 | rustChannel = mkOption { 29 | type = types.unspecified; 30 | default = channels.rust.stable; 31 | }; 32 | shell = mkOption { 33 | type = types.unspecified; 34 | default = config.rustChannel.mkShell { }; 35 | }; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { }, ci ? import { inherit pkgs; } }: ci.config.shell 2 | -------------------------------------------------------------------------------- /src/de.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display}; 2 | use std::str::FromStr; 3 | use std::mem::replace; 4 | use std::{error, io, num, result, str}; 5 | use serde::de::{self, Error as _, Deserialize, DeserializeOwned, DeserializeSeed, EnumAccess, Visitor, MapAccess, SeqAccess, VariantAccess, IntoDeserializer}; 6 | use parse::{self, Item}; 7 | 8 | pub trait Trait { 9 | fn next(&mut self) -> Option>; 10 | } 11 | 12 | impl>> Trait for T where Error: From { 13 | fn next(&mut self) -> Option> { 14 | Iterator::next(self).map(|v| v.map_err(Into::into)) 15 | } 16 | } 17 | 18 | #[derive(Debug, Clone)] 19 | pub enum Error { 20 | /// Deserialization error 21 | /// 22 | /// Passed through error message from the type being deserialized. 23 | Custom(String), 24 | 25 | /// Internal consistency error 26 | /// 27 | /// Encountering this is probably misuse of the deserialization API or a bug in serde-ini. 28 | UnexpectedEof, 29 | 30 | /// Internal consistency error 31 | /// 32 | /// Encountering this is probably misuse of the deserialization API or a bug in serde-ini. 33 | InvalidState, 34 | } 35 | 36 | impl From for Error { 37 | fn from(e: num::ParseIntError) -> Self { 38 | Error::Custom(e.to_string()) 39 | } 40 | } 41 | 42 | impl From for Error { 43 | fn from(e: num::ParseFloatError) -> Self { 44 | Error::Custom(e.to_string()) 45 | } 46 | } 47 | 48 | impl From> for Error { 49 | fn from(e: parse::Error) -> Self { 50 | Error::Custom(e.to_string()) 51 | } 52 | } 53 | 54 | impl Display for Error { 55 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 56 | match self { 57 | Error::Custom(msg) => write!(f, "{}", msg), 58 | Error::UnexpectedEof => write!(f, "internal consistency error: unexpected EOF"), 59 | Error::InvalidState => write!(f, "internal consistency error"), 60 | } 61 | } 62 | } 63 | 64 | impl error::Error for Error { 65 | fn description(&self) -> &str { 66 | "INI deserialization error" 67 | } 68 | } 69 | 70 | impl de::Error for Error { 71 | fn custom(msg: T) -> Self { 72 | Error::Custom(msg.to_string()) 73 | } 74 | } 75 | 76 | pub type Result = result::Result; 77 | 78 | enum PeekKind { 79 | Value, 80 | Section, 81 | } 82 | 83 | #[derive(Debug, Copy, Clone)] 84 | enum Next { 85 | Init, 86 | Eof, 87 | Some(T), 88 | } 89 | 90 | #[derive(Debug)] 91 | pub struct Deserializer { 92 | input: T, 93 | next: Next>, 94 | } 95 | 96 | impl Deserializer { 97 | pub fn new(input: T) -> Self { 98 | Deserializer { 99 | input: input, 100 | next: Next::Init, 101 | } 102 | } 103 | } 104 | 105 | impl Deserializer { 106 | fn populate(&mut self) { 107 | while let Next::Init = self.next { 108 | let next = self.input.next(); 109 | self.next = match next { 110 | Some(Ok(Item::Comment { .. })) => Next::Init, 111 | Some(Ok(Item::Empty)) => Next::Init, 112 | Some(v) => Next::Some(v), 113 | None => Next::Eof, 114 | }; 115 | } 116 | } 117 | 118 | fn next_item(&mut self) -> Result { 119 | let next = match self.next { 120 | Next::Eof | Next::Some(Err(..)) => Next::Eof, 121 | _ => Next::Init, 122 | }; 123 | let next = replace(&mut self.next, next); 124 | match next { 125 | Next::Some(v) => v, 126 | Next::Eof => Err(Error::UnexpectedEof), 127 | Next::Init => unreachable!(), 128 | } 129 | } 130 | 131 | fn peek_item(&mut self) -> Result> { 132 | match &mut self.next { 133 | &mut Next::Some(Ok(ref mut v)) => Ok(Some(v)), 134 | e @ &mut Next::Some(Err(..)) => { 135 | if let Next::Some(Err(e)) = replace(e, Next::Eof) { 136 | Err(e) 137 | } else { 138 | unreachable!() 139 | } 140 | }, 141 | &mut Next::Eof => Ok(None), 142 | &mut Next::Init => unreachable!(), 143 | } 144 | } 145 | 146 | fn peek_kind(&mut self) -> Result> { 147 | self.populate(); 148 | Ok(match self.peek_item()? { 149 | Some(&mut Item::Value { .. }) => Some(PeekKind::Value), 150 | Some(&mut Item::Section { .. }) => Some(PeekKind::Section), 151 | None => None, 152 | Some(..) => unreachable!(), 153 | }) 154 | } 155 | 156 | fn peek_section(&mut self) -> Result<&str> { 157 | self.populate(); 158 | match self.peek_item()? { 159 | Some(&mut Item::Section { ref name }) => Ok(name), 160 | Some(..) => Err(Error::InvalidState), 161 | None => Err(Error::UnexpectedEof), 162 | } 163 | } 164 | 165 | fn peek_key(&mut self) -> Result<&str> { 166 | self.populate(); 167 | match self.peek_item()? { 168 | Some(&mut Item::Value { ref key, .. }) => Ok(key), 169 | Some(..) => Err(Error::InvalidState), 170 | None => Err(Error::UnexpectedEof), 171 | } 172 | } 173 | 174 | fn next_value(&mut self) -> Result { 175 | self.populate(); 176 | match self.next_item()? { 177 | Item::Value { value, .. } => Ok(value), 178 | _ => Err(Error::InvalidState), 179 | } 180 | } 181 | 182 | fn next_section(&mut self) -> Result { 183 | self.populate(); 184 | match self.next_item()? { 185 | Item::Section { name } => Ok(name), 186 | _ => Err(Error::InvalidState), 187 | } 188 | } 189 | 190 | fn assert_eof(&mut self) -> Result<()> { 191 | self.populate(); 192 | match self.peek_item()? { 193 | Some(..) => Err(Error::InvalidState), 194 | None => Ok(()), 195 | } 196 | } 197 | } 198 | 199 | impl<'de, 'a, T: Trait> de::Deserializer<'de> for &'a mut Deserializer { 200 | type Error = Error; 201 | 202 | fn deserialize_any>(self, visitor: V) -> Result { 203 | visitor.visit_map(MapAccessTop(self)) 204 | } 205 | 206 | fn deserialize_seq>(self, visitor: V) -> Result { 207 | visitor.visit_seq(SeqAccessTop(self)) 208 | } 209 | 210 | fn deserialize_option>(self, visitor: V) -> Result { 211 | visitor.visit_some(self) 212 | } 213 | 214 | forward_to_deserialize_any! { 215 | bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes 216 | byte_buf unit unit_struct newtype_struct tuple tuple_struct 217 | map struct identifier ignored_any enum 218 | } 219 | } 220 | 221 | impl Deserializer>> { 222 | /// Creates an INI deserializer from an `io::BufRead`. 223 | pub fn from_bufread(reader: R) -> Self { 224 | Deserializer::new(parse::Parser::from_bufread(reader)) 225 | } 226 | } 227 | 228 | impl Deserializer>>> { 229 | /// Creates an INI deserializer from a reader. 230 | pub fn from_read(reader: R) -> Self { 231 | Deserializer::new(parse::Parser::from_read(reader)) 232 | } 233 | } 234 | 235 | impl<'a> Deserializer>>> { 236 | /// Creates an INI deserializer from a `&str`. 237 | pub fn from_str(s: &'a str) -> Self { 238 | Deserializer::new(parse::Parser::from_str(s)) 239 | } 240 | } 241 | 242 | pub struct SectionDeserializer<'a, T: 'a>(&'a mut Deserializer); 243 | 244 | impl<'de, 'a, T: Trait> de::Deserializer<'de> for &'a mut SectionDeserializer<'a, T> { 245 | type Error = Error; 246 | 247 | fn deserialize_any>(self, visitor: V) -> Result { 248 | struct MapAccessSectionBody<'a, T: Trait + 'a>(&'a mut Deserializer); 249 | 250 | impl<'de, 'a, T: Trait + 'a> MapAccess<'de> for MapAccessSectionBody<'a, T> { 251 | type Error = Error; 252 | 253 | fn next_key_seed>( 254 | &mut self, 255 | seed: K, 256 | ) -> Result> { 257 | match (self.0).peek_kind()? { 258 | Some(PeekKind::Value) => seed 259 | .deserialize((self.0).peek_key()?.into_deserializer()) 260 | .map(Some), 261 | None | Some(PeekKind::Section) => Ok(None), 262 | } 263 | } 264 | 265 | fn next_value_seed>(&mut self, seed: V) -> Result { 266 | seed.deserialize(&mut ValueDeserializer(self.0)) 267 | } 268 | } 269 | 270 | (self.0).next_section()?; 271 | visitor.visit_map(MapAccessSectionBody(self.0)) 272 | } 273 | 274 | fn deserialize_enum>( 275 | self, 276 | _name: &'static str, 277 | _variants: &'static [&'static str], 278 | visitor: V, 279 | ) -> Result { 280 | struct EnumAccessSection<'a, T: Trait + 'a>(&'a mut Deserializer); 281 | 282 | impl<'de, 'a, T: Trait + 'a> EnumAccess<'de> for EnumAccessSection<'a, T> { 283 | type Error = Error; 284 | type Variant = VariantAccessSection<'a, T>; 285 | 286 | fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant)> 287 | where 288 | V: DeserializeSeed<'de>, 289 | { 290 | struct SectionNameDeserializer<'a, T: 'a>(&'a mut Deserializer); 291 | 292 | impl<'de, 'a, T: Trait> de::Deserializer<'de> for &'a mut SectionNameDeserializer<'a, T> { 293 | type Error = Error; 294 | 295 | fn deserialize_any>(self, _visitor: V) -> Result { 296 | Err(Error::InvalidState) 297 | } 298 | 299 | fn deserialize_identifier(self, visitor: V) -> Result 300 | where 301 | V: Visitor<'de>, 302 | { 303 | let name = self.0.peek_section()?; 304 | visitor.visit_str(name) 305 | } 306 | 307 | forward_to_deserialize_any! { 308 | bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char bytes 309 | byte_buf str string unit unit_struct newtype_struct seq tuple tuple_struct 310 | map struct ignored_any enum option 311 | } 312 | } 313 | 314 | let variant = seed.deserialize(&mut SectionNameDeserializer(self.0))?; 315 | Ok((variant, VariantAccessSection(self.0))) 316 | } 317 | } 318 | 319 | struct VariantAccessSection<'a, T: Trait + 'a>(&'a mut Deserializer); 320 | 321 | impl<'de, 'a, T: Trait + 'a> VariantAccess<'de> for VariantAccessSection<'a, T> { 322 | type Error = Error; 323 | 324 | fn unit_variant(self) -> Result<()> { 325 | Err(Error::custom("unit variant is not supported")) 326 | } 327 | 328 | fn newtype_variant_seed(self, seed: E) -> Result 329 | where 330 | E: DeserializeSeed<'de>, 331 | { 332 | seed.deserialize(&mut SectionDeserializer(self.0)) 333 | } 334 | 335 | fn tuple_variant(self, _len: usize, _visitor: V) -> Result 336 | where 337 | V: Visitor<'de>, 338 | { 339 | Err(Error::custom("tuple variant is not supported")) 340 | } 341 | 342 | fn struct_variant( 343 | self, 344 | _fields: &'static [&'static str], 345 | visitor: V, 346 | ) -> Result 347 | where 348 | V: Visitor<'de>, 349 | { 350 | use serde::Deserializer; 351 | SectionDeserializer(self.0).deserialize_any(visitor) 352 | } 353 | } 354 | 355 | visitor.visit_enum(EnumAccessSection(self.0)) 356 | } 357 | 358 | fn deserialize_option>(self, visitor: V) -> Result { 359 | visitor.visit_some(self) 360 | } 361 | 362 | forward_to_deserialize_any! { 363 | bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes 364 | byte_buf unit unit_struct newtype_struct seq tuple tuple_struct 365 | map struct identifier ignored_any 366 | } 367 | } 368 | 369 | pub struct ValueDeserializer<'a, T: 'a>(&'a mut Deserializer); 370 | 371 | impl<'de, 'a, T: Trait> de::Deserializer<'de> for &'a mut ValueDeserializer<'a, T> { 372 | type Error = Error; 373 | 374 | fn deserialize_any>(self, visitor: V) -> Result { 375 | match (self.0).peek_kind()? { 376 | Some(PeekKind::Value) => self.deserialize_str(visitor), 377 | None | Some(PeekKind::Section) => Err(Error::InvalidState), 378 | } 379 | } 380 | 381 | fn deserialize_bool>(self, visitor: V) -> Result { 382 | self.deserialize_any(visitor) 383 | } 384 | 385 | fn deserialize_i8>(self, visitor: V) -> Result { 386 | visitor.visit_i8(FromStr::from_str(&(self.0).next_value()?)?) 387 | } 388 | 389 | fn deserialize_i16>(self, visitor: V) -> Result { 390 | visitor.visit_i16(FromStr::from_str(&(self.0).next_value()?)?) 391 | } 392 | 393 | fn deserialize_i32>(self, visitor: V) -> Result { 394 | visitor.visit_i32(FromStr::from_str(&(self.0).next_value()?)?) 395 | } 396 | 397 | fn deserialize_i64>(self, visitor: V) -> Result { 398 | visitor.visit_i64(FromStr::from_str(&(self.0).next_value()?)?) 399 | } 400 | 401 | fn deserialize_u8>(self, visitor: V) -> Result { 402 | visitor.visit_u8(FromStr::from_str(&(self.0).next_value()?)?) 403 | } 404 | 405 | fn deserialize_u16>(self, visitor: V) -> Result { 406 | visitor.visit_u16(FromStr::from_str(&(self.0).next_value()?)?) 407 | } 408 | 409 | fn deserialize_u32>(self, visitor: V) -> Result { 410 | visitor.visit_u32(FromStr::from_str(&(self.0).next_value()?)?) 411 | } 412 | 413 | fn deserialize_u64>(self, visitor: V) -> Result { 414 | visitor.visit_u64(FromStr::from_str(&(self.0).next_value()?)?) 415 | } 416 | 417 | fn deserialize_f32>(self, visitor: V) -> Result { 418 | visitor.visit_f32(FromStr::from_str(&(self.0).next_value()?)?) 419 | } 420 | 421 | fn deserialize_f64>(self, visitor: V) -> Result { 422 | visitor.visit_f64(FromStr::from_str(&(self.0).next_value()?)?) 423 | } 424 | 425 | fn deserialize_char>(self, visitor: V) -> Result { 426 | let value = (self.0).next_value()?; 427 | let mut chars = value.chars(); 428 | if let Some(c) = chars.next() { 429 | if chars.next().is_some() { 430 | // >1 char long 431 | visitor.visit_str(&value) 432 | } else { 433 | visitor.visit_char(c) 434 | } 435 | } else { 436 | // 0 chars long 437 | visitor.visit_str(&value) 438 | } 439 | } 440 | 441 | fn deserialize_str>(self, visitor: V) -> Result { 442 | visitor.visit_str(&(self.0).next_value()?) 443 | } 444 | 445 | fn deserialize_string>(self, visitor: V) -> Result { 446 | visitor.visit_string((self.0).next_value()?) 447 | } 448 | 449 | fn deserialize_bytes>(self, visitor: V) -> Result { 450 | self.deserialize_any(visitor) 451 | } 452 | 453 | fn deserialize_byte_buf>(self, visitor: V) -> Result { 454 | self.deserialize_any(visitor) 455 | } 456 | 457 | fn deserialize_option>(self, visitor: V) -> Result { 458 | visitor.visit_some(self) 459 | } 460 | 461 | fn deserialize_unit>(self, visitor: V) -> Result { 462 | self.deserialize_any(visitor) 463 | } 464 | 465 | // Unit struct means a named value containing no data. 466 | fn deserialize_unit_struct>(self, _name: &'static str, visitor: V) -> Result { 467 | self.deserialize_unit(visitor) 468 | } 469 | 470 | fn deserialize_newtype_struct>(self, _name: &'static str, visitor: V) -> Result { 471 | visitor.visit_newtype_struct(self) 472 | } 473 | 474 | fn deserialize_seq>(self, visitor: V) -> Result { 475 | self.deserialize_any(visitor) 476 | } 477 | 478 | fn deserialize_tuple>(self, _len: usize, visitor: V) -> Result { 479 | self.deserialize_any(visitor) 480 | } 481 | 482 | fn deserialize_tuple_struct>(self, _name: &'static str, _len: usize, visitor: V) -> Result { 483 | self.deserialize_any(visitor) 484 | } 485 | 486 | fn deserialize_map>(self, visitor: V) -> Result { 487 | self.deserialize_any(visitor) 488 | } 489 | 490 | fn deserialize_struct>(self, _name: &'static str, _fields: &'static [&'static str], visitor: V) -> Result { 491 | self.deserialize_map(visitor) 492 | } 493 | 494 | fn deserialize_enum>(self, _name: &'static str, _variants: &'static [&'static str], visitor: V) -> Result { 495 | match (self.0).peek_kind()? { 496 | Some(PeekKind::Value) => visitor.visit_enum((self.0).next_value()?.into_deserializer()), 497 | None | Some(PeekKind::Section) => Err(Error::InvalidState), 498 | } 499 | } 500 | 501 | fn deserialize_identifier>(self, visitor: V) -> Result { 502 | self.deserialize_str(visitor) 503 | } 504 | 505 | fn deserialize_ignored_any>(self, visitor: V) -> Result { 506 | self.deserialize_any(visitor) 507 | } 508 | } 509 | 510 | struct SeqAccessTop<'a, T: Trait + 'a>(&'a mut Deserializer); 511 | 512 | impl<'de, 'a, T: Trait + 'a> SeqAccess<'de> for SeqAccessTop<'a, T> { 513 | type Error = Error; 514 | 515 | fn next_element_seed(&mut self, seed: E) -> Result> 516 | where 517 | E: DeserializeSeed<'de>, 518 | { 519 | pub struct KeyValueDeserializer<'a, T: 'a>(&'a mut Deserializer); 520 | 521 | impl<'de, 'a, T: Trait> de::Deserializer<'de> for &'a mut KeyValueDeserializer<'a, T> { 522 | type Error = Error; 523 | 524 | fn deserialize_any>(self, _visitor: V) -> Result { 525 | Err(Error::custom("expect an enum type")) 526 | } 527 | 528 | fn deserialize_enum>( 529 | self, 530 | _name: &'static str, 531 | _variants: &'static [&'static str], 532 | visitor: V, 533 | ) -> Result { 534 | struct EnumAccessKeyValue<'a, T: Trait + 'a>(&'a mut Deserializer); 535 | 536 | impl<'de, 'a, T: Trait + 'a> EnumAccess<'de> for EnumAccessKeyValue<'a, T> { 537 | type Error = Error; 538 | type Variant = VariantAccessKeyValue<'a, T>; 539 | 540 | fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant)> 541 | where 542 | V: DeserializeSeed<'de>, 543 | { 544 | struct KeyDeserializer<'a, T: 'a>(&'a mut Deserializer); 545 | 546 | impl<'de, 'a, T: Trait> de::Deserializer<'de> for &'a mut KeyDeserializer<'a, T> { 547 | type Error = Error; 548 | 549 | fn deserialize_any>( 550 | self, 551 | _visitor: V, 552 | ) -> Result { 553 | Err(Error::InvalidState) 554 | } 555 | 556 | fn deserialize_identifier(self, visitor: V) -> Result 557 | where 558 | V: Visitor<'de>, 559 | { 560 | let name = self.0.peek_key()?; 561 | visitor.visit_str(name) 562 | } 563 | 564 | forward_to_deserialize_any! { 565 | bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char bytes 566 | byte_buf str string unit unit_struct newtype_struct seq tuple tuple_struct 567 | map struct ignored_any enum option 568 | } 569 | } 570 | 571 | let variant = seed.deserialize(&mut KeyDeserializer(self.0))?; 572 | Ok((variant, VariantAccessKeyValue(self.0))) 573 | } 574 | } 575 | 576 | struct VariantAccessKeyValue<'a, T: Trait + 'a>(&'a mut Deserializer); 577 | 578 | impl<'de, 'a, T: Trait + 'a> VariantAccess<'de> for VariantAccessKeyValue<'a, T> { 579 | type Error = Error; 580 | 581 | fn unit_variant(self) -> Result<()> { 582 | Err(Error::custom("unit variant is not supported")) 583 | } 584 | 585 | fn newtype_variant_seed(self, seed: E) -> Result 586 | where 587 | E: DeserializeSeed<'de>, 588 | { 589 | seed.deserialize(&mut ValueDeserializer(self.0)) 590 | } 591 | 592 | fn tuple_variant(self, _len: usize, _visitor: V) -> Result 593 | where 594 | V: Visitor<'de>, 595 | { 596 | Err(Error::custom("tuple variant is not supported")) 597 | } 598 | 599 | fn struct_variant( 600 | self, 601 | _fields: &'static [&'static str], 602 | _visitor: V, 603 | ) -> Result 604 | where 605 | V: Visitor<'de>, 606 | { 607 | Err(Error::custom("struct variant is not supported")) 608 | } 609 | } 610 | 611 | visitor.visit_enum(EnumAccessKeyValue(self.0)) 612 | } 613 | 614 | forward_to_deserialize_any! { 615 | bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes 616 | byte_buf unit unit_struct newtype_struct seq tuple tuple_struct 617 | map struct identifier ignored_any option 618 | } 619 | } 620 | 621 | match (self.0).peek_kind()? { 622 | Some(PeekKind::Value) => seed.deserialize(&mut KeyValueDeserializer(self.0)), 623 | Some(PeekKind::Section) => seed.deserialize(&mut SectionDeserializer(self.0)), 624 | None => return Ok(None), 625 | } 626 | .map(Some) 627 | } 628 | } 629 | 630 | struct MapAccessTop<'a, T: Trait + 'a>(&'a mut Deserializer); 631 | 632 | impl<'de, 'a, T: Trait + 'a> MapAccess<'de> for MapAccessTop<'a, T> { 633 | type Error = Error; 634 | 635 | fn next_key_seed>(&mut self, seed: K) -> Result> { 636 | match (self.0).peek_kind()? { 637 | Some(PeekKind::Value) => seed.deserialize((self.0).peek_key()?.into_deserializer()), 638 | Some(PeekKind::Section) => { 639 | seed.deserialize((self.0).peek_section()?.into_deserializer()) 640 | } 641 | None => return Ok(None), 642 | } 643 | .map(Some) 644 | } 645 | 646 | fn next_value_seed>(&mut self, seed: V) -> Result { 647 | match (self.0).peek_kind()? { 648 | Some(PeekKind::Value) => seed.deserialize(&mut ValueDeserializer(self.0)), 649 | Some(PeekKind::Section) => seed.deserialize(&mut SectionDeserializer(self.0)), 650 | None => Err(Error::UnexpectedEof), 651 | } 652 | } 653 | } 654 | 655 | /// Deserialize an instance of type `T` from a string of INI text. 656 | pub fn from_str(s: &str) -> Result { 657 | let mut de = Deserializer::new(parse::Parser::from_str(s.as_ref())); 658 | let value = Deserialize::deserialize(&mut de)?; 659 | 660 | de.assert_eof()?; 661 | Ok(value) 662 | } 663 | 664 | /// Deserialize an instance of type `T` from a buffered IO stream of INI. 665 | pub fn from_bufread(reader: R) -> Result { 666 | let mut de = Deserializer::new(parse::Parser::from_bufread(reader)); 667 | let value = Deserialize::deserialize(&mut de)?; 668 | 669 | de.assert_eof()?; 670 | Ok(value) 671 | } 672 | 673 | /// Deserialize an instance of type `T` from a stream of INI data. 674 | pub fn from_read(reader: R) -> Result { 675 | let mut de = Deserializer::new(parse::Parser::from_read(reader)); 676 | let value = Deserialize::deserialize(&mut de)?; 677 | 678 | de.assert_eof()?; 679 | Ok(value) 680 | } 681 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use serde; 2 | use std::fmt::{self, Display}; 3 | 4 | #[derive(Debug, Clone)] 5 | pub enum Error { 6 | Custom(String) 7 | } 8 | 9 | impl Display for Error { 10 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 11 | match self { 12 | Error::Custom(msg) => write!(f, "{}", msg), 13 | } 14 | } 15 | } 16 | 17 | impl From for Error { 18 | fn from(e: super::de::Error) -> Self { 19 | Error::Custom(e.to_string()) 20 | } 21 | } 22 | 23 | impl ::std::error::Error for Error { 24 | fn description(&self) -> &str { 25 | "INI serialization error" 26 | } 27 | } 28 | 29 | impl serde::ser::Error for Error { 30 | fn custom(msg: T) -> Self { 31 | Error::Custom(msg.to_string()) 32 | } 33 | } 34 | 35 | impl serde::de::Error for Error { 36 | fn custom(msg: T) -> Self { 37 | Error::Custom(msg.to_string()) 38 | } 39 | } 40 | 41 | pub type Result = ::std::result::Result; 42 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //#![deny(missing_docs)] 2 | #![doc(html_root_url = "http://arcnmx.github.io/serde-ini/")] 3 | 4 | //! Windows INI format serialization for serde 5 | 6 | extern crate result; 7 | extern crate void; 8 | #[macro_use] 9 | extern crate serde; 10 | 11 | pub mod de; 12 | pub mod error; 13 | pub mod parse; 14 | pub mod ser; 15 | pub mod write; 16 | 17 | pub use de::{Deserializer, from_str, from_bufread, from_read}; 18 | pub use ser::{Serializer, to_string, to_vec, to_writer}; 19 | pub use parse::{Parser, Item}; 20 | pub use write::{Writer, LineEnding}; 21 | -------------------------------------------------------------------------------- /src/parse.rs: -------------------------------------------------------------------------------- 1 | use std::{io, fmt, error, str}; 2 | use result::prelude::*; 3 | use void::Void; 4 | 5 | #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] 6 | pub enum Item { 7 | Empty, 8 | Section { 9 | name: String 10 | }, 11 | Value { 12 | key: String, 13 | value: String, 14 | }, 15 | Comment { 16 | text: String 17 | }, 18 | } 19 | 20 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] 21 | pub enum SyntaxError { 22 | SectionNotClosed, 23 | SectionName, 24 | MissingEquals, 25 | } 26 | 27 | impl fmt::Display for SyntaxError { 28 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 29 | match *self { 30 | SyntaxError::SectionNotClosed => write!(f, "section missing ']'"), 31 | SyntaxError::SectionName => write!(f, "section name contains ']'"), 32 | SyntaxError::MissingEquals => write!(f, "variable assignment missing '='"), 33 | } 34 | } 35 | } 36 | 37 | #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] 38 | pub enum Error { 39 | Inner(E), 40 | Syntax(SyntaxError), 41 | } 42 | 43 | impl From for Error { 44 | fn from(e: E) -> Self { 45 | Error::Inner(e) 46 | } 47 | } 48 | 49 | impl fmt::Display for Error { 50 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 51 | match *self { 52 | Error::Inner(ref e) => fmt::Display::fmt(e, f), 53 | Error::Syntax(s) => write!(f, "INI syntax error: {}", s), 54 | } 55 | } 56 | } 57 | 58 | impl error::Error for Error { 59 | fn description(&self) -> &str { 60 | match *self { 61 | Error::Inner(ref e) => e.description(), 62 | Error::Syntax(..) => "INI syntax error", 63 | } 64 | } 65 | 66 | fn cause(&self) -> Option<&error::Error> { 67 | match *self { 68 | Error::Inner(ref e) => Some(e), 69 | _ => None, 70 | } 71 | } 72 | } 73 | 74 | pub struct Parser { 75 | input: T, 76 | } 77 | 78 | impl Parser { 79 | pub fn new(input: T) -> Self { 80 | Parser { 81 | input: input, 82 | } 83 | } 84 | 85 | pub fn into_inner(self) -> T { 86 | self.input 87 | } 88 | } 89 | 90 | impl<'a> Parser>> { 91 | pub fn from_str(s: &'a str) -> Self { 92 | Self::new(OkIter(s.lines())) 93 | } 94 | } 95 | 96 | impl Parser> { 97 | pub fn from_bufread(r: R) -> Self { 98 | Self::new(r.lines()) 99 | } 100 | } 101 | 102 | impl Parser>> { 103 | pub fn from_read(r: R) -> Self { 104 | Self::from_bufread(io::BufReader::new(r)) 105 | } 106 | } 107 | 108 | impl Parser { 109 | fn parse_next>(line: Option) -> Result, Error> { 110 | let line = match line { 111 | Some(line) => line, 112 | None => return Ok(None), 113 | }; 114 | let line = line.as_ref(); 115 | 116 | if line.starts_with('[') { 117 | if line.ends_with(']') { 118 | let line = &line[1..line.len() - 1]; 119 | if line.contains(']') { 120 | Err(Error::Syntax(SyntaxError::SectionName)) 121 | } else { 122 | Ok(Some(Item::Section { 123 | name: line.into(), 124 | })) 125 | } 126 | } else { 127 | Err(Error::Syntax(SyntaxError::SectionNotClosed)) 128 | } 129 | } else if line.starts_with(';') || line.starts_with('#') { 130 | Ok(Some(Item::Comment { 131 | text: line.into(), 132 | })) 133 | } else { 134 | let mut line = line.splitn(2, '='); 135 | if let Some(key) = line.next() { 136 | if let Some(value) = line.next() { 137 | Ok(Some(Item::Value { 138 | key: key.trim().into(), 139 | value: value.trim().into(), 140 | })) 141 | } else if key.is_empty() { 142 | Ok(Some(Item::Empty)) 143 | } else { 144 | Err(Error::Syntax(SyntaxError::MissingEquals)) 145 | } 146 | } else { 147 | unreachable!() 148 | } 149 | } 150 | } 151 | } 152 | 153 | impl, T: Iterator>> Iterator for Parser { 154 | type Item = Result>; 155 | 156 | fn next(&mut self) -> Option { 157 | self.input.next_invert().map_err(Error::Inner).and_then(|l| Self::parse_next(l)).invert() 158 | } 159 | } 160 | 161 | pub struct OkIter(pub I); 162 | 163 | impl> Iterator for OkIter { 164 | type Item = Result; 165 | 166 | fn next(&mut self) -> Option { 167 | (self.0).next().map(Ok) 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/ser.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Write}; 2 | use std::{result, fmt}; 3 | use serde::ser::{self, Serialize, Impossible}; 4 | use write::Writer; 5 | use parse::Item; 6 | 7 | #[derive(Copy, Clone, Debug)] 8 | pub enum UnsupportedType { 9 | Bool, 10 | Bytes, 11 | None, 12 | Unit, 13 | Seq, 14 | Map, 15 | } 16 | 17 | #[derive(Debug, Clone)] 18 | pub enum Error { 19 | /// Serialization error 20 | /// 21 | /// Passed through error message from the type being serialized. 22 | Custom(String), 23 | 24 | /// Attempted to serialize a type not supported by the INI format 25 | /// 26 | /// INI values can only be strings, or numeric values supported by `FromStr`. 27 | UnsupportedType(UnsupportedType), 28 | 29 | /// INI section and key names must be a string 30 | NonStringKey, 31 | 32 | /// An entire INI file can only be serialized from a map or struct type 33 | TopLevelMap, 34 | 35 | /// Top-level values without a section cannot be serialized after a section has been written 36 | OrphanValue, 37 | 38 | /// Serializer consistency error 39 | /// 40 | /// This error indicates that the `SerializeMap` API was misused. 41 | MapKeyMissing, 42 | } 43 | 44 | impl From for Error { 45 | fn from(e: io::Error) -> Self { 46 | Error::Custom(e.to_string()) 47 | } 48 | } 49 | 50 | impl From for Error { 51 | fn from(t: UnsupportedType) -> Self { 52 | Error::UnsupportedType(t) 53 | } 54 | } 55 | 56 | impl fmt::Display for Error { 57 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 58 | match self { 59 | Error::Custom(msg) => write!(f, "{}", msg), 60 | Error::UnsupportedType(ty) => write!(f, "{:?} cannot be serialized into INI", ty), 61 | Error::NonStringKey => write!(f, "INI map keys must be a string type"), 62 | Error::OrphanValue => write!(f, "top-level INI values must be serialized before any map sections"), 63 | Error::MapKeyMissing => write!(f, "serializer consistency error: attempted to serialize map value without key"), 64 | Error::TopLevelMap => write!(f, "INI can only represent a map or struct type"), 65 | } 66 | } 67 | } 68 | 69 | impl ::std::error::Error for Error { 70 | fn description(&self) -> &str { 71 | "INI serialization error" 72 | } 73 | } 74 | 75 | impl ser::Error for Error { 76 | fn custom(msg: T) -> Self { 77 | Error::Custom(msg.to_string()) 78 | } 79 | } 80 | 81 | pub type Result = result::Result; 82 | 83 | pub struct Serializer { 84 | writer: Writer, 85 | } 86 | 87 | impl Serializer { 88 | pub fn new(writer: Writer) -> Self { 89 | Serializer { 90 | writer: writer, 91 | } 92 | } 93 | } 94 | 95 | struct ValueSerializer<'a, 'k, W: 'a> { 96 | writer: &'a mut Writer, 97 | key: &'k str, 98 | top_level: bool, 99 | allow_values: &'a mut bool, 100 | } 101 | 102 | pub struct MapSerializer<'a, W: 'a> { 103 | writer: &'a mut Writer, 104 | key: Option, 105 | top_level: bool, 106 | allow_values: bool, 107 | } 108 | 109 | impl<'a, 'k, W: Write> ValueSerializer<'a, 'k, W> { 110 | fn serialize_string(&mut self, s: String) -> Result<()> { 111 | if !self.top_level || *self.allow_values { 112 | self.writer.write(&Item::Value { 113 | key: self.key.into(), 114 | value: s, 115 | }).map_err(Into::into) 116 | } else { 117 | Err(Error::OrphanValue) 118 | } 119 | } 120 | 121 | fn serialize_section(&mut self) -> Result<()> { 122 | self.writer.write(&Item::Section { 123 | name: self.key.into(), 124 | }).map_err(Into::into) 125 | } 126 | } 127 | 128 | impl<'a, 'k, W: Write + 'a> ser::Serializer for ValueSerializer<'a, 'k, W> { 129 | type Ok = (); 130 | type Error = Error; 131 | 132 | type SerializeSeq = Impossible; 133 | type SerializeTuple = Impossible; 134 | type SerializeTupleStruct = Impossible; 135 | type SerializeTupleVariant = Impossible; 136 | type SerializeMap = MapSerializer<'a, W>; 137 | type SerializeStruct = MapSerializer<'a, W>; 138 | type SerializeStructVariant = Impossible; 139 | 140 | fn serialize_bool(self, _v: bool) -> Result<()> { 141 | Err(UnsupportedType::Bool.into()) 142 | } 143 | 144 | fn serialize_i8(mut self, v: i8) -> Result<()> { 145 | self.serialize_string(v.to_string()) 146 | } 147 | 148 | fn serialize_i16(mut self, v: i16) -> Result<()> { 149 | self.serialize_string(v.to_string()) 150 | } 151 | 152 | fn serialize_i32(mut self, v: i32) -> Result<()> { 153 | self.serialize_string(v.to_string()) 154 | } 155 | 156 | fn serialize_i64(mut self, v: i64) -> Result<()> { 157 | self.serialize_string(v.to_string()) 158 | } 159 | 160 | fn serialize_u8(mut self, v: u8) -> Result<()> { 161 | self.serialize_string(v.to_string()) 162 | } 163 | 164 | fn serialize_u16(mut self, v: u16) -> Result<()> { 165 | self.serialize_string(v.to_string()) 166 | } 167 | 168 | fn serialize_u32(mut self, v: u32) -> Result<()> { 169 | self.serialize_string(v.to_string()) 170 | } 171 | 172 | fn serialize_u64(mut self, v: u64) -> Result<()> { 173 | self.serialize_string(v.to_string()) 174 | } 175 | 176 | fn serialize_f32(mut self, v: f32) -> Result<()> { 177 | self.serialize_string(v.to_string()) 178 | } 179 | 180 | fn serialize_f64(mut self, v: f64) -> Result<()> { 181 | self.serialize_string(v.to_string()) 182 | } 183 | 184 | fn serialize_char(mut self, v: char) -> Result<()> { 185 | self.serialize_string(v.to_string()) 186 | } 187 | 188 | fn serialize_str(mut self, v: &str) -> Result<()> { 189 | self.serialize_string(v.into()) 190 | } 191 | 192 | fn serialize_bytes(self, _v: &[u8]) -> Result<()> { 193 | Err(UnsupportedType::Bytes.into()) 194 | } 195 | 196 | fn serialize_none(self) -> Result<()> { 197 | Err(UnsupportedType::None.into()) 198 | } 199 | 200 | fn serialize_some(self, value: &T) -> Result<()> { 201 | value.serialize(self) 202 | } 203 | 204 | fn serialize_unit(self) -> Result<()> { 205 | Err(UnsupportedType::Unit.into()) 206 | } 207 | 208 | fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { 209 | Err(UnsupportedType::Unit.into()) 210 | } 211 | 212 | fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str) -> Result<()> { 213 | self.serialize_str(variant) 214 | } 215 | 216 | fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> { 217 | value.serialize(self) 218 | } 219 | 220 | fn serialize_newtype_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T) -> Result<()> { 221 | Err(UnsupportedType::Unit.into()) 222 | } 223 | 224 | fn serialize_seq(self, _len: Option) -> Result { 225 | Err(UnsupportedType::Seq.into()) 226 | } 227 | 228 | fn serialize_tuple(self, len: usize) -> Result { 229 | self.serialize_seq(Some(len)) 230 | } 231 | 232 | fn serialize_tuple_struct(self, _name: &'static str, len: usize) -> Result { 233 | self.serialize_seq(Some(len)) 234 | } 235 | 236 | fn serialize_tuple_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize) -> Result { 237 | Err(UnsupportedType::Seq.into()) 238 | } 239 | 240 | fn serialize_map(mut self, _len: Option) -> Result { 241 | if self.top_level { 242 | *self.allow_values = false; 243 | self.serialize_section().map(move |_| MapSerializer { 244 | writer: self.writer, 245 | key: None, 246 | top_level: false, 247 | allow_values: false, 248 | }) 249 | } else { 250 | Err(UnsupportedType::Map.into()) 251 | } 252 | } 253 | 254 | fn serialize_struct(self, _name: &'static str, len: usize) -> Result { 255 | self.serialize_map(Some(len)) 256 | } 257 | 258 | fn serialize_struct_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize) -> Result { 259 | Err(UnsupportedType::Map.into()) 260 | } 261 | } 262 | 263 | #[derive(Default)] 264 | struct KeySerializer { 265 | key: String, 266 | } 267 | 268 | impl<'a> ser::Serializer for &'a mut KeySerializer { 269 | type Ok = (); 270 | type Error = Error; 271 | 272 | type SerializeSeq = Impossible; 273 | type SerializeTuple = Impossible; 274 | type SerializeTupleStruct = Impossible; 275 | type SerializeTupleVariant = Impossible; 276 | type SerializeMap = Impossible; 277 | type SerializeStruct = Impossible; 278 | type SerializeStructVariant = Impossible; 279 | 280 | fn serialize_bool(self, _v: bool) -> Result<()> { 281 | Err(Error::NonStringKey) 282 | } 283 | 284 | fn serialize_i8(self, _v: i8) -> Result<()> { 285 | Err(Error::NonStringKey) 286 | } 287 | 288 | fn serialize_i16(self, _v: i16) -> Result<()> { 289 | Err(Error::NonStringKey) 290 | } 291 | 292 | fn serialize_i32(self, _v: i32) -> Result<()> { 293 | Err(Error::NonStringKey) 294 | } 295 | 296 | fn serialize_i64(self, _v: i64) -> Result<()> { 297 | Err(Error::NonStringKey) 298 | } 299 | 300 | fn serialize_u8(self, _v: u8) -> Result<()> { 301 | Err(Error::NonStringKey) 302 | } 303 | 304 | fn serialize_u16(self, _v: u16) -> Result<()> { 305 | Err(Error::NonStringKey) 306 | } 307 | 308 | fn serialize_u32(self, _v: u32) -> Result<()> { 309 | Err(Error::NonStringKey) 310 | } 311 | 312 | fn serialize_u64(self, _v: u64) -> Result<()> { 313 | Err(Error::NonStringKey) 314 | } 315 | 316 | fn serialize_f32(self, _v: f32) -> Result<()> { 317 | Err(Error::NonStringKey) 318 | } 319 | 320 | fn serialize_f64(self, _v: f64) -> Result<()> { 321 | Err(Error::NonStringKey) 322 | } 323 | 324 | fn serialize_char(self, v: char) -> Result<()> { 325 | self.serialize_str(&v.to_string()) 326 | } 327 | 328 | fn serialize_str(self, v: &str) -> Result<()> { 329 | Ok(self.key = v.into()) 330 | } 331 | 332 | fn serialize_bytes(self, _v: &[u8]) -> Result<()> { 333 | Err(Error::NonStringKey) 334 | } 335 | 336 | fn serialize_none(self) -> Result<()> { 337 | Err(Error::NonStringKey) 338 | } 339 | 340 | fn serialize_some(self, value: &T) -> Result<()> { 341 | value.serialize(self) 342 | } 343 | 344 | fn serialize_unit(self) -> Result<()> { 345 | Err(Error::NonStringKey) 346 | } 347 | 348 | fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { 349 | Err(Error::NonStringKey) 350 | } 351 | 352 | fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str) -> Result<()> { 353 | self.serialize_str(variant) 354 | } 355 | 356 | fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> { 357 | value.serialize(self) 358 | } 359 | 360 | fn serialize_newtype_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T) -> Result<()> { 361 | Err(Error::NonStringKey) 362 | } 363 | 364 | fn serialize_seq(self, _len: Option) -> Result { 365 | Err(Error::NonStringKey) 366 | } 367 | 368 | fn serialize_tuple(self, len: usize) -> Result { 369 | self.serialize_seq(Some(len)) 370 | } 371 | 372 | fn serialize_tuple_struct(self, _name: &'static str, len: usize) -> Result { 373 | self.serialize_seq(Some(len)) 374 | } 375 | 376 | fn serialize_tuple_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize) -> Result { 377 | Err(Error::NonStringKey) 378 | } 379 | 380 | fn serialize_map(self, _len: Option) -> Result { 381 | Err(Error::NonStringKey) 382 | } 383 | 384 | fn serialize_struct(self, _name: &'static str, len: usize) -> Result { 385 | self.serialize_map(Some(len)) 386 | } 387 | 388 | fn serialize_struct_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize) -> Result { 389 | Err(Error::NonStringKey) 390 | } 391 | } 392 | 393 | impl<'a, W: Write> ser::Serializer for &'a mut Serializer { 394 | type Ok = (); 395 | type Error = Error; 396 | 397 | type SerializeSeq = Impossible; 398 | type SerializeTuple = Impossible; 399 | type SerializeTupleStruct = Impossible; 400 | type SerializeTupleVariant = Impossible; 401 | type SerializeMap = MapSerializer<'a, W>; 402 | type SerializeStruct = MapSerializer<'a, W>; 403 | type SerializeStructVariant = Impossible; 404 | 405 | fn serialize_bool(self, _v: bool) -> Result<()> { 406 | Err(Error::TopLevelMap) 407 | } 408 | 409 | fn serialize_i8(self, _v: i8) -> Result<()> { 410 | Err(Error::TopLevelMap) 411 | } 412 | 413 | fn serialize_i16(self, _v: i16) -> Result<()> { 414 | Err(Error::TopLevelMap) 415 | } 416 | 417 | fn serialize_i32(self, _v: i32) -> Result<()> { 418 | Err(Error::TopLevelMap) 419 | } 420 | 421 | fn serialize_i64(self, _v: i64) -> Result<()> { 422 | Err(Error::TopLevelMap) 423 | } 424 | 425 | fn serialize_u8(self, _v: u8) -> Result<()> { 426 | Err(Error::TopLevelMap) 427 | } 428 | 429 | fn serialize_u16(self, _v: u16) -> Result<()> { 430 | Err(Error::TopLevelMap) 431 | } 432 | 433 | fn serialize_u32(self, _v: u32) -> Result<()> { 434 | Err(Error::TopLevelMap) 435 | } 436 | 437 | fn serialize_u64(self, _v: u64) -> Result<()> { 438 | Err(Error::TopLevelMap) 439 | } 440 | 441 | fn serialize_f32(self, _v: f32) -> Result<()> { 442 | Err(Error::TopLevelMap) 443 | } 444 | 445 | fn serialize_f64(self, _v: f64) -> Result<()> { 446 | Err(Error::TopLevelMap) 447 | } 448 | 449 | fn serialize_char(self, _v: char) -> Result<()> { 450 | Err(Error::TopLevelMap) 451 | } 452 | 453 | fn serialize_str(self, _v: &str) -> Result<()> { 454 | Err(Error::TopLevelMap) 455 | } 456 | 457 | fn serialize_bytes(self, _v: &[u8]) -> Result<()> { 458 | Err(Error::TopLevelMap) 459 | } 460 | 461 | fn serialize_none(self) -> Result<()> { 462 | Err(Error::TopLevelMap) 463 | } 464 | 465 | fn serialize_some(self, value: &T) -> Result<()> { 466 | value.serialize(self) 467 | } 468 | 469 | fn serialize_unit(self) -> Result<()> { 470 | Err(Error::TopLevelMap) 471 | } 472 | 473 | fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { 474 | Err(Error::TopLevelMap) 475 | } 476 | 477 | fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str) -> Result<()> { 478 | self.serialize_str(variant) 479 | } 480 | 481 | fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> { 482 | value.serialize(self) 483 | } 484 | 485 | fn serialize_newtype_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T) -> Result<()> { 486 | Err(Error::TopLevelMap) 487 | } 488 | 489 | fn serialize_seq(self, _len: Option) -> Result { 490 | Err(Error::TopLevelMap) 491 | } 492 | 493 | fn serialize_tuple(self, len: usize) -> Result { 494 | self.serialize_seq(Some(len)) 495 | } 496 | 497 | fn serialize_tuple_struct(self, _name: &'static str, len: usize) -> Result { 498 | self.serialize_seq(Some(len)) 499 | } 500 | 501 | fn serialize_tuple_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize) -> Result { 502 | Err(Error::TopLevelMap) 503 | } 504 | 505 | fn serialize_map(self, _len: Option) -> Result { 506 | Ok(MapSerializer { 507 | writer: &mut self.writer, 508 | key: None, 509 | top_level: true, 510 | allow_values: true, 511 | }) 512 | } 513 | 514 | fn serialize_struct(self, _name: &'static str, len: usize) -> Result { 515 | self.serialize_map(Some(len)) 516 | } 517 | 518 | fn serialize_struct_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize) -> Result { 519 | Err(Error::TopLevelMap) 520 | } 521 | } 522 | 523 | impl<'a, W: Write> ser::SerializeMap for MapSerializer<'a, W> { 524 | type Ok = (); 525 | type Error = Error; 526 | 527 | fn serialize_key(&mut self, key: &T) -> Result<()> { 528 | let mut k = KeySerializer::default(); 529 | key.serialize(&mut k)?; 530 | self.key = Some(k.key); 531 | Ok(()) 532 | } 533 | 534 | fn serialize_value(&mut self, value: &T) -> Result<()> { 535 | let writer = &mut self.writer; 536 | let allow_values = &mut self.allow_values; 537 | let top_level = self.top_level; 538 | self.key.as_ref().ok_or_else(|| Error::MapKeyMissing).and_then(move |key| value.serialize(ValueSerializer { 539 | writer: writer, 540 | key: key, 541 | top_level: top_level, 542 | allow_values: allow_values, 543 | })) 544 | } 545 | 546 | fn end(self) -> Result<()> { 547 | Ok(()) 548 | } 549 | } 550 | 551 | impl<'a, W: Write> ser::SerializeStruct for MapSerializer<'a, W> { 552 | type Ok = (); 553 | type Error = Error; 554 | 555 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> { 556 | value.serialize(ValueSerializer { 557 | writer: &mut self.writer, 558 | key: key, 559 | top_level: self.top_level, 560 | allow_values: &mut self.allow_values, 561 | }) 562 | } 563 | 564 | fn end(self) -> Result<()> { 565 | Ok(()) 566 | } 567 | } 568 | 569 | pub fn to_writer(writer: W, value: &T) -> Result<()> { 570 | let mut ser = Serializer::new(Writer::new(writer, Default::default())); 571 | 572 | value.serialize(&mut ser) 573 | } 574 | 575 | pub fn to_vec(value: &T) -> Result> { 576 | let mut writer = Vec::with_capacity(128); 577 | to_writer(&mut writer, value).map(|_| writer) 578 | } 579 | 580 | pub fn to_string(value: &T) -> Result { 581 | let vec = to_vec(value)?; 582 | 583 | // does not emit invalid utf8 584 | Ok(unsafe { String::from_utf8_unchecked(vec) }) 585 | } 586 | -------------------------------------------------------------------------------- /src/write.rs: -------------------------------------------------------------------------------- 1 | use parse::Item; 2 | use std::io::{self, Write}; 3 | use std::fmt; 4 | 5 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 6 | pub enum LineEnding { 7 | Linefeed, 8 | CrLf, 9 | } 10 | 11 | impl Default for LineEnding { 12 | fn default() -> Self { 13 | LineEnding::CrLf 14 | } 15 | } 16 | 17 | impl fmt::Display for LineEnding { 18 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 19 | write!(f, "{}", match *self { 20 | LineEnding::Linefeed => "\n", 21 | LineEnding::CrLf => "\r\n", 22 | }) 23 | } 24 | } 25 | 26 | #[derive(Debug, Copy, Clone)] 27 | pub struct Writer { 28 | write: W, 29 | line_ending: LineEnding, 30 | } 31 | 32 | impl Writer { 33 | pub fn new(write: W, line_ending: LineEnding) -> Self { 34 | Writer { 35 | write: write, 36 | line_ending: line_ending, 37 | } 38 | } 39 | 40 | pub fn into_inner(self) -> W { 41 | self.write 42 | } 43 | } 44 | 45 | impl Writer { 46 | pub fn write(&mut self, item: &Item) -> io::Result<()> { 47 | match *item { 48 | Item::Section { ref name } => write!(&mut self.write, "[{}]{}", name, self.line_ending), 49 | Item::Value { ref key, ref value } => write!(&mut self.write, "{}={}{}", key, value, self.line_ending), 50 | Item::Comment { ref text } => write!(&mut self.write, ";{}{}", text, self.line_ending), 51 | Item::Empty => write!(&mut self.write, "{}", self.line_ending), 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/enum_seq.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | extern crate serde; 4 | extern crate serde_ini; 5 | 6 | use serde::Deserialize; 7 | use serde_ini::{Deserializer, Parser}; 8 | 9 | #[derive(Deserialize, Serialize, Clone, PartialEq, Debug)] 10 | enum TestModel { 11 | Person { 12 | name: String, 13 | } 14 | } 15 | 16 | const TEST_INPUT: &'static str = " 17 | [Person] 18 | name=Ana 19 | 20 | [Person] 21 | name=Box 22 | "; 23 | 24 | fn expected() -> Vec { 25 | vec![ 26 | TestModel::Person { 27 | name: "Ana".into(), 28 | }, 29 | TestModel::Person { 30 | name: "Box".into(), 31 | }, 32 | ] 33 | } 34 | 35 | #[test] 36 | fn enum_seq_de() { 37 | assert_eq!(expected(), serde_ini::from_str::>(TEST_INPUT).unwrap()); 38 | } 39 | 40 | #[test] 41 | #[ignore = "not yet implemented"] 42 | fn enum_seq_en() { 43 | let model = expected(); 44 | 45 | let data = serde_ini::to_vec(&model).unwrap(); 46 | 47 | assert_eq!(model, serde_ini::from_read::<_, Vec>(&data[..]).unwrap()); 48 | } 49 | -------------------------------------------------------------------------------- /tests/smoke.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | extern crate serde; 4 | extern crate serde_ini; 5 | 6 | use serde::Deserialize; 7 | use serde_ini::{Deserializer, Parser}; 8 | 9 | #[derive(Deserialize, Serialize, Clone, PartialEq, Default, Debug)] 10 | struct TestModel { 11 | key1: String, 12 | key2: u32, 13 | key3: String, 14 | #[serde(default, skip_serializing_if = "Option::is_none")] 15 | map1: Option>, 16 | #[serde(default, skip_serializing_if = "Option::is_none")] 17 | map2: Option>, 18 | } 19 | 20 | const TEST_INPUT: &'static str = " 21 | ; Ignored comment 22 | key1=value1 23 | key2=255 24 | key3 = value3 25 | 26 | [map1] 27 | key2=256 28 | key1=value2 29 | key3= 30 | 31 | # We also treat hash as a comment character. 32 | [map2] 33 | key1=value3 34 | key2=257 35 | key3= 36 | "; 37 | 38 | fn expected() -> TestModel { 39 | TestModel { 40 | key1: "value1".into(), 41 | key2: 255, 42 | key3: "value3".into(), 43 | map1: Some(Box::new(TestModel { 44 | key1: "value2".into(), 45 | key2: 256, 46 | .. Default::default() 47 | })), 48 | map2: Some(Box::new(TestModel { 49 | key1: "value3".into(), 50 | key2: 257, 51 | .. Default::default() 52 | })), 53 | } 54 | } 55 | 56 | #[test] 57 | fn smoke_de() { 58 | 59 | // Parser 60 | assert_eq!(expected(), TestModel::deserialize(&mut Deserializer::new(Parser::from_bufread(TEST_INPUT.as_bytes()))).unwrap()); 61 | assert_eq!(expected(), TestModel::deserialize(&mut Deserializer::new(Parser::from_read(TEST_INPUT.as_bytes()))).unwrap()); 62 | assert_eq!(expected(), TestModel::deserialize(&mut Deserializer::new(Parser::from_str(TEST_INPUT))).unwrap()); 63 | 64 | // Deserializer 65 | assert_eq!(expected(), TestModel::deserialize(&mut Deserializer::from_bufread(TEST_INPUT.as_bytes())).unwrap()); 66 | assert_eq!(expected(), TestModel::deserialize(&mut Deserializer::from_read(TEST_INPUT.as_bytes())).unwrap()); 67 | assert_eq!(expected(), TestModel::deserialize(&mut Deserializer::from_str(TEST_INPUT)).unwrap()); 68 | 69 | // Static methods 70 | assert_eq!(expected(), serde_ini::from_bufread::<_, TestModel>(TEST_INPUT.as_bytes()).unwrap()); 71 | assert_eq!(expected(), serde_ini::from_read::<_, TestModel>(TEST_INPUT.as_bytes()).unwrap()); 72 | assert_eq!(expected(), serde_ini::from_str::(TEST_INPUT).unwrap()); 73 | } 74 | 75 | #[test] 76 | fn smoke_en() { 77 | let model = expected(); 78 | 79 | let data = serde_ini::to_vec(&model).unwrap(); 80 | 81 | assert_eq!(model, serde_ini::from_read::<_, TestModel>(&data[..]).unwrap()); 82 | } 83 | --------------------------------------------------------------------------------