How can I store a format string template outside of my source code? - rust

When translating, messages can be in different languages and have format parameters. I want to be able to do this where the template can be stored in a file:
static PATTERN: &'static str = r"Hello {inner};";
/// in some implementation
fn any_method(&self) -> String {
format!(PATTERN, inner = "world");
}

That's not possible. Format strings must be actual literal strings.
The next best approach would be some kind of dynamic string format library. Or, failing that, you could always use str::replace if your needs aren't too complex.

This is definitely possible and trivial to do using the include_str macro that has been available in the standard library since version 1.0.0. The following example was tested with rustc 1.58.1:
Contents of src/main.rs:
println!(include_str!("src/hello-world.tmpl"), "world");
Contents of src/hello-world.tmpl
Hello {inner}
This works because include_str injects the contents of the template file as a string literal before println, format, and friends have a chance to evaluate their arguments. This approach only works when the format template you want to include is available during macro expansion - like it is in your example. If it's not, then you should consider other options like the ones suggested by #DK.
As an added bonus: You can also define format strings in source code locations other than the site where they are used by defining them as macros.

Related

fmt : format a float/double without format string

I would like to format a single double/float to a string/wstring with fmt.
something like
std::string s = fmt:format_??(my_float);
Since the formats I use are kind of fixed, is there a way to use the library without having to use the format string, but directly feeding the settings?
I noticed there are format_int and format_float that uses a settings struct,which may be suitable, but these are inside the detail namespace and also it's not so clear how to initialize the input parameters.
My (unbenchmarked) guess is that avoiding the format string would result in a faster conversion.
Any idea or experience in this regard?
Thanks!
is there a way to use the library without having to use the format string, but directly feeding the settings?
No but if the format is known at compile time you can use format string compilation which completely eliminates the runtime overhead of handling a format string, however small. For example:
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
is the fastest method of converting a number into an std::string (there is also fmt::to_string which is equivalent): https://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html.

Can you set multiple (different) tags with the same value?

For some of my projects, I have had to use the viper package to use configuration.
The package requires you to add the mapstructure:"fieldname" to identify and set your configuration object's fields correctly, but I have also had to add other tags for other purposes, leading to something looking like the following :
type MyStruct struct {
MyField string `mapstructure:"myField" json:"myField" yaml:"myField"`
}
As you can see, it is quite redundant for me to write tag:"myField" for each of my tag, so I was wondering if there was any way to "bundle" them up and reduce the verbosity, with something like this mapstructure,json,yaml:"myField"
Or is it simply not possible and you must specify every tag separately ?
Struct tags are arbitrary string literals. Data stored in struct tags may look like whatever you want them to be, but if you don't follow the conventions, you'll have to write your own parser / processing logic. If you follow the conventions, you may use StructTag.Get() and StructTag.Lookup() to easily get tag values.
The conventions do not support "merging" multiple tags, so just write them all out.
The conventions, quoted from reflect.StructTag:
By convention, tag strings are a concatenation of optionally space-separated key:"value" pairs. Each key is a non-empty string consisting of non-control characters other than space (U+0020 ' '), quote (U+0022 '"'), and colon (U+003A ':'). Each value is quoted using U+0022 '"' characters and Go string literal syntax.
See related question: What are the use(s) for tags in Go?

Converting an ASTNode into code

How does one convert an ASTNode (or at least a CompilationUnit) into a valid piece of source code?
The documentation says that one shouldn't use toString, but doesn't mention any alternatives:
Returns a string representation of this node suitable for debugging purposes only.
CompilationUnits have rewrite, but that one does not work for ASTs created by hand.
Formatting options would be nice to have, but I'd basically be satisfied with anything that turns arbitrary ASTNodes into semantically equivalent source code.
In JDT the normal way for AST manipulation is to start with a basic CompilationUnit and then use a rewriter to add content. Then ASTRewriteAnalyzer / ASTRewriteFormatter should take care of creating formatted source code. Creating a CU just containing a stub type declaration shouldn't be hard, so that's one option.
If that doesn't suite your needs, you may want to experiement with directly calling the internal org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteFlattener.asString(ASTNode, RewriteEventStore). If not editing existing files, you may probably ignore the events collected in the RewriteEventStore, just use the returned String.

How do I transform existing log statements into interpolated string format?

I want to change existing statements such as
log.debug("Value of foo : {}", foo)
to use interpolated string format instead
log.debug(s"Value of foo : $foo")
Although IntelliJ has a blog post on converting between string formats, it seems to require having to first convert the existing string to
"Value of foo ".format(foo)
which defeats the purpose of wanting an automatic format change option in the first place (and doesn't work properly for me anyway).
How can I easily format these log statements to use string interpolation? Is there any way to do so wholesale (across an entire project)?
In IntelliJ, you can do Inspect Code (right click -> Analyze -> Inspect Code). Make sure to turn on Legacy String Formatting rule in your Inspection Profile. I think this rule is part of the Scala plugin.
If you turn on this rule, you can make it a warning, error, or some other options. You can always hit alt + enter while your cursor is on a statement with string concatenation and then select convert to interpolated string from the dropdown.

Get argument names in String Interpolation in Scala 2.10

As of scala 2.10, the following interpolation is possible.
val name = "someName"
val interpolated = s"Hello world, my name is $name"
Now it is also possible defining custom string interpolations, as you can see in the scala documentation in the "Advanced usage" section here http://docs.scala-lang.org/overviews/core/string-interpolation.html#advanced_usage
Now then, my question is... is there a way to obtain the original string, before interpolation, including any interpolated variable names, from inside the implicit class that is defining the new interpolation for strings?
In other words, i want to be able to define an interpolation x, in such a way that when i call
x"My interpolated string has a $name"
i can obtain the string exactly as seen above, without replacing the $name part, inside the interpolation.
Edit: on a quick note, the reason i want to do this is because i want to obtain the original string and replace it with another string, an internationalized string, and then replace the variable values. This is the main reason i want to get the original string with no interpolation performed on it.
Thanks in advance.
Since Scala's string interpolation can handle arbitrary expressions within ${} it has to evaluate the arguments before passing them to the formatting function. Thus, direct access to the variable names is not possible by design. As pointed out by Eugene, it is possible to get the name of a plain variable by using macros. I don't think this is a very scalable solution, though. After all, you'll lose the possibility to evaluate arbitrary expressions. What, for instance, will happen in this case:
x"My interpolated string has a ${"Mr. " + name}"
You might be able to extract the variable name by using macros but it might get complicated for arbitrary expressions. My suggestions would be: If the name of your variable should be meaningful within the string interpolation, make it a part of the data structure. For example, you can do the following:
case class NamedValue(variableName: String, value: Any)
val name = NamedValue("name", "Some Name")
x"My interpolated string has a $name"
The objects are passed as Any* to the x. Thus, you now can match for NamedValue within x and you can do specific things depending on the "variable name", which now is part of your data structure. Instead of storing the variable name explicitly you could also exploit a type hierarchy, for instance:
sealed trait InterpolationType
case class InterpolationTypeName(name: String) extends InterpolationType
case class InterpolationTypeDate(date: String) extends InterpolationType
val name = InterpolationTypeName("Someone")
val date = InterpolationTypeDate("2013-02-13")
x"$name is born on $date"
Again, within x you can match for the InterpolationType subtype and handle things according to the type.
It seems that's not possible. String interpolation seems like a compile feature that compiles the example to:
StringContext("My interpolated string has a ").x(name)
As you can see the $name part is already gone. It became really clear for me when I looked at the source code of StringContext: https://github.com/scala/scala/blob/v2.10.0/src/library/scala/StringContext.scala#L1
If you define x as a macro, then you will be able to see the tree of the desugaring produced by the compiler (as shown by #EECOLOR). In that tree, the "name" argument will be seen as Ident(newTermName("name")), so you'll be able to extract a name from there. Be sure to take a look at macro and reflection guides at docs.scala-lang.org to learn how to write macros and work with trees.

Resources