blob: 17c3ebf16c8c7321107fc0a99bfad4ccf0149d4a [file] [log] [blame]
// 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;
enum MessageIds : 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(()),
}
}
}