From 10c5ee1159288b6313e59fb78e03c0ed434cdddb Mon Sep 17 00:00:00 2001 From: Vlasislav Kashin <99754299+vloldik@users.noreply.github.com> Date: Fri, 11 Jul 2025 03:46:50 +0300 Subject: [PATCH] Java: added compiled metadata generation --- src/lib.rs | 2 +- .../metadata.rs | 17 ++- src/phonenumberutil/generated/mod.rs | 3 + .../generated}/test_metadata.rs | 20 +++- src/phonenumberutil/helper_constants/mod.rs | 2 - src/phonenumberutil/helper_functions.rs | 7 +- src/phonenumberutil/mod.rs | 1 + src/tests/mod.rs | 1 - src/tests/tests.rs | 4 +- tools/java/Readme.md | 12 +- .../i18n/phonenumbers/CopyrightNotice.java | 21 +--- tools/java/rust-build/pom.xml | 11 +- ...Xml.java => BuildMetadataRustFromXml.java} | 75 ++++++++----- .../google/i18n/phonenumbers/EntryPoint.java | 6 +- ...erator.java => RustMetadataGenerator.java} | 106 +++++------------- ...java => BuildMetadataRustFromXmlTest.java} | 93 +++++---------- ...st.java => RustMetadataGeneratorTest.java} | 55 ++++----- tools/scripts/generate_metadata.sh | 33 ++++++ 18 files changed, 222 insertions(+), 247 deletions(-) rename src/phonenumberutil/{helper_constants => generated}/metadata.rs (99%) create mode 100644 src/phonenumberutil/generated/mod.rs rename src/{tests => phonenumberutil/generated}/test_metadata.rs (99%) rename tools/java/rust-build/src/com/google/i18n/phonenumbers/{BuildMetadataCppFromXml.java => BuildMetadataRustFromXml.java} (74%) rename tools/java/rust-build/src/com/google/i18n/phonenumbers/{CppMetadataGenerator.java => RustMetadataGenerator.java} (55%) rename tools/java/rust-build/test/com/google/i18n/phonenumbers/{BuildMetadataCppFromXmlTest.java => BuildMetadataRustFromXmlTest.java} (56%) rename tools/java/rust-build/test/com/google/i18n/phonenumbers/{CppMetadataGeneratorTest.java => RustMetadataGeneratorTest.java} (58%) create mode 100644 tools/scripts/generate_metadata.sh diff --git a/src/lib.rs b/src/lib.rs index 172b27f..e3d6aa3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,4 +32,4 @@ pub use phonenumberutil::{ }; pub use proto_gen::phonemetadata; pub use proto_gen::phonenumber; -mod tests; \ No newline at end of file +mod tests; diff --git a/src/phonenumberutil/helper_constants/metadata.rs b/src/phonenumberutil/generated/metadata.rs similarity index 99% rename from src/phonenumberutil/helper_constants/metadata.rs rename to src/phonenumberutil/generated/metadata.rs index 82419a3..5b6389f 100644 --- a/src/phonenumberutil/helper_constants/metadata.rs +++ b/src/phonenumberutil/generated/metadata.rs @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2011 The Libphonenumber Authors + * Copyright (C) 2025 Vladislav Kashin (modified) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ pub const METADATA: [u8; 201802] = [ 0x0A, 0xE9, 0x01, 0x0A, 0x1D, 0x12, 0x17, 0x28, 0x3F, 0x3A, 0x5B, 0x30, 0x31, @@ -15525,4 +15541,3 @@ pub const METADATA: [u8; 201802] = [ 0xFF, 0x01, 0xE2, 0x01, 0x0B, 0x48, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 ]; - diff --git a/src/phonenumberutil/generated/mod.rs b/src/phonenumberutil/generated/mod.rs new file mode 100644 index 0000000..6c7373e --- /dev/null +++ b/src/phonenumberutil/generated/mod.rs @@ -0,0 +1,3 @@ +pub mod metadata; +pub mod test_metadata; + diff --git a/src/tests/test_metadata.rs b/src/phonenumberutil/generated/test_metadata.rs similarity index 99% rename from src/tests/test_metadata.rs rename to src/phonenumberutil/generated/test_metadata.rs index e3be2a2..2c59a1f 100644 --- a/src/tests/test_metadata.rs +++ b/src/phonenumberutil/generated/test_metadata.rs @@ -1,5 +1,21 @@ +/* + * Copyright (C) 2011 The Libphonenumber Authors + * Copyright (C) 2025 Vladislav Kashin (modified) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -pub const METADATA: [u8; 13260] = [ +pub const TEST_METADATA: [u8; 13260] = [ 0x0A, 0xAD, 0x01, 0x0A, 0x09, 0x12, 0x05, 0x5C, 0x64, 0x7B, 0x36, 0x7D, 0x48, 0x06, 0x12, 0x0F, 0x12, 0x05, 0x5C, 0x64, 0x7B, 0x36, 0x7D, 0x32, 0x06, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x1A, 0x0B, 0x48, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, @@ -1020,4 +1036,4 @@ pub const METADATA: [u8; 13260] = [ 0x48, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xCA, 0x01, 0x0B, 0x48, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xE2, 0x01, 0x0B, 0x48, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 -]; \ No newline at end of file +]; diff --git a/src/phonenumberutil/helper_constants/mod.rs b/src/phonenumberutil/helper_constants/mod.rs index da7f7a0..372cdc2 100644 --- a/src/phonenumberutil/helper_constants/mod.rs +++ b/src/phonenumberutil/helper_constants/mod.rs @@ -1,5 +1,3 @@ mod helper_constants; -mod metadata; pub(super) use helper_constants::{*}; -pub(super) use metadata::METADATA; diff --git a/src/phonenumberutil/helper_functions.rs b/src/phonenumberutil/helper_functions.rs index 490c1dd..35a2e73 100644 --- a/src/phonenumberutil/helper_functions.rs +++ b/src/phonenumberutil/helper_functions.rs @@ -4,18 +4,17 @@ use protobuf::Message; use strum::IntoEnumIterator; use crate::{ - interfaces::MatcherApi, - proto_gen::{ + interfaces::MatcherApi, phonenumberutil::generated::metadata::METADATA, proto_gen::{ phonemetadata::{PhoneMetadata, PhoneMetadataCollection, PhoneNumberDesc}, phonenumber::PhoneNumber, - }, + } }; use super::{ PhoneNumberFormat, PhoneNumberType, ValidNumberLenType, errors::ValidationResultErr, helper_constants::{ - METADATA, OPTIONAL_EXT_SUFFIX, PLUS_SIGN, POSSIBLE_CHARS_AFTER_EXT_LABEL, + OPTIONAL_EXT_SUFFIX, PLUS_SIGN, POSSIBLE_CHARS_AFTER_EXT_LABEL, POSSIBLE_SEPARATORS_BETWEEN_NUMBER_AND_EXT_LABEL, RFC3966_EXTN_PREFIX, RFC3966_PREFIX, }, }; diff --git a/src/phonenumberutil/mod.rs b/src/phonenumberutil/mod.rs index 21d2abb..7aa9989 100644 --- a/src/phonenumberutil/mod.rs +++ b/src/phonenumberutil/mod.rs @@ -6,6 +6,7 @@ pub mod phonenumberutil; mod phone_number_regexps_and_mappings; pub(self) mod helper_types; pub(self) mod comparisons; +pub(crate) mod generated; use std::sync::LazyLock; diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 54cdcab..14f0038 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,2 +1 @@ mod tests; -mod test_metadata; \ No newline at end of file diff --git a/src/tests/tests.rs b/src/tests/tests.rs index 58aef3e..c9ba7f3 100644 --- a/src/tests/tests.rs +++ b/src/tests/tests.rs @@ -10,13 +10,13 @@ use protobuf::Message; use crate::{errors::ParseError, phonemetadata::PhoneMetadataCollection, phonenumber::PhoneNumber, PhoneNumberUtil}; -use super::test_metadata::METADATA; +use crate::phonenumberutil::generated::test_metadata::TEST_METADATA; // This setup function simulates getting the PhoneNumberUtil instance for each test. fn get_phone_util() -> PhoneNumberUtil { - let metadata = PhoneMetadataCollection::parse_from_bytes(&METADATA) + let metadata = PhoneMetadataCollection::parse_from_bytes(&TEST_METADATA) .expect("Metadata should be valid"); // In a real scenario, this would likely return a singleton instance. return PhoneNumberUtil::new_for_metadata(metadata); diff --git a/tools/java/Readme.md b/tools/java/Readme.md index a958d94..608f0c7 100644 --- a/tools/java/Readme.md +++ b/tools/java/Readme.md @@ -1 +1,11 @@ -## This directory contains script for autogeneration of metadata in rust \ No newline at end of file +## This directory contains script for autogeneration of metadata in rust + +To build from source cd /tools/java and +``` +mvn install +``` + +Example command on build generator +``` +java -jar tools\java\rust-build\target\rust-build-1.0-SNAPSHOT-jar-with-dependencies.jar BuildMetadataRustFromXml resources\PhoneNumberMetadata.xml ./test.rs metadata --const-name=test +``` \ No newline at end of file diff --git a/tools/java/common/src/com/google/i18n/phonenumbers/CopyrightNotice.java b/tools/java/common/src/com/google/i18n/phonenumbers/CopyrightNotice.java index d8cd9e2..03fdd30 100644 --- a/tools/java/common/src/com/google/i18n/phonenumbers/CopyrightNotice.java +++ b/tools/java/common/src/com/google/i18n/phonenumbers/CopyrightNotice.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 The Libphonenumber Authors + * Copyright (C) %d Vladislav Kashin (modified) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,18 +25,16 @@ import java.util.Formatter; * Class containing the Apache copyright notice used by code generators. * * @author Philippe Liard + * @author Kashin Vladislav (modified for Rust code generation) */ public class CopyrightNotice { private static final String TEXT_OPENING = "/*\n"; - private static final String TEXT_OPENING_FOR_JAVASCRIPT = - "/**\n" + - " * @license\n"; - private static final String TEXT = " * Copyright (C) %d The Libphonenumber Authors\n" + + " * Copyright (C) %d Vladislav Kashin (modified)\n" + " *\n" + " * Licensed under the Apache License, Version 2.0 (the \"License\");\n" + " * you may not use this file except in compliance with the License.\n" + @@ -50,17 +49,9 @@ public class CopyrightNotice { " * limitations under the License.\n" + " */\n\n"; - static final void writeTo(Writer writer, int year) throws IOException { - writeTo(writer, year, false); - } - - static final void writeTo(Writer writer, int year, boolean isJavascript) throws IOException { - if (isJavascript) { - writer.write(TEXT_OPENING_FOR_JAVASCRIPT); - } else { - writer.write(TEXT_OPENING); - } + static final void writeTo(Writer writer, int year, int yearSecondAuthor) throws IOException { + writer.write(TEXT_OPENING); Formatter formatter = new Formatter(writer); - formatter.format(TEXT, year); + formatter.format(TEXT, year, yearSecondAuthor); } } diff --git a/tools/java/rust-build/pom.xml b/tools/java/rust-build/pom.xml index 86407e0..3b9fec6 100644 --- a/tools/java/rust-build/pom.xml +++ b/tools/java/rust-build/pom.xml @@ -10,12 +10,13 @@ com.google.i18n.phonenumbers.tools - cpp-build + rust-build 1.0-SNAPSHOT - Libphonenumber C++ build tools + Libphonenumber Rust build tools - C++ build tools that download dependencies under base/ from the Chromium source repository, and - generate the C++ metadata code needed to build the libphonenumber library. + Rust build tools that download dependencies under base/ from the Chromium source repository, and + generate the Rust metadata code needed to build the libphonenumber library. + It depends on libphonenumber original Java library. @@ -115,8 +116,6 @@ - org.apache.maven.plugins maven-assembly-plugin diff --git a/tools/java/rust-build/src/com/google/i18n/phonenumbers/BuildMetadataCppFromXml.java b/tools/java/rust-build/src/com/google/i18n/phonenumbers/BuildMetadataRustFromXml.java similarity index 74% rename from tools/java/rust-build/src/com/google/i18n/phonenumbers/BuildMetadataCppFromXml.java rename to tools/java/rust-build/src/com/google/i18n/phonenumbers/BuildMetadataRustFromXml.java index ff25165..4f51d42 100644 --- a/tools/java/rust-build/src/com/google/i18n/phonenumbers/BuildMetadataCppFromXml.java +++ b/tools/java/rust-build/src/com/google/i18n/phonenumbers/BuildMetadataRustFromXml.java @@ -1,6 +1,7 @@ /* * Copyright (C) 2011 The Libphonenumber Authors - * + * Copyright (C) 2025 The Kashin Vladislav (modified) + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,7 +17,7 @@ package com.google.i18n.phonenumbers; -import com.google.i18n.phonenumbers.CppMetadataGenerator.Type; +import com.google.i18n.phonenumbers.RustMetadataGenerator.Type; import java.io.ByteArrayOutputStream; import java.io.File; @@ -26,19 +27,22 @@ import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * This class generates the C++ code representation of the provided XML metadata file. It lets us + * This class generates the Rust code representation of the provided XML metadata file. It lets us * embed metadata directly in a native binary. We link the object resulting from the compilation of - * the code emitted by this class with the C++ phonenumber library. + * the code emitted by this class with the Rust rlibphonenumber library. * * @author Philippe Liard * @author David Beaumont + * + * @author Kashin Vladislav (modified for Rust code generation) */ -public class BuildMetadataCppFromXml extends Command { +public class BuildMetadataRustFromXml extends Command { /** An enum encapsulating the variations of metadata that we can produce. */ public enum Variant { @@ -93,22 +97,38 @@ public class BuildMetadataCppFromXml extends Command { static final class Options { private static final Pattern BASENAME_PATTERN = Pattern.compile("(?:(test|lite)_)?([a-z_]+)"); - - public static Options parse(String commandName, String[] args) { - if (args.length == 4) { - String inputXmlFilePath = args[1]; - String outputDirPath = args[2]; - Matcher basenameMatcher = BASENAME_PATTERN.matcher(args[3]); + private static final Pattern CONSTANT_NAME_PATTERN = + Pattern.compile("--const-name[ =]([a-zA-Z_]+)"); + private static final String DEFAULT_METADATA_CONSTANT_NAME = "METADATA"; + public static Options parse(String commandName, String[] argsArray) { + ArrayList args = new ArrayList(Arrays.asList(argsArray)); + String constantName = DEFAULT_METADATA_CONSTANT_NAME; + if (args.size() == 5) { + for (int i = 0; i < args.size(); i++) { + String arg = args.get(i).toString(); + Matcher matcher = CONSTANT_NAME_PATTERN.matcher(arg.toString()); + if (matcher.matches()) { + constantName = matcher.group(1); + args.remove(arg); + break; + } + } + } + if (args.size() == 4) { + String inputXmlFilePath = args.get(1).toString(); + String outputDirPath = args.get(2).toString(); + Matcher basenameMatcher = BASENAME_PATTERN.matcher(args.get(3).toString()); if (basenameMatcher.matches()) { Variant variant = Variant.parse(basenameMatcher.group(1)); Type type = Type.parse(basenameMatcher.group(2)); if (type != null && variant != null) { - return new Options(inputXmlFilePath, outputDirPath, type, variant); + return new Options(inputXmlFilePath, outputDirPath, type, variant, constantName); } } } throw new IllegalArgumentException(String.format( - "Usage: %s ( | test_ | lite_ )\n" + + "Usage: %s | test_ | lite_ ) " + + "[--const-name ] \n" + " where is one of: %s", commandName, Arrays.asList(Type.values()))); } @@ -119,12 +139,14 @@ public class BuildMetadataCppFromXml extends Command { private final String outputDirPath; private final Type type; private final Variant variant; + private final String constantName; - private Options(String inputXmlFilePath, String outputDirPath, Type type, Variant variant) { + private Options(String inputXmlFilePath, String outputDirPath, Type type, Variant variant, String constantName) { this.inputXmlFilePath = inputXmlFilePath; this.outputDirPath = outputDirPath; this.type = type; this.variant = variant; + this.constantName = constantName; } public String getInputFilePath() { @@ -142,17 +164,21 @@ public class BuildMetadataCppFromXml extends Command { public Variant getVariant() { return variant; } + + public String getConstantName() { + return constantName; + } } @Override public String getCommandName() { - return "BuildMetadataCppFromXml"; + return "BuildMetadataRustFromXml"; } /** - * Generates C++ header and source files to represent the metadata specified by this command's + * Generates Rust source file to represent the metadata specified by this command's * arguments. The metadata XML file is read and converted to a byte array before being written - * into a C++ source file as a static data array. + * into a Rust source file as a static data array. * * @return true if the generation succeeded. */ @@ -161,16 +187,14 @@ public class BuildMetadataCppFromXml extends Command { try { Options opt = Options.parse(getCommandName(), getArgs()); byte[] data = loadMetadataBytes(opt.getInputFilePath(), opt.getVariant() == Variant.LITE); - CppMetadataGenerator metadata = CppMetadataGenerator.create(opt.getType(), data); + RustMetadataGenerator metadata = RustMetadataGenerator.create(opt.getType(), data, opt.constantName); // TODO: Consider adding checking for correctness of file paths and access. OutputStream headerStream = null; OutputStream sourceStream = null; try { File dir = new File(opt.getOutputDir()); - headerStream = openHeaderStream(dir, opt.getType()); - sourceStream = openSourceStream(dir, opt.getType(), opt.getVariant()); - metadata.outputHeaderFile(new OutputStreamWriter(headerStream, UTF_8)); + sourceStream = openSourceStream(dir); metadata.outputSourceFile(new OutputStreamWriter(sourceStream, UTF_8)); } finally { FileUtils.closeFiles(headerStream, sourceStream); @@ -206,13 +230,8 @@ public class BuildMetadataCppFromXml extends Command { } // @VisibleForTesting - OutputStream openHeaderStream(File dir, Type type) throws FileNotFoundException { - return new FileOutputStream(new File(dir, type + ".h")); - } - - // @VisibleForTesting - OutputStream openSourceStream(File dir, Type type, Variant variant) throws FileNotFoundException { - return new FileOutputStream(new File(dir, variant.getBasename(type) + ".cc")); + OutputStream openSourceStream(File file) throws FileNotFoundException { + return new FileOutputStream(file); } /** The charset in which our source and header files will be written. */ diff --git a/tools/java/rust-build/src/com/google/i18n/phonenumbers/EntryPoint.java b/tools/java/rust-build/src/com/google/i18n/phonenumbers/EntryPoint.java index d08184d..40de501 100644 --- a/tools/java/rust-build/src/com/google/i18n/phonenumbers/EntryPoint.java +++ b/tools/java/rust-build/src/com/google/i18n/phonenumbers/EntryPoint.java @@ -1,5 +1,7 @@ /* * Copyright (C) 2011 The Libphonenumber Authors + * Copyright (C) 2025 The Kashin Vladislav (modified) + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +22,14 @@ package com.google.i18n.phonenumbers; * Entry point class for C++ build tools. * * @author Philippe Liard + * + * @author Kashin Vladislav (modified for Rust code generation) */ public class EntryPoint { public static void main(String[] args) { boolean status = new CommandDispatcher(args, new Command[] { - new BuildMetadataCppFromXml() + new BuildMetadataRustFromXml() }).start(); System.exit(status ? 0 : 1); diff --git a/tools/java/rust-build/src/com/google/i18n/phonenumbers/CppMetadataGenerator.java b/tools/java/rust-build/src/com/google/i18n/phonenumbers/RustMetadataGenerator.java similarity index 55% rename from tools/java/rust-build/src/com/google/i18n/phonenumbers/CppMetadataGenerator.java rename to tools/java/rust-build/src/com/google/i18n/phonenumbers/RustMetadataGenerator.java index 22e832c..4f60871 100644 --- a/tools/java/rust-build/src/com/google/i18n/phonenumbers/CppMetadataGenerator.java +++ b/tools/java/rust-build/src/com/google/i18n/phonenumbers/RustMetadataGenerator.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 The Libphonenumber Authors + * Copyright (C) 2025 The Kashin Vladislav (modified) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +28,10 @@ import java.util.Locale; * * @author David Beaumont * @author Philippe Liard + * + * @author Kashin Vladislav (modified for Rust code generation) */ -public final class CppMetadataGenerator { +public final class RustMetadataGenerator { /** * The metadata type represents the known types of metadata and includes additional information @@ -37,18 +40,18 @@ public final class CppMetadataGenerator { */ public enum Type { /** The basic phone number metadata (expected to be written to metadata.[h/cc]). */ - METADATA("metadata", 2011), + METADATA(2011, 2025), /** The alternate format metadata (expected to be written to alternate_format.[h/cc]). */ - ALTERNATE_FORMAT("alternate_format", 2012), + ALTERNATE_FORMAT(2012, 2025), /** Metadata for short numbers (expected to be written to short_metadata.[h/cc]). */ - SHORT_NUMBERS("short_metadata", 2013); + SHORT_NUMBERS(2013, 2025); - private final String typeName; private final int copyrightYear; + private final int copyrightSecondYear; - private Type(String typeName, int copyrightYear) { - this.typeName = typeName; + private Type(int copyrightYear, int CopyrightSecondYear) { this.copyrightYear = copyrightYear; + this.copyrightSecondYear = CopyrightSecondYear; } /** Returns the year in which this metadata type was first introduced. */ @@ -56,12 +59,9 @@ public final class CppMetadataGenerator { return copyrightYear; } - /** - * Returns the name of this type for use in C++ source/header files. Use this in preference to - * using {@link #name}. - */ - @Override public String toString() { - return typeName; + /** Returns the year in which this metadata type was modified for RUST. */ + public int getCopyrightSecondYear() { + return copyrightSecondYear; } /** @@ -88,93 +88,39 @@ public final class CppMetadataGenerator { * retained by the newly created CppXmlMetadata instance, so the caller should treat the array as * immutable after making this call. */ - public static CppMetadataGenerator create(Type type, byte[] data) { - return new CppMetadataGenerator(type, data); + public static RustMetadataGenerator create(Type type, byte[] data, String constantName) { + return new RustMetadataGenerator(type, data, constantName); } private final Type type; private final byte[] data; - private final String guardName; // e.g. "I18N_PHONENUMBERS__H_" - private final String headerInclude; // e.g. "phonenumbers/.h" + private final String constantName; - private CppMetadataGenerator(Type type, byte[] data) { + private RustMetadataGenerator(Type type, byte[] data, String variableName) { this.type = type; this.data = data; - this.guardName = createGuardName(type); - this.headerInclude = createHeaderInclude(type); + this.constantName = variableName; } /** - * Writes the header file for the C++ representation of the metadata to the given writer. Note - * that this method does not close the given writer. - */ - public void outputHeaderFile(Writer out) throws IOException { - PrintWriter pw = new PrintWriter(out); - CopyrightNotice.writeTo(pw, type.getCopyrightYear()); - pw.println("#ifndef " + guardName); - pw.println("#define " + guardName); - pw.println(); - emitNamespaceStart(pw); - pw.println(); - pw.println("int " + type + "_size();"); - pw.println("const void* " + type + "_get();"); - pw.println(); - emitNamespaceEnd(pw); - pw.println(); - pw.println("#endif // " + guardName); - pw.flush(); - } - - /** - * Writes the source file for the C++ representation of the metadata, including a static array + * Writes the source file for the Rust representation of the metadata - a static array * containing the data itself, to the given writer. Note that this method does not close the given * writer. */ public void outputSourceFile(Writer out) throws IOException { // TODO: Consider outputting a load method to return the parsed proto directly. + String dataLength = String.valueOf(data.length); + + PrintWriter pw = new PrintWriter(out); - CopyrightNotice.writeTo(pw, type.getCopyrightYear()); - pw.println("#include \"" + headerInclude + "\""); - pw.println(); - emitNamespaceStart(pw); - pw.println(); - pw.println("namespace {"); - pw.println("static const unsigned char data[] = {"); + CopyrightNotice.writeTo(pw, type.getCopyrightYear(), type.getCopyrightSecondYear()); + pw.println("pub const "+constantName+": [u8; "+dataLength+"] = ["); emitStaticArrayData(pw, data); - pw.println("};"); - pw.println("} // namespace"); - pw.println(); - pw.println("int " + type + "_size() {"); - pw.println(" return sizeof(data) / sizeof(data[0]);"); - pw.println("}"); - pw.println(); - pw.println("const void* " + type + "_get() {"); - pw.println(" return data;"); - pw.println("}"); - pw.println(); - emitNamespaceEnd(pw); + pw.println("];"); pw.flush(); } - private static String createGuardName(Type type) { - return String.format("I18N_PHONENUMBERS_%s_H_", type.toString().toUpperCase(Locale.ENGLISH)); - } - - private static String createHeaderInclude(Type type) { - return String.format("phonenumbers/%s.h", type); - } - - private static void emitNamespaceStart(PrintWriter pw) { - pw.println("namespace i18n {"); - pw.println("namespace phonenumbers {"); - } - - private static void emitNamespaceEnd(PrintWriter pw) { - pw.println("} // namespace phonenumbers"); - pw.println("} // namespace i18n"); - } - - /** Emits the C++ code corresponding to the binary metadata as a static byte array. */ + /** Emits the Rust code corresponding to the binary metadata as a static byte array. */ // @VisibleForTesting static void emitStaticArrayData(PrintWriter pw, byte[] data) { String separator = " "; diff --git a/tools/java/rust-build/test/com/google/i18n/phonenumbers/BuildMetadataCppFromXmlTest.java b/tools/java/rust-build/test/com/google/i18n/phonenumbers/BuildMetadataRustFromXmlTest.java similarity index 56% rename from tools/java/rust-build/test/com/google/i18n/phonenumbers/BuildMetadataCppFromXmlTest.java rename to tools/java/rust-build/test/com/google/i18n/phonenumbers/BuildMetadataRustFromXmlTest.java index b1976e8..0847e90 100644 --- a/tools/java/rust-build/test/com/google/i18n/phonenumbers/BuildMetadataCppFromXmlTest.java +++ b/tools/java/rust-build/test/com/google/i18n/phonenumbers/BuildMetadataRustFromXmlTest.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 The Libphonenumber Authors + * Copyright (C) 2025 The Kashin Vladislav (modified) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +22,9 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.google.i18n.phonenumbers.BuildMetadataCppFromXml.Options; -import com.google.i18n.phonenumbers.BuildMetadataCppFromXml.Variant; -import com.google.i18n.phonenumbers.CppMetadataGenerator.Type; +import com.google.i18n.phonenumbers.BuildMetadataRustFromXml.Options; +import com.google.i18n.phonenumbers.BuildMetadataRustFromXml.Variant; +import com.google.i18n.phonenumbers.RustMetadataGenerator.Type; import org.junit.Test; @@ -36,7 +37,7 @@ import java.nio.charset.Charset; * Tests the BuildMetadataCppFromXml implementation to make sure it parses command line options and * generates code correctly. */ -public class BuildMetadataCppFromXmlTest { +public class BuildMetadataRustFromXmlTest { // Various repeated test strings and data. private static final String IGNORED = "IGNORED"; @@ -44,7 +45,9 @@ public class BuildMetadataCppFromXmlTest { private static final String INPUT_PATH_XML = "input/path.xml"; private static final byte[] TEST_DATA = new byte[] { (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE }; - private static final String CPP_TEST_DATA = "0xCA, 0xFE, 0xBA, 0xBE"; + private static final int TEST_DATA_LEN = TEST_DATA.length; + private static final String TEST_CONSTANT_NAME = "METADATA"; + private static final String OUTPUT_DATA = "0xCA, 0xFE, 0xBA, 0xBE"; @Test public void parseVariant() { @@ -60,7 +63,7 @@ public class BuildMetadataCppFromXmlTest { @Test public void parseBadOptions() { try { - BuildMetadataCppFromXml.Options.parse("MyCommand", new String[] { IGNORED }); + BuildMetadataRustFromXml.Options.parse("MyCommand", new String[] { IGNORED }); fail("Expected exception not thrown"); } catch (IllegalArgumentException e) { assertTrue(e.getMessage().contains("MyCommand")); @@ -69,93 +72,58 @@ public class BuildMetadataCppFromXmlTest { @Test public void parseGoodOptions() { - Options opt = BuildMetadataCppFromXml.Options.parse("MyCommand", - new String[] { IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "test_alternate_format" }); + Options opt = BuildMetadataRustFromXml.Options.parse("MyCommand", + new String[] { IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "test_alternate_format", "--const-name=" + TEST_CONSTANT_NAME }); assertEquals(Type.ALTERNATE_FORMAT, opt.getType()); assertEquals(Variant.TEST, opt.getVariant()); assertEquals(INPUT_PATH_XML, opt.getInputFilePath()); assertEquals(OUTPUT_DIR, opt.getOutputDir()); + assertEquals(TEST_CONSTANT_NAME, opt.getConstantName()); } @Test public void generateMetadata() { String[] args = new String[] { - IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "metadata" }; + IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "metadata", "--const-name " + TEST_CONSTANT_NAME }; // Most of the useful asserts are done in the mock class. MockedCommand command = new MockedCommand( - INPUT_PATH_XML, false, OUTPUT_DIR, Type.METADATA, Variant.FULL); + INPUT_PATH_XML, false, OUTPUT_DIR, Type.METADATA, Variant.FULL, TEST_CONSTANT_NAME + ); command.setArgs(args); command.start(); // Sanity check the captured data (asserting implicitly that the mocked methods were called). - String headerString = command.capturedHeaderFile(); - assertTrue(headerString.contains("const void* metadata_get()")); - assertTrue(headerString.contains("int metadata_size()")); String sourceString = command.capturedSourceFile(); - assertTrue(sourceString.contains("const void* metadata_get()")); - assertTrue(sourceString.contains("int metadata_size()")); - assertTrue(sourceString.contains(CPP_TEST_DATA)); + assertTrue(sourceString.contains("pub const "+TEST_CONSTANT_NAME+": [u8; " + TEST_DATA_LEN + "] =")); + assertTrue(sourceString.contains(OUTPUT_DATA)); + assertTrue(sourceString.contains("];")); } - @Test - public void generateLiteMetadata() { - String[] args = new String[] { - IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "lite_metadata" }; - // Most of the useful asserts are done in the mock class. - MockedCommand command = new MockedCommand( - INPUT_PATH_XML, true, OUTPUT_DIR, Type.METADATA, Variant.LITE); - command.setArgs(args); - command.start(); - // Sanity check the captured data (asserting implicitly that the mocked methods were called). - String headerString = command.capturedHeaderFile(); - assertTrue(headerString.contains("const void* metadata_get()")); - assertTrue(headerString.contains("int metadata_size()")); - String sourceString = command.capturedSourceFile(); - assertTrue(sourceString.contains("const void* metadata_get()")); - assertTrue(sourceString.contains("int metadata_size()")); - assertTrue(sourceString.contains(CPP_TEST_DATA)); - } - - @Test - public void generateAlternateFormat() { - String[] args = new String[] { - IGNORED, INPUT_PATH_XML, OUTPUT_DIR, "alternate_format" }; - // Most of the useful asserts are done in the mock class. - MockedCommand command = new MockedCommand( - INPUT_PATH_XML, false, OUTPUT_DIR, Type.ALTERNATE_FORMAT, Variant.FULL); - command.setArgs(args); - command.start(); - // Sanity check the captured data (asserting implicitly that the mocked methods were called). - String headerString = command.capturedHeaderFile(); - assertTrue(headerString.contains("const void* alternate_format_get()")); - assertTrue(headerString.contains("int alternate_format_size()")); - String sourceString = command.capturedSourceFile(); - assertTrue(sourceString.contains("const void* alternate_format_get()")); - assertTrue(sourceString.contains("int alternate_format_size()")); - assertTrue(sourceString.contains(CPP_TEST_DATA)); - } + // no need test for metadata with other names since it's set with parameter /** * Manually mocked subclass of BuildMetadataCppFromXml which overrides all file related behavior * while asserting the validity of any parameters passed to the mocked methods. After starting * this command, the captured header and source file contents can be retrieved for testing. */ - static class MockedCommand extends BuildMetadataCppFromXml { + static class MockedCommand extends BuildMetadataRustFromXml { private static final Charset UTF_8 = Charset.forName("UTF-8"); private final String expectedInputFilePath; private final boolean expectedLiteMetadata; private final String expectedOutputDirPath; private final Type expectedType; private final Variant expectedVariant; - private final ByteArrayOutputStream headerOut = new ByteArrayOutputStream(); + private final String expectedConstantName; private final ByteArrayOutputStream sourceOut = new ByteArrayOutputStream(); public MockedCommand(String expectedInputFilePath, boolean expectedLiteMetadata, - String expectedOutputDirPath, Type expectedType, Variant expectedVariant) { + String expectedOutputDirPath, Type expectedType, Variant expectedVariant, + String expectedConstantName) { this.expectedInputFilePath = expectedInputFilePath; this.expectedLiteMetadata = expectedLiteMetadata; this.expectedOutputDirPath = expectedOutputDirPath; this.expectedType = expectedType; + this.expectedConstantName = expectedConstantName; this.expectedVariant = expectedVariant; } @Override void writePhoneMetadataCollection( @@ -164,20 +132,11 @@ public class BuildMetadataCppFromXmlTest { assertEquals(expectedLiteMetadata, liteMetadata); out.write(TEST_DATA, 0, TEST_DATA.length); } - @Override OutputStream openHeaderStream(File dir, Type type) { + @Override OutputStream openSourceStream(File dir) { assertEquals(expectedOutputDirPath, dir.getPath()); - assertEquals(expectedType, type); - return headerOut; - } - @Override OutputStream openSourceStream(File dir, Type type, Variant variant) { - assertEquals(expectedOutputDirPath, dir.getPath()); - assertEquals(expectedType, type); - assertEquals(expectedVariant, variant); return sourceOut; } - String capturedHeaderFile() { - return new String(headerOut.toByteArray(), UTF_8); - } + String capturedSourceFile() { return new String(sourceOut.toByteArray(), UTF_8); } diff --git a/tools/java/rust-build/test/com/google/i18n/phonenumbers/CppMetadataGeneratorTest.java b/tools/java/rust-build/test/com/google/i18n/phonenumbers/RustMetadataGeneratorTest.java similarity index 58% rename from tools/java/rust-build/test/com/google/i18n/phonenumbers/CppMetadataGeneratorTest.java rename to tools/java/rust-build/test/com/google/i18n/phonenumbers/RustMetadataGeneratorTest.java index d0bf3d3..be41348 100644 --- a/tools/java/rust-build/test/com/google/i18n/phonenumbers/CppMetadataGeneratorTest.java +++ b/tools/java/rust-build/test/com/google/i18n/phonenumbers/RustMetadataGeneratorTest.java @@ -1,6 +1,7 @@ /* * Copyright (C) 2012 The Libphonenumber Authors - * + * Copyright (C) 2025 The Kashin Vladislav (modified) + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -19,7 +20,7 @@ package com.google.i18n.phonenumbers; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import com.google.i18n.phonenumbers.CppMetadataGenerator.Type; +import com.google.i18n.phonenumbers.RustMetadataGenerator.Type; import org.junit.Test; @@ -35,60 +36,42 @@ import java.util.List; /** * Tests that the CppXmlMetadata class emits the expected source and header files for metadata. */ -public class CppMetadataGeneratorTest { +public class RustMetadataGeneratorTest { - @Test - public void emitStaticArrayData() { - // 13 bytes per line, so have 16 bytes to test > 1 line (general case). - // Use all hex digits in both nibbles to test hex formatting. - byte[] data = new byte[] { + // 13 bytes per line, so have 16 bytes to test > 1 line (general case). + // Use all hex digits in both nibbles to test hex formatting. + private static final byte[] TEST_DATA = new byte[] { (byte) 0xF0, (byte) 0xE1, (byte) 0xD2, (byte) 0xC3, (byte) 0xB4, (byte) 0xA5, (byte) 0x96, (byte) 0x87, (byte) 0x78, (byte) 0x69, (byte) 0x5A, (byte) 0x4B, (byte) 0x3C, (byte) 0x2D, (byte) 0x1E, (byte) 0x0F, - }; - - StringWriter writer = new StringWriter(); - CppMetadataGenerator.emitStaticArrayData(new PrintWriter(writer), data); - - } + }; + private static final int TEST_DATA_LEN = TEST_DATA.length; + private static final String TEST_CONSTANT_NAME = "METADATA"; @Test - public void outputHeaderFile() throws IOException { - byte[] data = new byte[] { (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE }; - CppMetadataGenerator metadata = CppMetadataGenerator.create(Type.METADATA, data); + public void emitStaticArrayData() { + + byte[] data = TEST_DATA; StringWriter writer = new StringWriter(); - metadata.outputHeaderFile(writer); - Iterator lines = toLines(writer.toString()).iterator(); - // Sanity check that at least some of the expected lines are present. - assertTrue(consumeUntil(" * Copyright (C) 2011 The Libphonenumber Authors", lines)); - assertTrue(consumeUntil("#ifndef I18N_PHONENUMBERS_METADATA_H_", lines)); - assertTrue(consumeUntil("#define I18N_PHONENUMBERS_METADATA_H_", lines)); - assertTrue(consumeUntil("namespace i18n {", lines)); - assertTrue(consumeUntil("namespace phonenumbers {", lines)); - assertTrue(consumeUntil("int metadata_size();", lines)); - assertTrue(consumeUntil("const void* metadata_get();", lines)); - assertTrue(consumeUntil("#endif // I18N_PHONENUMBERS_METADATA_H_", lines)); + RustMetadataGenerator.emitStaticArrayData(new PrintWriter(writer), data); + } @Test public void outputSourceFile() throws IOException { byte[] data = new byte[] { (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE }; - CppMetadataGenerator metadata = CppMetadataGenerator.create(Type.ALTERNATE_FORMAT, data); + String testDataLen = String.valueOf(data.length); + RustMetadataGenerator metadata = RustMetadataGenerator.create(Type.ALTERNATE_FORMAT, data, TEST_CONSTANT_NAME); StringWriter writer = new StringWriter(); metadata.outputSourceFile(writer); Iterator lines = toLines(writer.toString()).iterator(); // Sanity check that at least some of the expected lines are present. - assertTrue(consumeUntil(" * Copyright (C) 2012 The Libphonenumber Authors", lines)); - assertTrue(consumeUntil("namespace i18n {", lines)); - assertTrue(consumeUntil("namespace phonenumbers {", lines)); - assertTrue(consumeUntil("namespace {", lines)); - assertTrue(consumeUntil("static const unsigned char data[] = {", lines)); + assertTrue(consumeUntil("pub const "+TEST_CONSTANT_NAME+": [u8; "+testDataLen+"] = [", lines)); assertTrue(consumeUntil(" 0xCA, 0xFE, 0xBA, 0xBE", lines)); - assertTrue(consumeUntil("int alternate_format_size() {", lines)); - assertTrue(consumeUntil("const void* alternate_format_get() {", lines)); + assertTrue(consumeUntil("];", lines)); } /** Converts a string containing newlines into a list of lines. */ diff --git a/tools/scripts/generate_metadata.sh b/tools/scripts/generate_metadata.sh new file mode 100644 index 0000000..243bb33 --- /dev/null +++ b/tools/scripts/generate_metadata.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +filedir="./$(dirname "$0")" +javadir="$filedir/../java" +project_home="$filedir/../.." +generated_dir="$project_home/src/phonenumberutil/generated" +echo $generated_dir + +resources_dir="$project_home/resources" +rust_build_jar="$javadir/rust-build/target/rust-build-1.0-SNAPSHOT-jar-with-dependencies.jar" + +# mvn -f "$javadir/pom.xml" install +mkdir -p "$generated_dir" + +function generate { + java -jar "$rust_build_jar" \ + BuildMetadataRustFromXml \ + "$resources_dir/$1" \ + "$generated_dir/$2.rs" \ + "$3" \ + "--const-name=$4" +} + +# generate general metadata +generate "PhoneNumberMetadata.xml" "metadata" "metadata" "METADATA" + +# generate short metadata +generate "PhoneNumberMetadataForTesting.xml" "test_metadata" "metadata" "TEST_METADATA" + +echo "\ +pub mod metadata; +pub mod test_metadata; +" > "$generated_dir/mod.rs" \ No newline at end of file