| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! # A message ID generator for the `strings.xml` format |
| //! |
| //! This crate contains a binary `strings_to_fidl`, which generates message IDs from the |
| //! Android-formatted [strings.xml resource file][strings-xml], as a set of FIDL constants. |
| //! Complete support is not a specific goal, rather the generator will be amended to include more |
| //! features as more features are needed. |
| //! |
| //! [strings-xml]: https://developer.android.com/guide/topics/resources/string-resource |
| |
| // TODO(fmil): temporary, until all code is used. |
| #![allow(dead_code)] |
| |
| use { |
| anyhow::Context, |
| anyhow::Error, |
| anyhow::Result, |
| intl_strings::{message_ids, parser, veprintln}, |
| std::env, |
| std::fs::File, |
| std::io, |
| std::path::PathBuf, |
| structopt::StructOpt, |
| }; |
| |
| // TODO(fmil): Add usage link here. |
| #[derive(Debug, StructOpt)] |
| #[structopt(name = "Extracts information from strings.xml into FIDL")] |
| struct Args { |
| #[structopt(long = "input", help = "The path to the input strings.xml format file")] |
| input: PathBuf, |
| #[structopt(long = "output", help = "The path to the output FIDL file")] |
| output: PathBuf, |
| #[structopt(long = "verbose", help = "Verbose output, for debugging")] |
| verbose: bool, |
| #[structopt( |
| long = "library", |
| default_value = "fuchsia.intl.l10n", |
| help = "The FIDL library name for which to generate the code" |
| )] |
| library: String, |
| } |
| |
| /// Open the needed files, and handle usual errors. |
| fn open_files(args: &Args) -> Result<(impl io::Read, impl io::Write), Error> { |
| let input_str = args.input.to_str().with_context(|| { |
| format!("input filename is not utf-8, what? Use --verbose flag to print the value.") |
| })?; |
| let input = io::BufReader::new( |
| File::open(&args.input) |
| .with_context(|| format!("could not open input file: {}", input_str))?, |
| ); |
| let output_str = args.output.to_str().with_context(|| { |
| format!("output filename is not utf-8, what? Use --verbose flag to print the value.") |
| })?; |
| let output = File::create(&args.output) |
| .with_context(|| format!("could not open output file: {}", output_str))?; |
| Ok((input, output)) |
| } |
| |
| fn run(args: Args) -> Result<()> { |
| veprintln!(args.verbose, "args: {:?}", args); |
| |
| let (input, mut output) = open_files(&args).with_context(|| "while opening files")?; |
| let reader = parser::Instance::reader(input); |
| |
| let mut parser = parser::Instance::new(args.verbose); |
| let dictionary = parser.parse(reader).with_context(|| "while parsing dictionary")?; |
| let model = message_ids::from_dictionary(dictionary, &args.library)?; |
| message_ids::render(model, &mut output) |
| } |
| |
| fn main() -> Result<()> { |
| env::set_var("RUST_BACKTRACE", "full"); |
| run(Args::from_args()) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use std::fs; |
| use std::io::Write; |
| use tempfile; |
| |
| // This test is only used to confirm that a program call generates some |
| // output that looks meaningful. Refer to the unit tests in the library |
| // for tests that actually enforce the specification. |
| #[test] |
| fn basic() -> Result<(), Error> { |
| let en = tempfile::NamedTempFile::new()?; |
| write!( |
| en.as_file(), |
| r#" |
| <?xml version="1.0" encoding="utf-8"?> |
| <resources> |
| <!-- comment --> |
| <string |
| name="string_name" |
| >string</string> |
| </resources> |
| |
| "# |
| ) |
| .with_context(|| "while writing 'en' tempfile")?; |
| |
| let output = tempfile::NamedTempFile::new()?; |
| |
| let args = Args { |
| input: en.path().to_path_buf(), |
| output: output.path().to_path_buf(), |
| verbose: false, |
| library: "some_lib".to_string(), |
| }; |
| |
| run(args)?; |
| |
| let outcome = fs::read_to_string(output.path())?; |
| assert_eq!( |
| r#"// Generated by strings_to_fidl. DO NOT EDIT! |
| |
| library some_lib; |
| |
| type MessageIds = strict enum : uint64 { |
| // 'string' |
| STRING_NAME = 7134240810508078445; |
| }; |
| "#, |
| outcome |
| ); |
| Ok(()) |
| } |
| |
| #[test] |
| fn reader_config() -> Result<(), Error> { |
| let en = tempfile::NamedTempFile::new()?; |
| write!( |
| en.as_file(), |
| r#" |
| <!-- comment is not allowed before ?xml? --> |
| <?xml version="1.0" encoding="utf-8"?> |
| <resources> |
| <!-- comment --> |
| <string |
| name="string_name" |
| >string</string> |
| </resources> |
| |
| "# |
| ) |
| .with_context(|| "while writing 'en' tempfile")?; |
| |
| let output = tempfile::NamedTempFile::new()?; |
| |
| let args = Args { |
| input: en.path().to_path_buf(), |
| output: output.path().to_path_buf(), |
| verbose: false, |
| library: "some_lib".to_string(), |
| }; |
| |
| match run(args) { |
| Ok(_) => Err(anyhow::anyhow!("unexpected OK")), |
| Err(_) => Ok(()), |
| } |
| } |
| } |