<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>emre şahin's digital garden 🍃 - JSON</title>
    <link>https://emresahin.net/tags/json/</link>
    <description>Posts in the JSON tag</description>
    <language>en</language>
    <managingEditor>contact@emresahin.net (Emre Şahin)</managingEditor>
    <lastBuildDate>Tue, 15 Sep 2026 19:46:32 +0000</lastBuildDate>
    <atom:link href="https://emresahin.net/tags/json/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>devlog 23</title>
      <published>2025-02-03T10:30:44+00:00</published>
      <updated>2025-02-03T10:30:44+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Mon, 03 Feb 2025 10:30:44 +0000</pubDate>
      <link>https://emresahin.net/devlog-23/</link>
      <guid isPermaLink="true">https://emresahin.net/devlog-23/</guid>
      <description>🐇 Let’s turn to discussing the JSON output changes. We can add another option to XvcOutputLine , like XvcOutputLine::Json(T: Serialize) , that will output the type using serde_json . It won’t introduce any other type. 🐢 Yes, but what will T be? Although store structures have Serde implementations...</description>
      <category>XVC</category>
      <category>Development</category>
      <category>XVC</category>
      <category>Rust</category>
      <category>Serde</category>
      <category>JSON</category>
      <category>Serialization</category>
      <category>xvc file list</category>
      <content:encoded><![CDATA[<p>🐇 Let’s turn to discussing the JSON output changes. We can add another option
to <code>XvcOutputLine</code>, like <code>XvcOutputLine::Json(T: Serialize)</code>, that will output
the type using <code>serde_json</code>. It won’t introduce any other type.</p>
<p>🐢 Yes, but what will <code>T</code> be? Although store structures have Serde
implementations, they are not particularly useful for this.</p>
<p>🐇 We can have output types, named like <code>XvcFileListOutput</code>, that will be
converted to strings with serialization.</p>
<p>🦊 We do something similar in <code>xvc pipeline export</code> and <code>import</code> commands. We
use
<a href="https://github.com/iesahin/xvc/blob/main/pipeline/src/pipeline/schema.rs#L41"><code>XvcPipelineSchema</code></a>
and <code>XvcStepSchema</code> just for the import and export commands. We’ll write similar
structs for all JSON output and will use Serde to convert these to strings.</p>
<p>🐢 Unlike the <code>import</code> and <code>export</code> commands, we have optional fields in the
output, though. I don’t want content digests to appear in JSON output if they
are not required.</p>
<p>🐇 Let’s search for optional fields in Serde.</p>
<p>🦊 There is a <a href="https://docs.rs/optional-field/latest/optional_field/attr.serde_optional_fields.html">crate for optional
fields</a>.</p>
<p>🐢 We don’t need another crate for this. Serde has the
<a href="https://serde.rs/attr-skip-serializing.html"><code>skip_serializing_if</code></a> attribute
for fields. We can add <code>Option::is_none</code> as a method to these to skip outputting <code>None</code>
fields. All those fields, in this case, will be optional.</p>
<p>🐇 This is fine. We already use structs to format the <code>xvc file list</code> output. We
can just use them to output JSON.</p>]]></content:encoded>
    </item>
    <item>
      <title>devlog 21</title>
      <published>2025-02-03T09:59:28+00:00</published>
      <updated>2025-02-03T09:59:28+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Mon, 03 Feb 2025 09:59:28 +0000</pubDate>
      <link>https://emresahin.net/devlog-21/</link>
      <guid isPermaLink="true">https://emresahin.net/devlog-21/</guid>
      <description>🐢 Now, the next version will have a --json output for xvc file list . We can start working on it or update the Readme file? 🐇 What about adding at least command completions for Nushell? 🐢 Let’s read a bit about clap_complete_nushell . 🦊 There seems to be a nu-complete command. Let’s check its doc...</description>
      <category>xvc</category>
      <category>Development</category>
      <category>Nushell</category>
      <category>clap_complete_nushell</category>
      <category>carapace</category>
      <category>dynamic completions</category>
      <category>JSON</category>
      <category>Lazygit</category>
      <category>Rust</category>
      <category>XVC</category>
      <category>completions</category>
      <content:encoded><![CDATA[<p>🐢 Now, the next version will have a <code>--json</code> output for <code>xvc file list</code>. We
can start working on it or update the Readme file?</p>
<p>🐇 What about adding at least command completions for Nushell?</p>
<p>🐢 Let’s read a bit about <code>clap_complete_nushell</code>.</p>
<p>🦊 There seems to be a <code>nu-complete</code> command. Let’s check its documentation.</p>
<p>🐇 Nothing was found, and Kagi doesn’t help much either.</p>
<p>🐢 There is a completions document for Nushell: https://www.nushell.sh/book/custom_completions.html</p>
<p>🐇 There is a tool called carapace to provide completions across shells.</p>
<p>🐢 Its <a href="https://carapace-sh.github.io/carapace/carapace.html">documentation</a> is
thin, and I’m not sure if it supports dynamic completions out of the box. I
believe instead of adding a carapace setup, we can just write a Nushell
completion script that will use JSON output from the commands and add some
(maybe hidden) utility commands to support it.</p>
<p>🐇 There are a set of example scripts in the Nushell repo:
https://github.com/nushell/nu_scripts/tree/main/custom-completions</p>
<p>🐢 The reason I want to write custom completions for Nushell is that it will be
an exercise for the scripting language. <a href="https://github.com/nushell/nu_scripts/blob/main/custom-completions/gh/gh-completions.nu"><code>gh</code>
completions</a>
are not as scary as a Bash script.</p>
<p>🐇 <a href="https://github.com/nushell/nu_scripts/blob/main/custom-completions/git/git-completions.nu"><code>git</code>
completions</a>
are a better example for XVC. They simply run <code>git</code> whenever necessary. We can
start from a static completions command and update this with dynamic
completions manually. It will teach a lot.</p>
<p>🐢 I <a href="https://github.com/iesahin/nu_scripts">forked</a> the <code>nu_scripts</code> repo and
will add XVC completions script there.</p>
<p>🐇 Then let’s begin by adding Nushell static completions. Shall we add a
command for this?</p>
<p>🦊 Reviving the <code>completion</code> command we removed in 0.6.13?</p>
<p>🐢 We shouldn’t list it. We can make a <code>_comp</code> subcommand for the time being
and generate and distribute completions in the repository. When
<code>clap_complete_nushell</code> has the feature parity to provide dynamic completions,
we can remove these commands.</p>
<p>🐇 What will we use this for other than generating completions?</p>
<p>🐢 Maybe dynamic completions can call this as well.</p>
<p>🐇 Added Nushell static completions to be output using <code>xvc _comp generate-nushell</code>. Let’s bump up the version to 0.6.15.</p>
<pre><code>cargo set-version 0.6.15-alpha.1
   Upgrading xvc from 0.6.14 to 0.6.15-alpha.1
...
</code></pre>
<p>🐢 I noticed we forgot a line in the CLI command handler that asserts <code>xvc_root_opt.is_some()</code>, and this fails when we run <code>xvc</code> outside of repositories. We need to release this version quickly.</p>
<p>🐇 Oops, now, ok, let’s write a static Nushell generator and just release quickly.</p>
<p>🦊 Generating completions with</p>
<pre><code>xvc comp generate-nushell
</code></pre>
<p>🐢 Completion command is run with <code>comp</code> instead of <code>_comp</code>. Should we rename it?</p>
<p>🐇 Renamed it to <code>_comp</code>. It’s not hidden, but at least we can be sure that it won’t be misunderstood as a common command.</p>
<p>🐢 Bumping the version again. Now let’s source the generated script and test it.</p>
<pre><code>cargo set-version 0.6.15-alpha.2
   Upgrading xvc from 0.6.15-alpha.1 to 0.6.15-alpha.2
...
</code></pre>
<p>🦊 Yep, it works. We now have completions for Nushell.</p>
<p>🐢 Let’s update the completions documentation.</p>
<p>🐇 Done. Now, let’s take a look at CI and see what fails.</p>
<pre><code class="language-nu">ghrl | first
╭──────────────┬─────────────────────────────────────────────────────────╮
│ conclusion   │ success                                                 │
│ displayTitle │ Add Nushell completions                                 │
│ headBranch   │ nushell-completions                                     │
│ url          │ https://github.com/iesahin/xvc/actions/runs/13070200765 │
╰──────────────┴─────────────────────────────────────────────────────────╯
</code></pre>
<p>🐢 It fails because of coverage, not the tests. <a href="https://github.com/iesahin/xvc/pull/266#issuecomment-2626794240">Codecov says</a> the new code isn’t tested.</p>
<p>🐇 The added <code>xvc _comp</code> command isn’t tested. We can add a test running those lines and testing if the command outputs a completion script.</p>
<p>🐢 We have a <a href="https://github.com/iesahin/xvc/blob/main/lib/tests/test_completions.rs#L18">test for completions</a>. We can add a test that runs the lines.</p>
<p>🐇 Added a test and bumping up the version.</p>
<pre><code class="language-nu">cargo set-version 0.6.15-alpha.3
   Upgrading xvc from 0.6.15-alpha.2 to 0.6.15-alpha.3
...
</code></pre>
<p>🦊 We can add some more coverage while waiting for the tests.</p>
<p>🐇 <a href="https://app.codecov.io/gh/iesahin/xvc/blob/main/logging%2Fsrc%2Flib.rs#L285"><code>XvcOutputLine</code> implementation</a> seems to have no tests. It’s weird because we use these everywhere.</p>
<p>🐢 I’m not sure we use this particular implementation; we just use <code>XvcOutputLine::Info(s)</code>, not <code>XvcOutputLine::info(s)</code> anywhere. We can delete these methods actually.</p>
<p>🐇 We’ll add JSON output via this particular struct. Can we refactor these to use formatting for JSON, for example? Or use these to output JSON?</p>
<p>🦊 We can add a formatter to <code>XvcOutputLine</code> to output structures.</p>
<p>🐢 The enum is now defined as:</p>
<pre><code class="language-rust">#[derive(Clone, Debug)]
pub enum XvcOutputLine {
    /// The output that we should be reporting to user
    Output(String),
    /// For informational messages
    Info(String),
    /// For debug output to show the internals of Xvc
    Debug(String),
    /// Warnings that are against some usual workflows
    Warn(String),
    /// Errors that interrupts a workflow but may be recoverable
    Error(String),
    /// Panics that interrupts the workflow and ends the program
    /// Note that this doesn't call panic! automatically
    Panic(String),
    /// Progress bar ticks.
    /// Self::Info is also used for Tick(1)
    Tick(usize),
}</code></pre>
<p>Here, these fields can also have a <code>formatter</code> that will render the string in a particular format. For example, the output can be</p>
<pre><code class="language-rust">XvcOutputLine::Output(XvcJsonFormatter, String)</code></pre>
<p>🐇 I’m not sure this is a good idea. <code>Output</code> already specifies this string as output. We can have a wrapper instead, like,</p>
<pre><code class="language-rust">struct XvcJsonOutput(Format&lt;XvcStructuredOutput&gt;, XvcStructuredOutput)</code></pre>
<p>and we can use the supplied format to render <code>XvcStructuredOutput</code> to an output line with <code>XvcOutputLine::Output</code>. If we don’t provide output as structured, it will be too much error-prone work to convert the current outputs to structured.</p>
<p>🦊 The transition will also be gradual. We may not need structured output for most of the commands. We can start with <code>xvc file list</code> and convert others as we go.</p>
<p>🐢 This is sensible. By the way, coverage still didn’t increase. There may be something going on with Codecov or running the test.</p>
<pre><code class="language-nu">ghrl | first
╭──────────────┬─────────────────────────────────────────────────────────╮
│ conclusion   │ success                                                 │
│ displayTitle │ Add Nushell completions                                 │
│ headBranch   │ nushell-completions                                     │
│ url          │ https://github.com/iesahin/xvc/actions/runs/13087431038 │
╰──────────────┴─────────────────────────────────────────────────────────╯
</code></pre>
<p>🐇 Let’s run the test:</p>
<pre><code class="language-sh">cargo test -p xvc --test test_completions
...
test test_completions ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.80s
</code></pre>
<p>🐢 Can we make sure the output is a Nushell script and not an error message?</p>
<p>🐇 Let’s print it out.</p>
<p>🐢 It looks like when the <code>COMPLETE</code> environment variable is set, it never calls <code>_comp</code> subcommand and never calls those lines.</p>
<pre><code>cargo set-version 0.6.15-alpha.4
   Upgrading xvc from 0.6.15-alpha.3 to 0.6.15-alpha.4
...
</code></pre>
<p>🐢 Let’s make a release for 0.6.15. Coverage is OK now.</p>
<pre><code class="language-nu">cargo set-version 0.6.15
   Upgrading xvc from 0.6.15-alpha.4 to 0.6.15
...
</code></pre>
<pre><code class="language-nu">gh pr merge --squash --body $"(open CHANGELOG.md | lines | skip 2 | take 5)" --subject "Add static nushell completions"
</code></pre>
<p>🐇 Merged the PR.</p>
<p>🐢 Releases should appear in a few minutes.</p>
<p>🐇 We need to tag the merge commit for this.</p>
<p>🐢 Oh, yep. AFAIK Lazygit doesn’t have something for <code>git push --tags</code>. Let’s push from the CLI.</p>
<pre><code class="language-nu">git push --tags
You are on the main branch. Skipping CHANGELOG.md check.
To github.com:iesahin/xvc
 * [new tag]         v0.6.15 -&gt; v0.6.15
</code></pre>
<p>🦊 These commands, especially tables, are not rendered correctly on the web. We need to change the theme, I think.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
