Skip to main content

qubit_argument/argument/
argument_path.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Structured paths that identify arguments in validation errors.
9
10use std::fmt::{
11    self,
12    Display,
13    Formatter,
14};
15
16/// An owned path identifying an argument or one of its nested components.
17///
18/// The path is stored exactly as supplied; this type does not parse or
19/// normalize separators.
20///
21/// ```compile_fail
22/// #![deny(unused_must_use)]
23/// use qubit_argument::ArgumentPath;
24///
25/// ArgumentPath::new("value");
26/// ```
27#[must_use]
28#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29pub struct ArgumentPath(String);
30
31impl ArgumentPath {
32    /// Creates an argument path by copying `path` without normalization.
33    ///
34    /// # Parameters
35    ///
36    /// - `path`: The path text to store.
37    ///
38    /// # Returns
39    ///
40    /// A new owned argument path.
41    #[inline]
42    pub fn new(path: &str) -> Self {
43        Self(path.to_owned())
44    }
45
46    /// Returns the stored path text.
47    ///
48    /// The returned string slice remains valid for the lifetime of this path.
49    #[inline(always)]
50    pub fn as_str(&self) -> &str {
51        self.0.as_str()
52    }
53
54    /// Prepends a parent path component to this path.
55    ///
56    /// # Parameters
57    ///
58    /// - `prefix`: The parent path to place before the stored path.
59    ///
60    /// # Returns
61    ///
62    /// The composed path. Non-empty components are separated by one dot. If
63    /// either component is empty, no separator is added.
64    #[inline]
65    pub fn with_prefix(self, prefix: &str) -> Self {
66        if prefix.is_empty() {
67            return self;
68        }
69        if self.0.is_empty() {
70            return Self(prefix.to_owned());
71        }
72        let mut path = String::with_capacity(prefix.len() + 1 + self.0.len());
73        path.push_str(prefix);
74        path.push('.');
75        path.push_str(&self.0);
76        Self(path)
77    }
78}
79
80impl AsRef<str> for ArgumentPath {
81    /// Borrows the stored path as a string slice.
82    #[inline(always)]
83    fn as_ref(&self) -> &str {
84        self.as_str()
85    }
86}
87
88impl Display for ArgumentPath {
89    /// Writes the stored path text without additional decoration.
90    #[inline(always)]
91    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
92        formatter.write_str(self.as_str())
93    }
94}