Library / SDK
google/mug avatar
google/mug

Mug is a small Java library that treats string parsing as a typed operation

A small Java 8 library (string manipulation, BiStream, Structured Concurrency, SQL Templates)

536 stars90 forksJavaApache-2.0

At a glance

What is it?
Mug collects a set of Google-internal Java utilities into a public artifact: composable substring extraction, a compile-time-safe format parser, a parser combinator, streams over maps, SQL templates that are checked by the compiler, and structured concurrency on virtual threads. It has no dependencies, it is Apache-2.0 licensed, and it is not an official Google product.
Who is it for?
Adopt Mug if you are on Java 8 or newer and want typed string parsing, a small set of stream utilities, or SQL templates whose correctness the compiler checks, without pulling in a dependency. Do not adopt it if you need the Proto, BigQuery or Guava add-ons without their separate artifacts, since the core library deliberately has zero dependencies.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 16 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A grab bag with an unusually disciplined dependency policy

Mug is described as a small Java 8+ string processing and streams library, and it is presented with a caveat at the top of the readme: this is not an official Google product.

The origin story is in the positioning line. It is battle tested in Google's internal codebase, with zero dependencies, and the Proto, BigQuery and Guava add-ons are in separate artifacts. That is a slightly unusual packaging decision to see in a company library: the core artifact is dependency-free, and the parts that would drag in a large framework live next door under their own coordinates.

The repository layout explains the same thing in directory form. There is mug for the core, and then mug-safesql, dot-parse, mug-guava, mug-protobuf, mug-bigquery, mug-spanner, mug-bom, mug-benchmarks, mug-examples, mug-errorprone, mug-concurrent-testing, mug-concurrent24, mug-safesql-java16tests, dot-cel, plus a WORKSPACE, a pom.xml and a play.sh script.

So there is a project here for each combination, and the question for an adopter is which one you need. The main entry is com.google.mug:mug at version 11.2, with mug-safesql, dot-parse, mug-guava and mug-protobuf as the others documented alongside it. The releases are mug-root-11.2 on 2026-10-04, mug-root-11.1 on 2026-09-20 and mug-root-11.0 on 2026-09-04, so this is a project shipping regular minor versions.

Substring extraction that composes instead of indexing

The first headline feature is Substring, described as simple and composable substring extraction and manipulation.

The readme shows it as a single call, Substring.between("(", ")").from("call(foo)") returning "foo".

So instead of calling indexOf, then substring, then checking whether the index was negative, you build a description of the shape you want and apply it to a string. The between call names the delimiters, and the from call applies the result.

That is a different style from the index arithmetic that string handling usually degenerates into. The indexOf and substring version has three failure modes: the delimiter is not found, the delimiter is found at the wrong place, and the string is shorter than the offsets. The composed version does not have those branches to get wrong, and the check happens in one place.

The documentation points to a wiki page for Substring Explained, which is a hint that there is more to the API than the single example, and the library has a javadoc site. The topics on the repository list java-strings and java-streams, and the class name puts it in the string handling category rather than the parsing category, which is a distinction the library is careful about elsewhere.

A parser combinator, and a format parser that is checked at compile time

Two of the parsing tools are worth separating, because they solve different problems.

Parser is described as making everyday string parsing easier, faster and beyond regex. The example is a builder chain:

java
    sequence(word().followedBy("="), digits(), Map::entry)
        .zeroOrMoreDelimitedBy(",")
        .between("{", "}")         // {k1=100, k2=200, k3=300, ...}
        .parseSkipping(Character::isWhitespace, input);

The pieces read as a grammar. A sequence of a word followed by an equals sign, then digits, producing a map entry, repeated zero or more times with commas between them, wrapped in braces, parsed while skipping whitespace. Each step is a named operation, and the type system carries the shape of what has been recognised so far.

StringFormat is the other one, and its claim is stronger: compile-time-safe bidirectional parsing and formatting. The readme shows a format string with named placeholders used for parsing a path, new StringFormat("/home/{user}/{date}").parse(filePath, (user, date) -> ...).

Bidirectional means the same definition drives both directions, and compile-time-safe means a mismatch between the placeholders and the arguments is a compile error rather than a runtime surprise. The guardrail is backed by an annotation, @ParametersMustMatchByName, and the readme shows both halves of the behaviour with a record Profile holding a userId and a userName: passing the id and the name in the right order compiles, and swapping them does not compile.

There is also DateTimeFormats, which defines datetime formats by examples rather than by pattern strings, with the example being a format of 2024-03-14 11.00.00.123 America/New_York. Defining a format by showing it is a real advantage, because there is no pattern syntax to get wrong.

SQL templates, and where the safety comes from

SafeSql is the feature with the boldest claim in the readme: a compile-time-enforced, safe, composable SQL template. The readme shows a single call, SafeSql.of("select id, `{col}` from Users where id = {id}", col, id).

The safety here is about the shape of the query rather than the values. A template that takes a column name and an id as arguments has the substituted identifiers visible at the call site, and the library is built so that a query constructed this way is one the compiler can reason about. That is the distinction from string concatenation, where a value pasted into a statement is invisible to any tool.

The catch is that this is one of the separate artifacts. SafeSql lives in mug-safesql, with its own javadoc and its own artifact, and the readme links a separate README in that module. There is also a mug-safesql-java16tests module, which suggests the library is tested against a different Java baseline than the rest.

The dependency list for it in the readme is separate too, so adopting the SQL piece means adding an artifact rather than swapping a package.

The other modules follow the same pattern. BinarySearch lives in mug-guava and is described as solving LeetCode binary search problems, with the example being a search in a sorted array with a tolerance. Iteration is for implementing a lazy stream with recursive code. Optionals is a small helper for the map and get pattern, with the example being a return of optionally(obj.hasFoo(), obj::getFoo).

Streams over maps, and structured concurrency on virtual threads

The stream utilities are two pieces.

BiStream streams a Map and pair-wise collections, with the example being BiStream.zip(keys, values).toMap(). The point of a BiStream is that the usual workaround for zipping two collections is to convert one to a list, index into it and hope the sizes match, and the type makes the pairing explicit.

MoreStreams is the grab bag of extra stream utilities, with the example grouping consecutive elements of a price series:

java
    Stream<List<Double>> greenDays = MoreStreams.groupConsecutive(
        stockPrices, (p1, p2) -> p1 <= p2, toUnmodifiableList());

The structured concurrency helper is the one with a version requirement. It is described as simple structured concurrency on virtual threads, with the example being concurrently(() -> fetchArm(), () -> fetchLeg(), (arm, leg) -> makeRobot(arm, leg)).

That signature is worth a moment. The concurrency helper takes the two tasks and a combining function, and returns both results once both are done. The alternative is usually two futures and a join, with the error handling left to the caller. The structured version gives the scope a beginning and an end, which is the point of the pattern.

The virtual threads requirement is a real constraint. There is a mug-concurrent24 module and a mug-concurrent-testing module in the repository, so the concurrent parts are separated out, and the structured concurrency piece is described as running on virtual threads, which means the class files of that helper will not run on a Java 8 runtime even though the library as a whole claims Java 8 plus.

Installing it, and the processor that makes the guardrails work

The Gradle route is five lines:

gradle
  implementation 'com.google.mug:mug:11.2'
  implementation 'com.google.mug:mug-safesql:11.2'
  implementation 'com.google.mug:dot-parse:11.2'
  implementation 'com.google.mug:mug-guava:11.2'
  implementation 'com.google.mug:mug-protobuf:11.2'

The Maven route adds a dependency block for the mug artifact itself, and separate blocks for mug-safesql, dot-parse and mug-protobuf, all at version 11.2.

The part that is not a dependency is the annotation processor, and the readme is explicit that mug-errorprone has to be added to the annotationProcessorPaths of the maven-compiler-plugin, alongside error_prone_core at version 2.23.0.

That is the mechanism behind the compile-time guardrails. The @ParametersMustMatchByName check and the SafeSql checking are Error Prone checks, so they only run if you have the processor on the processor path. If you skip that step, the library still compiles and still works at runtime, but the errors it is designed to catch at compile time become runtime surprises instead. That is the single most important installation detail in the readme, and it is easy to miss because it is a build configuration change rather than a dependency line.

The documentation also has a javadoc site, a wiki with a page per feature, and a CONTRIBUTING guide.

Editorial conclusion

Adopt Mug if you are on Java 8 or newer and want typed string parsing, a small set of stream utilities, or SQL templates whose correctness the compiler checks, without pulling in a dependency. Do not adopt it if you need the Proto, BigQuery or Guava add-ons without their separate artifacts, since the core library deliberately has zero dependencies. Before you add it, check three things: which artifact you actually need, since the base, safesql, dot-parse, guava and protobuf pieces are separate coordinates in the same group, whether you are willing to run the Error Prone annotation processor, since the compile-time guardrails depend on it, and which Java version you target, since the structured concurrency helper is described as running on virtual threads.

Frequently asked questions

What is Google Mug?

A small Java 8+ string processing and streams library with zero dependencies, described as battle tested in Google's internal codebase. The readme states it is not an official Google product, and the Proto, BigQuery and Guava add-ons are in separate artifacts.

How do I add Mug to a Maven or Gradle project?

In Maven, add a dependency on com.google.mug:mug at version 11.2, with separate blocks for mug-safesql, dot-parse and mug-protobuf. In Gradle, add the matching implementation lines. There are also separate artifacts for the guava, bigquery and spanner add-ons.

What does SafeSql do in Mug?

It is a compile-time-enforced, safe, composable SQL template. A template such as SafeSql.of("select id, `{col}` from Users where id = {id}", col, id) takes the identifiers as arguments so the substituted names are visible at the call site, rather than being pasted into a concatenated string.

Why does Mug need an annotation processor?

The compile-time guardrails are Error Prone checks, so mug-errorprone has to be added to the annotationProcessorPaths of the maven-compiler-plugin alongside error_prone_core. Without the processor the library still works, but the errors it is designed to catch at compile time move to runtime.

Does Mug work on Java 8?

The library is described as Java 8 plus, and the core artifact is dependency free. The structured concurrency helper is the exception, since it is described as running on virtual threads, and the repository separates the concurrent code into mug-concurrent24 and mug-concurrent-testing modules.

Official sources

  1. google/mug on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/google-mug.svg)](https://hysenlabs.com/projects/google-mug)

Was this useful?