fn from_str()

in pkcs11/pkcs11/src/lib.rs [105:208]


    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // Ref https://tools.ietf.org/html/rfc7512#section-2.3
        //
        // Only object-label, slot-id, token-label and pin-value are parsed from the URL. If both slot-id and token are provided, token is ignored.

        enum PathComponentKey {
            Object,
            SlotId,
            Token,
        }

        enum QueryComponentKey {
            PinValue,
        }

        fn parse_key_value_pair<'a, F, T>(
            s: &'a str,
            mut key_discriminant: F,
        ) -> Result<Option<(T, std::borrow::Cow<'a, str>)>, ParsePkcs11UriError>
        where
            F: FnMut(&[u8]) -> Option<T>,
        {
            let (key, value) = s.split_once('=').unwrap_or((s, ""));

            let key = percent_encoding::percent_decode(key.as_bytes());
            let key: std::borrow::Cow<'a, _> = key.into();
            let Some(typed_key) = key_discriminant(&key) else {
                return Ok(None);
            };

            let value = percent_encoding::percent_decode(value.as_bytes());
            match value.decode_utf8() {
                Ok(value) => Ok(Some((typed_key, value))),
                Err(err) => Err(ParsePkcs11UriError::InvalidUtf8(
                    key.into_owned(),
                    err.into(),
                )),
            }
        }

        let mut object_label = None;
        let mut token_label = None;
        let mut slot_id = None;
        let mut pin = None;

        let s = s
            .strip_prefix("pkcs11:")
            .ok_or(ParsePkcs11UriError::InvalidScheme)?;

        let (path, query) = s.split_once('?').unwrap_or((s, ""));

        let path_components = path.split(';');
        for path_component in path_components {
            let key_value_pair = parse_key_value_pair(path_component, |key| match key {
                b"object" => Some(PathComponentKey::Object),
                b"slot-id" => Some(PathComponentKey::SlotId),
                b"token" => Some(PathComponentKey::Token),
                _ => None,
            })?;
            if let Some((key, value)) = key_value_pair {
                match key {
                    PathComponentKey::Object => {
                        object_label = Some(value.into_owned());
                    }
                    PathComponentKey::SlotId => {
                        let value = value.parse::<pkcs11_sys::CK_SLOT_ID>().map_err(|err| {
                            ParsePkcs11UriError::MalformedSlotId(value.into_owned(), err)
                        })?;
                        slot_id = Some(value);
                    }
                    PathComponentKey::Token => {
                        token_label = Some(value.into_owned());
                    }
                }
            }
        }

        let query_components = query.split('&');
        for query_component in query_components {
            let key_value_pair = parse_key_value_pair(query_component, |key| match key {
                b"pin-value" => Some(QueryComponentKey::PinValue),
                _ => None,
            })?;
            if let Some((key, value)) = key_value_pair {
                match key {
                    QueryComponentKey::PinValue => {
                        pin = Some(value.into_owned());
                    }
                }
            }
        }

        let slot_identifier = match (token_label, slot_id) {
            (_, Some(slot_id)) => UriSlotIdentifier::SlotId(slot_id),
            (Some(token_label), _) => UriSlotIdentifier::Label(token_label),
            (None, None) => return Err(ParsePkcs11UriError::NeitherSlotIdNorTokenSpecified),
        };

        Ok(Uri {
            slot_identifier,
            object_label,
            pin,
        })
    }